From d6dad2cee4307f6b190a96dd48942645060919cc Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 9 Jan 2019 17:05:21 +0100 Subject: Allocate projCtx_t as a C++ object --- src/ctx.cpp | 94 ++++++++++++++++++++++++++------------------------ src/internal.cpp | 2 +- src/iso19111/c_api.cpp | 4 +-- src/log.cpp | 2 +- src/proj_internal.h | 24 ++++++++----- 5 files changed, 69 insertions(+), 57 deletions(-) (limited to 'src') diff --git a/src/ctx.cpp b/src/ctx.cpp index 195f3b7f..a94eaf54 100644 --- a/src/ctx.cpp +++ b/src/ctx.cpp @@ -29,12 +29,10 @@ #include #include +#include + #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,45 +79,60 @@ 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; + projCtx_t ctx; + ctx.debug_level = PJ_LOG_NONE; + ctx.logger = pj_stderr_logger; + ctx.fileapi = pj_get_default_fileapi(); - pj_acquire_lock(); - - /* 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; - } - default_context_initialized = 1; + 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; +} + +/************************************************************************/ +/* projCtx_t(const projCtx_t& other) */ +/************************************************************************/ - pj_release_lock(); +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; +} +/************************************************************************/ +/* 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() +{ + proj_context_delete_cpp_context(cpp_context); +} + /************************************************************************/ /* pj_ctx_alloc() */ /************************************************************************/ @@ -127,15 +140,7 @@ projCtx pj_get_default_ctx() 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 +150,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 +214,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 +226,7 @@ void *pj_ctx_get_app_data( projCtx ctx ) { if (nullptr==ctx) return nullptr; - return ctx->app_data; + return ctx->logger_app_data; } /************************************************************************/ 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/proj_internal.h b/src/proj_internal.h index 63e10407..31365bce 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -644,14 +644,22 @@ 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 */ + + projCtx_t() = default; + projCtx_t(const projCtx_t&); + ~projCtx_t(); + + projCtx_t& operator= (const projCtx_t&) = delete; + + static projCtx_t createDefault(); }; /* Generate pj_list external or make list from include file */ -- cgit v1.2.3 From 3cc9336b038118de424d018dd0bdd90e329ad8c1 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 9 Jan 2019 18:30:17 +0100 Subject: proj.h: add proj_context_set_file_finder() and proj_context_set_search_paths() (refs #1150) --- src/4D_api.cpp | 26 ++++------ src/ctx.cpp | 23 +++++++++ src/open_lib.cpp | 145 +++++++++++++++++++++++++++++++++++----------------- src/proj.h | 6 +++ src/proj_internal.h | 12 +++-- 5 files changed, 144 insertions(+), 68 deletions(-) (limited to 'src') 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(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(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 @@ -99,6 +99,23 @@ projCtx_t projCtx_t::createDefault() return ctx; } +/************************************************************************/ +/* set_search_paths() */ +/************************************************************************/ + +void projCtx_t::set_search_paths(const std::vector& 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 @@ -36,12 +36,8 @@ #include #include -#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(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 vector_of_paths; + for (int i = 0; i < count_paths; i++) { - search_path[i] = static_cast(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(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 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& search_paths_in); + static projCtx_t createDefault(); }; -- cgit v1.2.3 From 03b8e99d388013558b1a2ec02a82af4763b26b34 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 9 Jan 2019 19:08:27 +0100 Subject: PROJ_LIB: support multiple paths supported by colon on Unix and semi-colon on Windows (fixes #1150) --- src/open_lib.cpp | 192 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 107 insertions(+), 85 deletions(-) (limited to 'src') diff --git a/src/open_lib.cpp b/src/open_lib.cpp index b022b263..510704e9 100644 --- a/src/open_lib.cpp +++ b/src/open_lib.cpp @@ -30,12 +30,18 @@ #define PJ_LIB__ +#ifndef FROM_PROJ_CPP +#define FROM_PROJ_CPP +#endif + #include #include #include #include #include +#include "proj/internal/internal.hpp" + #include "proj_internal.h" static const char * proj_lib_name = @@ -151,92 +157,73 @@ void pj_set_searchpath ( int count, const char **path ) 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 = nullptr; - PAFile fid; - int n = 0; + 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( ctx == nullptr ) { - ctx = pj_get_default_ctx(); - } - - 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( 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)) { - 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'; + + /* 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; - /* If none of those work and we have a search path, try it */ - std::string tmp; - if (!fid && ctx && !ctx->search_paths.empty() ) - { - for( const auto& path: ctx->search_paths ) { - try { - tmp = path + DIR_CHAR + name; - sysname = tmp.c_str(); + /* 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); - } catch( const std::exception& ) - { + if( fid ) + break; } - 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 ) { @@ -245,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; + } } /************************************************************************/ -- cgit v1.2.3