diff options
| -rw-r--r-- | src/4D_api.cpp | 26 | ||||
| -rw-r--r-- | src/ctx.cpp | 23 | ||||
| -rw-r--r-- | src/open_lib.cpp | 145 | ||||
| -rw-r--r-- | src/proj.h | 6 | ||||
| -rw-r--r-- | src/proj_internal.h | 12 | ||||
| -rw-r--r-- | test/unit/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | test/unit/Makefile.am | 9 | ||||
| -rw-r--r-- | test/unit/pj_transform_test.cpp | 32 | ||||
| -rw-r--r-- | test/unit/proj_context_test.cpp | 146 |
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) { @@ -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 |
