aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKristian Evers <kristianevers@gmail.com>2019-01-09 23:55:39 +0100
committerGitHub <noreply@github.com>2019-01-09 23:55:39 +0100
commit763ff547aea5e1013b49254b62fc06060eccfaaf (patch)
treedaf7a4e66059e2021a398bc80e93a120a1147bad
parent6d5924842035ae6b5af8a6685f5c531e667c7f18 (diff)
parent03b8e99d388013558b1a2ec02a82af4763b26b34 (diff)
downloadPROJ-763ff547aea5e1013b49254b62fc06060eccfaaf.tar.gz
PROJ-763ff547aea5e1013b49254b62fc06060eccfaaf.zip
Merge pull request #1218 from rouault/search_path
Add API in proj.h to set a file finder callback and search paths; support multiple directories in PROJ_LIB
-rw-r--r--docs/source/usage/environmentvars.rst8
-rw-r--r--src/4D_api.cpp26
-rw-r--r--src/ctx.cpp115
-rw-r--r--src/internal.cpp2
-rw-r--r--src/iso19111/c_api.cpp4
-rw-r--r--src/log.cpp2
-rw-r--r--src/open_lib.cpp297
-rw-r--r--src/proj.h6
-rw-r--r--src/proj_internal.h36
-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
13 files changed, 499 insertions, 192 deletions
diff --git a/docs/source/usage/environmentvars.rst b/docs/source/usage/environmentvars.rst
index 24954158..478e2eab 100644
--- a/docs/source/usage/environmentvars.rst
+++ b/docs/source/usage/environmentvars.rst
@@ -31,8 +31,12 @@ done by setting the variable with no content::
.. envvar:: PROJ_LIB
The location of PROJ :doc:`resource files<../resource_files>`.
- It is only possible to specify a single library in :envvar:`PROJ_LIB`; e.g. it
- does not behave like PATH. PROJ is hardcoded to look for resource files
+
+ Starting with PROJ 6, multiple directories can be specified. On Unix, they
+ should be separated by the colon (:) character. on Windows, by the semi-colon (;)
+ character.
+
+ PROJ is hardcoded to look for resource files
in other locations as well, amongst those are the users home directory,
``/usr/share/proj`` and the current folder.
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 195f3b7f..005070b0 100644
--- a/src/ctx.cpp
+++ b/src/ctx.cpp
@@ -29,12 +29,10 @@
#include <stdlib.h>
#include <string.h>
+#include <new>
+
#include "proj_experimental.h"
#include "proj_internal.h"
-#include "proj_internal.h"
-
-static projCtx_t default_context;
-static volatile int default_context_initialized = 0;
/************************************************************************/
/* pj_get_ctx() */
@@ -81,61 +79,90 @@ void proj_assign_context( PJ* pj, PJ_CONTEXT* ctx )
}
/************************************************************************/
-/* pj_get_default_ctx() */
+/* createDefault() */
/************************************************************************/
-projCtx pj_get_default_ctx()
-
+projCtx_t projCtx_t::createDefault()
{
- /* If already initialized, don't bother locking */
- if( default_context_initialized )
- return &default_context;
-
- pj_acquire_lock();
+ projCtx_t ctx;
+ ctx.debug_level = PJ_LOG_NONE;
+ ctx.logger = pj_stderr_logger;
+ ctx.fileapi = pj_get_default_fileapi();
- /* Ask again, since it may have been initialized in another thread */
- if( !default_context_initialized )
+ if( getenv("PROJ_DEBUG") != nullptr )
{
- default_context.last_errno = 0;
- default_context.debug_level = PJ_LOG_NONE;
- default_context.logger = pj_stderr_logger;
- default_context.app_data = nullptr;
- default_context.fileapi = pj_get_default_fileapi();
- default_context.cpp_context = nullptr;
- default_context.use_proj4_init_rules = -1;
- default_context.epsg_file_exists = -1;
-
- if( getenv("PROJ_DEBUG") != nullptr )
- {
- if( atoi(getenv("PROJ_DEBUG")) >= -PJ_LOG_DEBUG_MINOR )
- default_context.debug_level = atoi(getenv("PROJ_DEBUG"));
- else
- default_context.debug_level = PJ_LOG_DEBUG_MINOR;
+ if( atoi(getenv("PROJ_DEBUG")) >= -PJ_LOG_DEBUG_MINOR )
+ ctx.debug_level = atoi(getenv("PROJ_DEBUG"));
+ else
+ ctx.debug_level = PJ_LOG_DEBUG_MINOR;
+ }
+ return ctx;
+}
+
+/************************************************************************/
+/* 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();
}
- default_context_initialized = 1;
}
+}
- pj_release_lock();
+/************************************************************************/
+/* projCtx_t(const projCtx_t& other) */
+/************************************************************************/
+projCtx_t::projCtx_t(const projCtx_t& other)
+{
+ debug_level = other.debug_level;
+ logger = other.logger;
+ 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;
+}
+
+/************************************************************************/
+/* pj_get_default_ctx() */
+/************************************************************************/
+
+projCtx pj_get_default_ctx()
+
+{
+ // C++11 rules guarantee a thread-safe instanciation.
+ static projCtx_t default_context(projCtx_t::createDefault());
return &default_context;
}
/************************************************************************/
+/* ~projCtx_t() */
+/************************************************************************/
+
+projCtx_t::~projCtx_t()
+{
+ delete[] c_compat_paths;
+ proj_context_delete_cpp_context(cpp_context);
+}
+
+/************************************************************************/
/* pj_ctx_alloc() */
/************************************************************************/
projCtx pj_ctx_alloc()
{
- projCtx ctx = (projCtx_t *) malloc(sizeof(projCtx_t));
- if (nullptr==ctx)
- return nullptr;
- memcpy( ctx, pj_get_default_ctx(), sizeof(projCtx_t) );
- ctx->last_errno = 0;
- ctx->cpp_context = nullptr;
- ctx->use_proj4_init_rules = -1;
-
- return ctx;
+ return new (std::nothrow) projCtx_t(*pj_get_default_ctx());
}
/************************************************************************/
@@ -145,8 +172,7 @@ projCtx pj_ctx_alloc()
void pj_ctx_free( projCtx ctx )
{
- proj_context_delete_cpp_context( ctx->cpp_context );
- pj_dealloc( ctx );
+ delete ctx;
}
/************************************************************************/
@@ -210,7 +236,7 @@ void pj_ctx_set_app_data( projCtx ctx, void *new_app_data )
{
if (nullptr==ctx)
return;
- ctx->app_data = new_app_data;
+ ctx->logger_app_data = new_app_data;
}
/************************************************************************/
@@ -222,7 +248,7 @@ void *pj_ctx_get_app_data( projCtx ctx )
{
if (nullptr==ctx)
return nullptr;
- return ctx->app_data;
+ return ctx->logger_app_data;
}
/************************************************************************/
@@ -248,3 +274,4 @@ projFileAPI *pj_ctx_get_fileapi( projCtx ctx )
return nullptr;
return ctx->fileapi;
}
+
diff --git a/src/internal.cpp b/src/internal.cpp
index f774cad6..fbc7c9a1 100644
--- a/src/internal.cpp
+++ b/src/internal.cpp
@@ -449,7 +449,7 @@ void proj_log_func (PJ_CONTEXT *ctx, void *app_data, PJ_LOG_FUNCTION logf) {
pj_get_default_ctx ();
if (nullptr==ctx)
return;
- ctx->app_data = app_data;
+ ctx->logger_app_data = app_data;
if (nullptr!=logf)
ctx->logger = logf;
}
diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp
index 5717a76a..d13c33c7 100644
--- a/src/iso19111/c_api.cpp
+++ b/src/iso19111/c_api.cpp
@@ -74,7 +74,7 @@ static void PROJ_NO_INLINE proj_log_error(PJ_CONTEXT *ctx, const char *function,
std::string msg(function);
msg += ": ";
msg += text;
- ctx->logger(ctx->app_data, PJ_LOG_ERROR, msg.c_str());
+ ctx->logger(ctx->logger_app_data, PJ_LOG_ERROR, msg.c_str());
}
// ---------------------------------------------------------------------------
@@ -84,7 +84,7 @@ static void PROJ_NO_INLINE proj_log_debug(PJ_CONTEXT *ctx, const char *function,
std::string msg(function);
msg += ": ";
msg += text;
- ctx->logger(ctx->app_data, PJ_LOG_DEBUG, msg.c_str());
+ ctx->logger(ctx->logger_app_data, PJ_LOG_DEBUG, msg.c_str());
}
// ---------------------------------------------------------------------------
diff --git a/src/log.cpp b/src/log.cpp
index 4c15772b..3cc10cfd 100644
--- a/src/log.cpp
+++ b/src/log.cpp
@@ -74,7 +74,7 @@ void pj_vlog( projCtx ctx, int level, const char *fmt, va_list args )
/* we should use vsnprintf where available once we add configure detect.*/
vsprintf( msg_buf, fmt, args );
- ctx->logger( ctx->app_data, level, msg_buf );
+ ctx->logger( ctx->logger_app_data, level, msg_buf );
free( msg_buf );
}
diff --git a/src/open_lib.cpp b/src/open_lib.cpp
index f4fa313f..510704e9 100644
--- a/src/open_lib.cpp
+++ b/src/open_lib.cpp
@@ -30,18 +30,20 @@
#define PJ_LIB__
+#ifndef FROM_PROJ_CPP
+#define FROM_PROJ_CPP
+#endif
+
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include "proj_internal.h"
+#include "proj/internal/internal.hpp"
+
#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 +58,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() */
/************************************************************************/
@@ -111,83 +157,73 @@ int proj_get_path_count(void) {
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;
- PAFile fid;
- int n = 0;
- int i;
+ try {
+ std::string fname;
+ const char *sysname = nullptr;
+ PAFile fid = nullptr;
#ifdef WIN32
- static const char dir_chars[] = "/\\";
+ static const char dir_chars[] = "/\\";
+ const char dirSeparator = ';';
#else
- static const char dir_chars[] = "/";
+ static const char dir_chars[] = "/";
+ const char dirSeparator = ':';
#endif
- if( out_full_filename != nullptr && out_full_filename_size > 0 )
- out_full_filename[0] = '\0';
-
- /* check if ~/name */
- if (*name == '~' && strchr(dir_chars,name[1]) )
- if ((sysname = getenv("HOME")) != nullptr) {
- if( strlen(sysname) + 1 + strlen(name) + 1 > sizeof(fname) )
- {
- return nullptr;
- }
- (void)strcpy(fname, sysname);
- fname[n = (int)strlen(fname)] = DIR_CHAR;
- fname[++n] = '\0';
- (void)strcpy(fname+n, name + 1);
- sysname = fname;
- } else
- return nullptr;
-
- /* or fixed path: /name, ./name or ../name */
- else if (strchr(dir_chars,*name)
- || (*name == '.' && strchr(dir_chars,name[1]))
- || (!strncmp(name, "..", 2) && strchr(dir_chars,name[2]))
- || (name[1] == ':' && strchr(dir_chars,name[2])) )
- sysname = name;
-
- /* or try to use application provided file finder */
- else if( pj_finder != nullptr && pj_finder( name ) != nullptr )
- sysname = pj_finder( name );
-
- /* or is environment PROJ_LIB defined */
- else if ((sysname = getenv("PROJ_LIB")) || (sysname = proj_lib_name)) {
- if( strlen(sysname) + 1 + strlen(name) + 1 > sizeof(fname) )
- {
- return nullptr;
+ if( ctx == nullptr ) {
+ ctx = pj_get_default_ctx();
}
- (void)strcpy(fname, sysname);
- fname[n = (int)strlen(fname)] = DIR_CHAR;
- fname[++n] = '\0';
- (void)strcpy(fname+n, name);
- sysname = fname;
- } else /* just try it bare bones */
- sysname = name;
-
- if ((fid = pj_ctx_fopen(ctx, sysname, mode)) != nullptr)
- {
+
if( out_full_filename != nullptr && out_full_filename_size > 0 )
- {
- strncpy(out_full_filename, sysname, out_full_filename_size);
- out_full_filename[out_full_filename_size-1] = '\0';
- }
- errno = 0;
- }
+ out_full_filename[0] = '\0';
- /* If none of those work and we have a search path, try it */
- if (!fid && path_count > 0)
- {
- 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;
+ /* check if ~/name */
+ if (*name == '~' && strchr(dir_chars,name[1]) )
+ if ((sysname = getenv("HOME")) != nullptr) {
+ fname = sysname;
+ fname += DIR_CHAR;
+ fname += name;
+ sysname = fname.c_str();
+ } else
+ return nullptr;
+
+ /* or fixed path: /name, ./name or ../name */
+ else if (strchr(dir_chars,*name)
+ || (*name == '.' && strchr(dir_chars,name[1]))
+ || (!strncmp(name, "..", 2) && strchr(dir_chars,name[2]))
+ || (name[1] == ':' && strchr(dir_chars,name[2])) )
+ sysname = name;
+
+ /* or try to use application provided file finder */
+ 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")) != nullptr) {
+
+ auto paths = NS_PROJ::internal::split(std::string(sysname), dirSeparator);
+ for( const auto& path: paths ) {
+ fname = path;
+ fname += DIR_CHAR;
+ fname += name;
+ sysname = fname.c_str();
fid = pj_ctx_fopen(ctx, sysname, mode);
+ if( fid )
+ break;
}
- }
- if (fid)
+
+ /* or hardcoded path */
+ } else if ((sysname = proj_lib_name) != nullptr) {
+ fname = sysname;
+ fname += DIR_CHAR;
+ fname += name;
+ sysname = fname.c_str();
+ } else /* just try it bare bones */
+ sysname = name;
+
+ if ( fid != nullptr || (fid = pj_ctx_fopen(ctx, sysname, mode)) != nullptr)
{
if( out_full_filename != nullptr && out_full_filename_size > 0 )
{
@@ -196,17 +232,52 @@ pj_open_lib_ex(projCtx ctx, const char *name, const char *mode,
}
errno = 0;
}
- }
- if( ctx->last_errno == 0 && errno != 0 )
- pj_ctx_set_errno( ctx, errno );
+ /* If none of those work and we have a search path, try it */
+ if (!fid && ctx && !ctx->search_paths.empty() )
+ {
+ for( const auto& path: ctx->search_paths ) {
+ try {
+ fname = path;
+ fname += DIR_CHAR;
+ fname += name;
+ sysname = fname.c_str();
+ fid = pj_ctx_fopen(ctx, sysname, mode);
+ } catch( const std::exception& )
+ {
+ }
+ if( fid )
+ break;
+ }
+ if (fid)
+ {
+ if( out_full_filename != nullptr && out_full_filename_size > 0 )
+ {
+ strncpy(out_full_filename, sysname, out_full_filename_size);
+ out_full_filename[out_full_filename_size-1] = '\0';
+ }
+ errno = 0;
+ }
+ }
+
+ if( ctx->last_errno == 0 && errno != 0 )
+ pj_ctx_set_errno( ctx, errno );
- pj_log( ctx, PJ_LOG_DEBUG_MAJOR,
- "pj_open_lib(%s): call fopen(%s) - %s",
- name, sysname,
- fid == nullptr ? "failed" : "succeeded" );
+ pj_log( ctx, PJ_LOG_DEBUG_MAJOR,
+ "pj_open_lib(%s): call fopen(%s) - %s",
+ name, sysname,
+ fid == nullptr ? "failed" : "succeeded" );
- return(fid);
+ return(fid);
+ }
+ catch( const std::exception& ) {
+
+ pj_log( ctx, PJ_LOG_DEBUG_MAJOR,
+ "pj_open_lib(%s): out of memory",
+ name );
+
+ return nullptr;
+ }
}
/************************************************************************/
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 63e10407..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 */
@@ -644,14 +641,31 @@ struct projCppContext;
/* proj thread context */
struct projCtx_t {
- int last_errno;
- int debug_level;
- void (*logger)(void *, int, const char *);
- void *app_data;
- struct projFileAPI_t *fileapi;
- struct projCppContext* cpp_context; /* internal context for C++ code */
- int use_proj4_init_rules; /* -1 = unknown, 0 = no, 1 = yes */
- int epsg_file_exists; /* -1 = unknown, 0 = no, 1 = yes */
+ int last_errno = 0;
+ int debug_level = 0;
+ void (*logger)(void *, int, const char *) = nullptr;
+ void *logger_app_data = nullptr;
+ struct projFileAPI_t *fileapi = nullptr;
+ struct projCppContext* cpp_context = nullptr; /* internal context for C++ code */
+ 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();
};
/* Generate pj_list external or make list from include file */
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