Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * Project: PROJ.4 |
3 | | * Purpose: Implementation of the PJ_CONTEXT thread context object. |
4 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
5 | | * |
6 | | ****************************************************************************** |
7 | | * Copyright (c) 2010, Frank Warmerdam |
8 | | * |
9 | | * Permission is hereby granted, free of charge, to any person obtaining a |
10 | | * copy of this software and associated documentation files (the "Software"), |
11 | | * to deal in the Software without restriction, including without limitation |
12 | | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
13 | | * and/or sell copies of the Software, and to permit persons to whom the |
14 | | * Software is furnished to do so, subject to the following conditions: |
15 | | * |
16 | | * The above copyright notice and this permission notice shall be included |
17 | | * in all copies or substantial portions of the Software. |
18 | | * |
19 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
20 | | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
21 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
22 | | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
23 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
24 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
25 | | * DEALINGS IN THE SOFTWARE. |
26 | | *****************************************************************************/ |
27 | | #ifndef FROM_PROJ_CPP |
28 | | #define FROM_PROJ_CPP |
29 | | #endif |
30 | | |
31 | | #include <errno.h> |
32 | | #include <stdlib.h> |
33 | | #include <string.h> |
34 | | |
35 | | #include <new> |
36 | | |
37 | | #include "filemanager.hpp" |
38 | | #include "proj/internal/internal.hpp" |
39 | | #include "proj/internal/io_internal.hpp" |
40 | | #include "proj_experimental.h" |
41 | | #include "proj_internal.h" |
42 | | |
43 | | /************************************************************************/ |
44 | | /* pj_get_ctx() */ |
45 | | /************************************************************************/ |
46 | | |
47 | | PJ_CONTEXT *pj_get_ctx(PJ *pj) |
48 | | |
49 | 0 | { |
50 | 0 | if (nullptr == pj) |
51 | 0 | return pj_get_default_ctx(); |
52 | 0 | if (nullptr == pj->ctx) |
53 | 0 | return pj_get_default_ctx(); |
54 | 0 | return pj->ctx; |
55 | 0 | } |
56 | | |
57 | | /************************************************************************/ |
58 | | /* proj_assign_context() */ |
59 | | /************************************************************************/ |
60 | | |
61 | | /** \brief Re-assign a context to a PJ* object. |
62 | | * |
63 | | * This may be useful if the PJ* has been created with a context that is |
64 | | * thread-specific, and is later used in another thread. In that case, |
65 | | * the user may want to assign another thread-specific context to the |
66 | | * object. |
67 | | */ |
68 | 0 | void proj_assign_context(PJ *pj, PJ_CONTEXT *ctx) { |
69 | 0 | if (pj == nullptr) |
70 | 0 | return; |
71 | 0 | pj->ctx = ctx; |
72 | 0 | if (pj->reassign_context) { |
73 | 0 | pj->reassign_context(pj, ctx); |
74 | 0 | } |
75 | 0 | for (const auto &alt : pj->alternativeCoordinateOperations) { |
76 | 0 | proj_assign_context(alt.pj, ctx); |
77 | 0 | } |
78 | 0 | } |
79 | | |
80 | | /************************************************************************/ |
81 | | /* createDefault() */ |
82 | | /************************************************************************/ |
83 | | |
84 | 0 | pj_ctx pj_ctx::createDefault() { |
85 | 0 | pj_ctx ctx; |
86 | 0 | ctx.debug_level = PJ_LOG_ERROR; |
87 | 0 | ctx.logger = pj_stderr_logger; |
88 | 0 | NS_PROJ::FileManager::fillDefaultNetworkInterface(&ctx); |
89 | |
|
90 | 0 | const char *projDebug = getenv("PROJ_DEBUG"); |
91 | 0 | if (projDebug != nullptr) { |
92 | 0 | if (NS_PROJ::internal::ci_equal(projDebug, "ON")) { |
93 | 0 | ctx.debug_level = PJ_LOG_DEBUG; |
94 | 0 | } else if (NS_PROJ::internal::ci_equal(projDebug, "OFF")) { |
95 | 0 | ctx.debug_level = PJ_LOG_ERROR; |
96 | 0 | } else if (projDebug[0] == '-' || |
97 | 0 | (projDebug[0] >= '0' && projDebug[0] <= '9')) { |
98 | 0 | const int debugLevel = atoi(projDebug); |
99 | | // Negative debug levels mean that we first start logging when errno |
100 | | // is set Cf |
101 | | // https://github.com/OSGeo/PROJ/commit/1c1d04b45d76366f54e104f9346879fd48bfde8e |
102 | | // This isn't documented for now. Not totally sure we really want |
103 | | // that... |
104 | 0 | if (debugLevel >= -PJ_LOG_TRACE) |
105 | 0 | ctx.debug_level = debugLevel; |
106 | 0 | else |
107 | 0 | ctx.debug_level = PJ_LOG_TRACE; |
108 | 0 | } else { |
109 | 0 | fprintf(stderr, "Invalid value for PROJ_DEBUG: %s\n", projDebug); |
110 | 0 | } |
111 | 0 | } |
112 | |
|
113 | 0 | return ctx; |
114 | 0 | } |
115 | | |
116 | | /**************************************************************************/ |
117 | | /* get_cpp_context() */ |
118 | | /**************************************************************************/ |
119 | | |
120 | 0 | projCppContext *pj_ctx::get_cpp_context() { |
121 | 0 | if (cpp_context == nullptr) { |
122 | 0 | cpp_context = new projCppContext(this); |
123 | 0 | } |
124 | 0 | return cpp_context; |
125 | 0 | } |
126 | | |
127 | | /************************************************************************/ |
128 | | /* set_search_paths() */ |
129 | | /************************************************************************/ |
130 | | |
131 | 0 | void pj_ctx::set_search_paths(const std::vector<std::string> &search_paths_in) { |
132 | 0 | lookupedFiles.clear(); |
133 | 0 | search_paths = search_paths_in; |
134 | 0 | delete[] c_compat_paths; |
135 | 0 | c_compat_paths = nullptr; |
136 | 0 | if (!search_paths.empty()) { |
137 | 0 | c_compat_paths = new const char *[search_paths.size()]; |
138 | 0 | for (size_t i = 0; i < search_paths.size(); ++i) { |
139 | 0 | c_compat_paths[i] = search_paths[i].c_str(); |
140 | 0 | } |
141 | 0 | } |
142 | 0 | } |
143 | | |
144 | | /**************************************************************************/ |
145 | | /* set_ca_bundle_path() */ |
146 | | /**************************************************************************/ |
147 | | |
148 | 0 | void pj_ctx::set_ca_bundle_path(const std::string &ca_bundle_path_in) { |
149 | 0 | ca_bundle_path = ca_bundle_path_in; |
150 | 0 | } |
151 | | |
152 | | /************************************************************************/ |
153 | | /* pj_ctx(const pj_ctx& other) */ |
154 | | /************************************************************************/ |
155 | | |
156 | | pj_ctx::pj_ctx(const pj_ctx &other) |
157 | 0 | : lastFullErrorMessage(std::string()), last_errno(0), |
158 | 0 | debug_level(other.debug_level), |
159 | | errorIfBestTransformationNotAvailableDefault( |
160 | 0 | other.errorIfBestTransformationNotAvailableDefault), |
161 | | warnIfBestTransformationNotAvailableDefault( |
162 | 0 | other.warnIfBestTransformationNotAvailableDefault), |
163 | 0 | logger(other.logger), logger_app_data(other.logger_app_data), |
164 | 0 | cpp_context(other.cpp_context ? other.cpp_context->clone(this) : nullptr), |
165 | 0 | use_proj4_init_rules(other.use_proj4_init_rules), |
166 | 0 | forceOver(other.forceOver), epsg_file_exists(other.epsg_file_exists), |
167 | 0 | env_var_proj_data(other.env_var_proj_data), |
168 | 0 | file_finder(other.file_finder), |
169 | 0 | file_finder_user_data(other.file_finder_user_data), |
170 | 0 | defer_grid_opening(false), |
171 | 0 | custom_sqlite3_vfs_name(other.custom_sqlite3_vfs_name), |
172 | 0 | user_writable_directory(other.user_writable_directory), |
173 | | // BEGIN ini file settings |
174 | 0 | iniFileLoaded(other.iniFileLoaded), endpoint(other.endpoint), |
175 | 0 | networking(other.networking), ca_bundle_path(other.ca_bundle_path), |
176 | 0 | native_ca(other.native_ca), gridChunkCache(other.gridChunkCache), |
177 | 0 | defaultTmercAlgo(other.defaultTmercAlgo), |
178 | | // END ini file settings |
179 | 0 | projStringParserCreateFromPROJStringRecursionCounter(0), |
180 | 0 | pipelineInitRecursiongCounter(0) { |
181 | 0 | set_search_paths(other.search_paths); |
182 | 0 | } |
183 | | |
184 | | /************************************************************************/ |
185 | | /* pj_get_default_ctx() */ |
186 | | /************************************************************************/ |
187 | | |
188 | | PJ_CONTEXT *pj_get_default_ctx() |
189 | | |
190 | 0 | { |
191 | | // C++11 rules guarantee a thread-safe instantiation. |
192 | 0 | static pj_ctx default_context(pj_ctx::createDefault()); |
193 | 0 | return &default_context; |
194 | 0 | } |
195 | | |
196 | | /************************************************************************/ |
197 | | /* ~pj_ctx() */ |
198 | | /************************************************************************/ |
199 | | |
200 | 0 | pj_ctx::~pj_ctx() { |
201 | 0 | delete[] c_compat_paths; |
202 | 0 | proj_context_delete_cpp_context(cpp_context); |
203 | 0 | } |
204 | | |
205 | | /************************************************************************/ |
206 | | /* proj_context_clone() */ |
207 | | /* Create a new context based on a custom context */ |
208 | | /************************************************************************/ |
209 | | |
210 | 0 | PJ_CONTEXT *proj_context_clone(PJ_CONTEXT *ctx) { |
211 | 0 | if (nullptr == ctx) |
212 | 0 | return proj_context_create(); |
213 | | |
214 | 0 | return new (std::nothrow) pj_ctx(*ctx); |
215 | 0 | } |
216 | | |
217 | | /*****************************************************************************/ |
218 | 0 | int proj_errno(const PJ *P) { |
219 | | /****************************************************************************** |
220 | | Read an error level from the context of a PJ. |
221 | | ******************************************************************************/ |
222 | 0 | return proj_context_errno(pj_get_ctx((PJ *)P)); |
223 | 0 | } |
224 | | |
225 | | /*****************************************************************************/ |
226 | 0 | int proj_context_errno(PJ_CONTEXT *ctx) { |
227 | | /****************************************************************************** |
228 | | Read an error directly from a context, without going through a PJ |
229 | | belonging to that context. |
230 | | ******************************************************************************/ |
231 | 0 | if (nullptr == ctx) |
232 | 0 | ctx = pj_get_default_ctx(); |
233 | 0 | return ctx->last_errno; |
234 | 0 | } |
235 | | |
236 | | /*****************************************************************************/ |
237 | 0 | int proj_errno_set(const PJ *P, int err) { |
238 | | /****************************************************************************** |
239 | | Set context-errno, bubble it up to the thread local errno, return err |
240 | | ******************************************************************************/ |
241 | | /* Use proj_errno_reset to explicitly clear the error status */ |
242 | 0 | if (0 == err) |
243 | 0 | return 0; |
244 | | |
245 | | /* For P==0 err goes to the default context */ |
246 | 0 | proj_context_errno_set(pj_get_ctx((PJ *)P), err); |
247 | 0 | errno = err; |
248 | |
|
249 | 0 | return err; |
250 | 0 | } |
251 | | |
252 | | /*****************************************************************************/ |
253 | 0 | int proj_errno_restore(const PJ *P, int err) { |
254 | | /****************************************************************************** |
255 | | Use proj_errno_restore when the current function succeeds, but the |
256 | | error flag was set on entry, and stored/reset using proj_errno_reset |
257 | | in order to monitor for new errors. |
258 | | |
259 | | See usage example under proj_errno_reset () |
260 | | ******************************************************************************/ |
261 | 0 | if (0 == err) |
262 | 0 | return 0; |
263 | 0 | proj_errno_set(P, err); |
264 | 0 | return 0; |
265 | 0 | } |
266 | | |
267 | | /*****************************************************************************/ |
268 | 0 | int proj_errno_reset(const PJ *P) { |
269 | | /****************************************************************************** |
270 | | Clears errno in the context and thread local levels |
271 | | through the low level pj_ctx interface. |
272 | | |
273 | | Returns the previous value of the errno, for convenient reset/restore |
274 | | operations: |
275 | | |
276 | | int foo (PJ *P) { |
277 | | // errno may be set on entry, but we need to reset it to be able to |
278 | | // check for errors from "do_something_with_P(P)" |
279 | | int last_errno = proj_errno_reset (P); |
280 | | |
281 | | // local failure |
282 | | if (0==P) |
283 | | return proj_errno_set (P, 42); |
284 | | |
285 | | // call to function that may fail |
286 | | do_something_with_P (P); |
287 | | |
288 | | // failure in do_something_with_P? - keep latest error status |
289 | | if (proj_errno(P)) |
290 | | return proj_errno (P); |
291 | | |
292 | | // success - restore previous error status, return 0 |
293 | | return proj_errno_restore (P, last_errno); |
294 | | } |
295 | | ******************************************************************************/ |
296 | 0 | int last_errno; |
297 | 0 | last_errno = proj_errno(P); |
298 | |
|
299 | 0 | proj_context_errno_set(pj_get_ctx((PJ *)P), 0); |
300 | 0 | errno = 0; |
301 | 0 | return last_errno; |
302 | 0 | } |
303 | | |
304 | | /* Create a new context based on the default context */ |
305 | 0 | PJ_CONTEXT *proj_context_create(void) { |
306 | 0 | return new (std::nothrow) pj_ctx(*pj_get_default_ctx()); |
307 | 0 | } |
308 | | |
309 | 0 | PJ_CONTEXT *proj_context_destroy(PJ_CONTEXT *ctx) { |
310 | 0 | if (nullptr == ctx) |
311 | 0 | return nullptr; |
312 | | |
313 | | /* Trying to free the default context is a no-op (since it is statically |
314 | | * allocated) */ |
315 | 0 | if (pj_get_default_ctx() == ctx) |
316 | 0 | return nullptr; |
317 | | |
318 | 0 | delete ctx; |
319 | 0 | return nullptr; |
320 | 0 | } |
321 | | |
322 | | /************************************************************************/ |
323 | | /* proj_context_use_proj4_init_rules() */ |
324 | | /************************************************************************/ |
325 | | |
326 | 0 | void proj_context_use_proj4_init_rules(PJ_CONTEXT *ctx, int enable) { |
327 | 0 | if (ctx == nullptr) { |
328 | 0 | ctx = pj_get_default_ctx(); |
329 | 0 | } |
330 | 0 | ctx->use_proj4_init_rules = enable; |
331 | 0 | } |
332 | | |
333 | | /************************************************************************/ |
334 | | /* EQUAL() */ |
335 | | /************************************************************************/ |
336 | | |
337 | 0 | static int EQUAL(const char *a, const char *b) { |
338 | | #ifdef _MSC_VER |
339 | | return _stricmp(a, b) == 0; |
340 | | #else |
341 | 0 | return strcasecmp(a, b) == 0; |
342 | 0 | #endif |
343 | 0 | } |
344 | | |
345 | | /************************************************************************/ |
346 | | /* proj_context_get_use_proj4_init_rules() */ |
347 | | /************************************************************************/ |
348 | | |
349 | | int proj_context_get_use_proj4_init_rules(PJ_CONTEXT *ctx, |
350 | 0 | int from_legacy_code_path) { |
351 | 0 | const char *val = getenv("PROJ_USE_PROJ4_INIT_RULES"); |
352 | |
|
353 | 0 | if (ctx == nullptr) { |
354 | 0 | ctx = pj_get_default_ctx(); |
355 | 0 | } |
356 | |
|
357 | 0 | if (val) { |
358 | 0 | if (EQUAL(val, "yes") || EQUAL(val, "on") || EQUAL(val, "true")) { |
359 | 0 | return TRUE; |
360 | 0 | } |
361 | 0 | if (EQUAL(val, "no") || EQUAL(val, "off") || EQUAL(val, "false")) { |
362 | 0 | return FALSE; |
363 | 0 | } |
364 | 0 | pj_log(ctx, PJ_LOG_ERROR, |
365 | 0 | "Invalid value for PROJ_USE_PROJ4_INIT_RULES"); |
366 | 0 | } |
367 | | |
368 | 0 | if (ctx->use_proj4_init_rules >= 0) { |
369 | 0 | return ctx->use_proj4_init_rules; |
370 | 0 | } |
371 | 0 | return from_legacy_code_path; |
372 | 0 | } |