Coverage Report

Created: 2025-06-13 06:18

/src/proj/src/ctx.cpp
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
}