aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2019-01-09 18:30:17 +0100
committerEven Rouault <even.rouault@spatialys.com>2019-01-09 19:45:21 +0100
commit3cc9336b038118de424d018dd0bdd90e329ad8c1 (patch)
tree790ceaa32d7217f7667264217ecdec6db5c5d3b9
parentd6dad2cee4307f6b190a96dd48942645060919cc (diff)
downloadPROJ-3cc9336b038118de424d018dd0bdd90e329ad8c1.tar.gz
PROJ-3cc9336b038118de424d018dd0bdd90e329ad8c1.zip
proj.h: add proj_context_set_file_finder() and proj_context_set_search_paths() (refs #1150)
-rw-r--r--src/4D_api.cpp26
-rw-r--r--src/ctx.cpp23
-rw-r--r--src/open_lib.cpp145
-rw-r--r--src/proj.h6
-rw-r--r--src/proj_internal.h12
-rw-r--r--test/unit/CMakeLists.txt8
-rw-r--r--test/unit/Makefile.am9
-rw-r--r--test/unit/pj_transform_test.cpp32
-rw-r--r--test/unit/proj_context_test.cpp146
9 files changed, 338 insertions, 69 deletions
diff --git a/src/4D_api.cpp b/src/4D_api.cpp
index e0d15773..cdfaa8b2 100644
--- a/src/4D_api.cpp
+++ b/src/4D_api.cpp
@@ -1006,7 +1006,6 @@ static char *path_append (char *buf, const char *app, size_t *buf_size) {
static const char *empty = {""};
static char version[64] = {""};
static PJ_INFO info = {0, 0, 0, nullptr, nullptr, nullptr, nullptr, 0};
-static volatile int info_initialized = 0;
/*****************************************************************************/
PJ_INFO proj_info (void) {
@@ -1015,19 +1014,11 @@ PJ_INFO proj_info (void) {
Returns PJ_INFO struct.
******************************************************************************/
- const char * const *paths;
- size_t i, n;
-
size_t buf_size = 0;
char *buf = nullptr;
pj_acquire_lock ();
- if (0!=info_initialized) {
- pj_release_lock ();
- return info;
- }
-
info.major = PROJ_VERSION_MAJOR;
info.minor = PROJ_VERSION_MINOR;
info.patch = PROJ_VERSION_PATCH;
@@ -1037,7 +1028,6 @@ PJ_INFO proj_info (void) {
long and there is room for 64 bytes in the version string. */
sprintf (version, "%d.%d.%d", info.major, info.minor, info.patch);
- info.searchpath = empty;
info.version = version;
info.release = pj_get_release ();
@@ -1045,17 +1035,19 @@ PJ_INFO proj_info (void) {
buf = path_append (buf, getenv ("HOME"), &buf_size);
buf = path_append (buf, getenv ("PROJ_LIB"), &buf_size);
- paths = proj_get_searchpath ();
- n = (size_t) proj_get_path_count ();
+ auto ctx = pj_get_default_ctx();
+ if( ctx ) {
+ for( const auto& path: ctx->search_paths ) {
+ buf = path_append(buf, path.c_str(), &buf_size);
+ }
+ }
- for (i = 0; i < n; i++)
- buf = path_append (buf, paths[i], &buf_size);
+ pj_dalloc(const_cast<char*>(info.searchpath));
info.searchpath = buf ? buf : empty;
- info.paths = paths;
- info.path_count = n;
+ info.paths = ctx ? ctx->c_compat_paths : nullptr;
+ info.path_count = ctx ? static_cast<int>(ctx->search_paths.size()) : 0;
- info_initialized = 1;
pj_release_lock ();
return info;
}
diff --git a/src/ctx.cpp b/src/ctx.cpp
index a94eaf54..005070b0 100644
--- a/src/ctx.cpp
+++ b/src/ctx.cpp
@@ -100,6 +100,23 @@ projCtx_t projCtx_t::createDefault()
}
/************************************************************************/
+/* set_search_paths() */
+/************************************************************************/
+
+void projCtx_t::set_search_paths(const std::vector<std::string>& search_paths_in )
+{
+ search_paths = search_paths_in;
+ delete[] c_compat_paths;
+ c_compat_paths = nullptr;
+ if( !search_paths.empty() ) {
+ c_compat_paths = new const char*[search_paths.size()];
+ for( size_t i = 0; i < search_paths.size(); ++i ) {
+ c_compat_paths[i] = search_paths[i].c_str();
+ }
+ }
+}
+
+/************************************************************************/
/* projCtx_t(const projCtx_t& other) */
/************************************************************************/
@@ -110,6 +127,10 @@ projCtx_t::projCtx_t(const projCtx_t& other)
logger_app_data = other.logger_app_data;
fileapi = other.fileapi;
epsg_file_exists = other.epsg_file_exists;
+ set_search_paths(other.search_paths);
+ file_finder = other.file_finder;
+ file_finder_legacy = other.file_finder_legacy;
+ file_finder_user_data = other.file_finder_user_data;
}
/************************************************************************/
@@ -130,6 +151,7 @@ projCtx pj_get_default_ctx()
projCtx_t::~projCtx_t()
{
+ delete[] c_compat_paths;
proj_context_delete_cpp_context(cpp_context);
}
@@ -252,3 +274,4 @@ projFileAPI *pj_ctx_get_fileapi( projCtx ctx )
return nullptr;
return ctx->fileapi;
}
+
diff --git a/src/open_lib.cpp b/src/open_lib.cpp
index f4fa313f..b022b263 100644
--- a/src/open_lib.cpp
+++ b/src/open_lib.cpp
@@ -37,11 +37,7 @@
#include <string.h>
#include "proj_internal.h"
-#include "proj_internal.h"
-static const char *(*pj_finder)(const char *) = nullptr;
-static int path_count = 0;
-static char **search_path = nullptr;
static const char * proj_lib_name =
#ifdef PROJ_LIB
PROJ_LIB;
@@ -56,54 +52,98 @@ nullptr;
void pj_set_finder( const char *(*new_finder)(const char *) )
{
- pj_finder = new_finder;
+ auto ctx = pj_get_default_ctx();
+ if( ctx ) {
+ ctx->file_finder_legacy = new_finder;
+ }
}
/************************************************************************/
-/* pj_set_searchpath() */
-/* */
-/* Path control for callers that can't practically provide */
-/* pj_set_finder() style callbacks. Call with (0,NULL) as args */
-/* to clear the searchpath set. */
+/* proj_context_set_file_finder() */
/************************************************************************/
-void pj_set_searchpath ( int count, const char **path )
+/** \brief Assign a file finder callback to a context.
+ *
+ * This callback will be used whenever PROJ must open one of its resource files
+ * (proj.db database, grids, etc...)
+ *
+ * The callback will be called with the context currently in use at the moment
+ * where it is used (not necessarily the one provided during this call), and
+ * with the provided user_data (which may be NULL).
+ * The user_data must remain valid during the whole lifetime of the context.
+ *
+ * A finder set on the default context will be inherited by contexts created
+ * later.
+ *
+ * @param ctx PROJ context, or NULL for the default context.
+ * @param finder Finder callback. May be NULL
+ * @param user_data User data provided to the finder callback. May be NULL.
+ *
+ * @since PROJ 6.0
+ */
+void proj_context_set_file_finder(PJ_CONTEXT *ctx, proj_file_finder finder,
+ void* user_data)
{
- int i;
+ if( !ctx )
+ ctx = pj_get_default_ctx();
+ if( !ctx )
+ return;
+ ctx->file_finder = finder;
+ ctx->file_finder_user_data = user_data;
+}
- if (path_count > 0 && search_path != nullptr)
- {
- for (i = 0; i < path_count; i++)
- {
- pj_dalloc(search_path[i]);
- }
- pj_dalloc(search_path);
- path_count = 0;
- search_path = nullptr;
- }
+/************************************************************************/
+/* proj_context_set_search_paths() */
+/************************************************************************/
- if( count > 0 )
- {
- search_path = static_cast<char**>(pj_malloc(sizeof *search_path * count));
- for (i = 0; i < count; i++)
+
+/** \brief Sets search paths.
+ *
+ * Those search paths will be used whenever PROJ must open one of its resource files
+ * (proj.db database, grids, etc...)
+ *
+ * If set on the default context, they will be inherited by contexts created
+ * later.
+ *
+ * @param ctx PROJ context, or NULL for the default context.
+ * @param count_paths Number of paths. 0 if paths == NULL.
+ * @param paths Paths. May be NULL.
+ *
+ * @since PROJ 6.0
+ */
+void proj_context_set_search_paths(PJ_CONTEXT *ctx,
+ int count_paths,
+ const char* const* paths)
+{
+ if( !ctx )
+ ctx = pj_get_default_ctx();
+ if( !ctx )
+ return;
+ try {
+ std::vector<std::string> vector_of_paths;
+ for (int i = 0; i < count_paths; i++)
{
- search_path[i] = static_cast<char*>(pj_malloc(strlen(path[i]) + 1));
- strcpy(search_path[i], path[i]);
+ vector_of_paths.emplace_back(paths[i]);
}
+ ctx->set_search_paths(vector_of_paths);
+ } catch( const std::exception& )
+ {
}
-
- path_count = count;
}
-/* just a couple of helper functions that lets other functions
- access the otherwise private search path */
-const char * const *proj_get_searchpath(void) {
- return (const char * const *)search_path;
-}
+/************************************************************************/
+/* pj_set_searchpath() */
+/* */
+/* Path control for callers that can't practically provide */
+/* pj_set_finder() style callbacks. Call with (0,NULL) as args */
+/* to clear the searchpath set. */
+/************************************************************************/
-int proj_get_path_count(void) {
- return path_count;
+void pj_set_searchpath ( int count, const char **path )
+{
+ proj_context_set_search_paths( nullptr, count, const_cast<const char* const*>(path) );
}
+
/************************************************************************/
/* pj_open_lib_ex() */
/************************************************************************/
@@ -112,16 +152,19 @@ static PAFile
pj_open_lib_ex(projCtx ctx, const char *name, const char *mode,
char* out_full_filename, size_t out_full_filename_size) {
char fname[MAX_PATH_FILENAME+1];
- const char *sysname;
+ const char *sysname = nullptr;
PAFile fid;
int n = 0;
- int i;
#ifdef WIN32
static const char dir_chars[] = "/\\";
#else
static const char dir_chars[] = "/";
#endif
+ if( ctx == nullptr ) {
+ ctx = pj_get_default_ctx();
+ }
+
if( out_full_filename != nullptr && out_full_filename_size > 0 )
out_full_filename[0] = '\0';
@@ -148,8 +191,11 @@ pj_open_lib_ex(projCtx ctx, const char *name, const char *mode,
sysname = name;
/* or try to use application provided file finder */
- else if( pj_finder != nullptr && pj_finder( name ) != nullptr )
- sysname = pj_finder( name );
+ else if( ctx && ctx->file_finder != nullptr && (sysname = ctx->file_finder( ctx, name, ctx->file_finder_user_data )) != nullptr )
+ ;
+
+ else if( ctx && ctx->file_finder_legacy != nullptr && (sysname = ctx->file_finder_legacy( name )) != nullptr )
+ ;
/* or is environment PROJ_LIB defined */
else if ((sysname = getenv("PROJ_LIB")) || (sysname = proj_lib_name)) {
@@ -176,16 +222,19 @@ pj_open_lib_ex(projCtx ctx, const char *name, const char *mode,
}
/* If none of those work and we have a search path, try it */
- if (!fid && path_count > 0)
+ std::string tmp;
+ if (!fid && ctx && !ctx->search_paths.empty() )
{
- for (i = 0; fid == nullptr && i < path_count; i++)
- {
- if( strlen(search_path[i]) + 1 + strlen(name) + 1 <= sizeof(fname) )
- {
- sprintf(fname, "%s%c%s", search_path[i], DIR_CHAR, name);
- sysname = fname;
+ for( const auto& path: ctx->search_paths ) {
+ try {
+ tmp = path + DIR_CHAR + name;
+ sysname = tmp.c_str();
fid = pj_ctx_fopen(ctx, sysname, mode);
+ } catch( const std::exception& )
+ {
}
+ if( fid )
+ break;
}
if (fid)
{
diff --git a/src/proj.h b/src/proj.h
index ef14f237..214bad17 100644
--- a/src/proj.h
+++ b/src/proj.h
@@ -336,6 +336,12 @@ typedef struct projCtx_t PJ_CONTEXT;
PJ_CONTEXT PROJ_DLL *proj_context_create (void);
PJ_CONTEXT PROJ_DLL *proj_context_destroy (PJ_CONTEXT *ctx);
+/** Callback to resolve a filename to a full path */
+typedef const char* (*proj_file_finder) (PJ_CONTEXT *ctx, const char*, void* user_data);
+
+void PROJ_DLL proj_context_set_file_finder(PJ_CONTEXT *ctx, proj_file_finder finder, void* user_data);
+void PROJ_DLL proj_context_set_search_paths(PJ_CONTEXT *ctx, int count_paths, const char* const* paths);
+
void PROJ_DLL proj_context_use_proj4_init_rules(PJ_CONTEXT *ctx, int enable);
int PROJ_DLL proj_context_get_use_proj4_init_rules(PJ_CONTEXT *ctx, int from_legacy_code_path);
diff --git a/src/proj_internal.h b/src/proj_internal.h
index 31365bce..bbadb376 100644
--- a/src/proj_internal.h
+++ b/src/proj_internal.h
@@ -228,9 +228,6 @@ char *pj_make_args (size_t argc, char **argv);
/* Lowest level: Minimum support for fileapi */
void proj_fileapi_set (PJ *P, void *fileapi);
-const char * const *proj_get_searchpath(void);
-int proj_get_path_count(void);
-
typedef struct { double r, i; } COMPLEX;
/* Forward declarations and typedefs for stuff needed inside the PJ object */
@@ -653,12 +650,21 @@ struct projCtx_t {
int use_proj4_init_rules = -1; /* -1 = unknown, 0 = no, 1 = yes */
int epsg_file_exists = -1; /* -1 = unknown, 0 = no, 1 = yes */
+ std::vector<std::string> search_paths{};
+ const char **c_compat_paths = nullptr; // same, but for projinfo usage
+
+ const char* (*file_finder_legacy) (const char*) = nullptr; // Only for proj_api compat. To remove once it is removed
+ const char* (*file_finder) (PJ_CONTEXT *, const char*, void* user_data) = nullptr;
+ void* file_finder_user_data = nullptr;
+
projCtx_t() = default;
projCtx_t(const projCtx_t&);
~projCtx_t();
projCtx_t& operator= (const projCtx_t&) = delete;
+ void set_search_paths(const std::vector<std::string>& search_paths_in);
+
static projCtx_t createDefault();
};
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
index 09405147..a157f630 100644
--- a/test/unit/CMakeLists.txt
+++ b/test/unit/CMakeLists.txt
@@ -82,6 +82,14 @@ target_link_libraries(proj_angular_io_test
${PROJ_LIBRARIES})
add_test(NAME proj_angular_io_test COMMAND proj_angular_io_test)
+add_executable(proj_context_test
+ main.cpp
+ proj_context_test.cpp)
+target_link_libraries(proj_context_test
+ gtest
+ ${PROJ_LIBRARIES})
+add_test(NAME proj_context_test COMMAND proj_context_test)
+
if (MSVC AND BUILD_LIBPROJ_SHARED)
# ph_phi2_test not compatible of a .dll build
else()
diff --git a/test/unit/Makefile.am b/test/unit/Makefile.am
index e6d95e9c..34624b0d 100644
--- a/test/unit/Makefile.am
+++ b/test/unit/Makefile.am
@@ -13,6 +13,7 @@ noinst_PROGRAMS = pj_transform_test
noinst_PROGRAMS += pj_phi2_test
noinst_PROGRAMS += proj_errno_string_test
noinst_PROGRAMS += proj_angular_io_test
+noinst_PROGRAMS += proj_context_test
noinst_PROGRAMS += test_cpp_api
noinst_PROGRAMS += gie_self_tests
noinst_PROGRAMS += include_proj_h_from_c
@@ -41,6 +42,12 @@ proj_angular_io_test_LDADD = ../../src/libproj.la ../../test/googletest/libgtest
proj_angular_io_test-check: proj_angular_io_test
./proj_angular_io_test
+proj_context_test_SOURCES = proj_context_test.cpp main.cpp
+proj_context_test_LDADD = ../../src/libproj.la ../../test/googletest/libgtest.la
+
+proj_context_test-check: proj_context_test
+ ./proj_context_test
+
test_cpp_api_SOURCES = test_util.cpp test_common.cpp test_crs.cpp test_metadata.cpp test_io.cpp test_operation.cpp test_datum.cpp test_factory.cpp test_c_api.cpp main.cpp
test_cpp_api_LDADD = ../../src/libproj.la ../../test/googletest/libgtest.la @SQLITE3_LDFLAGS@
@@ -55,4 +62,4 @@ gie_self_tests-check: gie_self_tests
include_proj_h_from_c_SOURCES = include_proj_h_from_c.c
-check-local: pj_transform_test-check pj_phi2_test-check proj_errno_string_test-check proj_angular_io_test-check test_cpp_api-check gie_self_tests-check
+check-local: pj_transform_test-check pj_phi2_test-check proj_errno_string_test-check proj_angular_io_test-check proj_context_test-check test_cpp_api-check gie_self_tests-check
diff --git a/test/unit/pj_transform_test.cpp b/test/unit/pj_transform_test.cpp
index ea9706dd..c2926df8 100644
--- a/test/unit/pj_transform_test.cpp
+++ b/test/unit/pj_transform_test.cpp
@@ -581,4 +581,36 @@ TEST(pj_transform_test, init_epsg) {
pj_free(dst);
}
+// ---------------------------------------------------------------------------
+
+TEST(proj_api_h, pj_set_searchpath ) {
+
+ const char* path = "/i_do/not/exit";
+ pj_set_searchpath(1, &path);
+ {
+ auto info = proj_info();
+ EXPECT_EQ(info.path_count, 1);
+ ASSERT_NE(info.paths, nullptr);
+ ASSERT_NE(info.paths[0], nullptr);
+ EXPECT_EQ(std::string(info.paths[0]), path);
+ }
+
+ pj_set_searchpath(0, nullptr);
+ {
+ auto info = proj_info();
+ EXPECT_EQ(info.path_count, 0);
+ EXPECT_EQ(info.paths, nullptr);
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(proj_api_h, pj_set_finder ) {
+
+ const auto myfinder = [](const char*) -> const char* { return nullptr; };
+ pj_set_finder(myfinder);
+
+ pj_set_finder(nullptr);
+}
+
} // namespace
diff --git a/test/unit/proj_context_test.cpp b/test/unit/proj_context_test.cpp
new file mode 100644
index 00000000..23c46f29
--- /dev/null
+++ b/test/unit/proj_context_test.cpp
@@ -0,0 +1,146 @@
+/******************************************************************************
+ *
+ * Project: PROJ
+ * Purpose: Test functions in proj_context namespae
+ * Author: Even Rouault <even dot rouault at spatialys dot com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2019, Even Rouault <even dot rouault at spatialys dot com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+#include <stdlib.h>
+#ifdef _MSC_VER
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+#include "proj.h"
+#include "proj_internal.h"
+
+#include "gtest_include.h"
+
+namespace {
+
+static std::string createTempDict(std::string &dirname) {
+ const char *temp_dir = getenv("TEMP");
+ if (!temp_dir) {
+ temp_dir = getenv("TMP");
+ }
+#ifndef WIN32
+ if (!temp_dir) {
+ temp_dir = "/tmp";
+ }
+#endif
+ if (!temp_dir)
+ return std::string();
+
+ dirname = temp_dir;
+
+ std::string tmpFilename;
+ tmpFilename = temp_dir;
+ tmpFilename += DIR_CHAR;
+ tmpFilename += "temp_proj_dic";
+
+ FILE *f = fopen(tmpFilename.c_str(), "wt");
+ if (!f)
+ return std::string();
+ fprintf(
+ f,
+ "<MY_PIPELINE> +proj=pipeline +step +proj=utm +zone=31 +ellps=GRS80\n");
+ fclose(f);
+ return tmpFilename;
+}
+
+// ---------------------------------------------------------------------------
+
+static int MyUnlink(const std::string &filename) {
+#ifdef _MSC_VER
+ return _unlink(filename.c_str());
+#else
+ return unlink(filename.c_str());
+#endif
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(proj_context, proj_context_set_file_finder) {
+
+ std::string dirname;
+ auto filename = createTempDict(dirname);
+ if (filename.empty())
+ return;
+
+ auto ctx = proj_context_create();
+
+ struct FinderData {
+ PJ_CONTEXT *got_ctx = nullptr;
+ std::string dirname{};
+ std::string tmpFilename{};
+ };
+
+ const auto finder = [](PJ_CONTEXT *got_ctx, const char *file,
+ void *user_data) -> const char * {
+ auto finderData = static_cast<FinderData *>(user_data);
+ finderData->got_ctx = got_ctx;
+ finderData->tmpFilename = finderData->dirname;
+ finderData->tmpFilename += DIR_CHAR;
+ finderData->tmpFilename += file;
+ return finderData->tmpFilename.c_str();
+ };
+
+ FinderData finderData;
+ finderData.dirname = dirname;
+ proj_context_set_file_finder(ctx, finder, &finderData);
+
+ auto P = proj_create(ctx, "+init=temp_proj_dic:MY_PIPELINE");
+ EXPECT_NE(P, nullptr);
+ proj_destroy(P);
+
+ EXPECT_EQ(finderData.got_ctx, ctx);
+
+ proj_context_destroy(ctx);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(proj_context, proj_context_set_search_paths) {
+
+ std::string dirname;
+ auto filename = createTempDict(dirname);
+ if (filename.empty())
+ return;
+
+ auto ctx = proj_context_create();
+
+ const char *path = dirname.c_str();
+ proj_context_set_search_paths(ctx, 1, &path);
+
+ auto P = proj_create(ctx, "+init=temp_proj_dic:MY_PIPELINE");
+ EXPECT_NE(P, nullptr);
+ proj_destroy(P);
+
+ proj_context_destroy(ctx);
+
+ MyUnlink(filename);
+}
+
+} // namespace