Coverage Report

Created: 2025-08-28 06:57

/src/proj/src/internal.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 * Project:  PROJ.4
3
 * Purpose:  This is primarily material originating from pj_obs_api.c
4
 *           (now proj_4D_api.c), that does not fit into the API
5
 *           category. Hence this pile of tubings and fittings for
6
 *           PROJ.4 internal plumbing.
7
 *
8
 * Author:   Thomas Knudsen,  thokn@sdfe.dk,  2017-07-05
9
 *
10
 ******************************************************************************
11
 * Copyright (c) 2016, 2017, 2018, Thomas Knudsen/SDFE
12
 *
13
 * Permission is hereby granted, free of charge, to any person obtaining a
14
 * copy of this software and associated documentation files (the "Software"),
15
 * to deal in the Software without restriction, including without limitation
16
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17
 * and/or sell copies of the Software, and to permit persons to whom the
18
 * Software is furnished to do so, subject to the following conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included
21
 * in all copies or substantial portions of the Software.
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29
 * DEALINGS IN THE SOFTWARE.
30
 *****************************************************************************/
31
32
#define FROM_PROJ_CPP
33
34
#include <ctype.h>
35
#include <errno.h>
36
#include <math.h>
37
#include <stdarg.h>
38
#include <stddef.h>
39
#include <stdlib.h>
40
#include <string.h>
41
42
#include "geodesic.h"
43
#include "proj_internal.h"
44
45
#include "proj/internal/internal.hpp"
46
47
using namespace NS_PROJ::internal;
48
49
99.8k
enum pj_io_units pj_left(PJ *P) {
50
99.8k
    enum pj_io_units u = P->inverted ? P->right : P->left;
51
99.8k
    if (u == PJ_IO_UNITS_CLASSIC)
52
2.17k
        return PJ_IO_UNITS_PROJECTED;
53
97.6k
    return u;
54
99.8k
}
55
56
83.5k
enum pj_io_units pj_right(PJ *P) {
57
83.5k
    enum pj_io_units u = P->inverted ? P->left : P->right;
58
83.5k
    if (u == PJ_IO_UNITS_CLASSIC)
59
1.78k
        return PJ_IO_UNITS_PROJECTED;
60
81.7k
    return u;
61
83.5k
}
62
63
/* Work around non-constness of MSVC HUGE_VAL by providing functions rather than
64
 * constants */
65
8
PJ_COORD proj_coord_error(void) {
66
8
    PJ_COORD c;
67
8
    c.v[0] = c.v[1] = c.v[2] = c.v[3] = HUGE_VAL;
68
8
    return c;
69
8
}
70
71
/**************************************************************************************/
72
0
PJ_COORD pj_approx_2D_trans(PJ *P, PJ_DIRECTION direction, PJ_COORD coo) {
73
    /***************************************************************************************
74
    Behave mostly as proj_trans, but attempt to use 2D interfaces only.
75
    Used in gie.c, to enforce testing 2D code, and by PJ_pipeline.c to implement
76
    chained calls starting out with a call to its 2D interface.
77
    ***************************************************************************************/
78
0
    if (nullptr == P)
79
0
        return coo;
80
0
    if (P->inverted)
81
0
        direction = static_cast<PJ_DIRECTION>(-direction);
82
0
    switch (direction) {
83
0
    case PJ_FWD: {
84
0
        const auto xy = pj_fwd(coo.lp, P);
85
0
        coo.xy = xy;
86
0
        return coo;
87
0
    }
88
0
    case PJ_INV: {
89
0
        const auto lp = pj_inv(coo.xy, P);
90
0
        coo.lp = lp;
91
0
        return coo;
92
0
    }
93
0
    case PJ_IDENT:
94
0
        break;
95
0
    }
96
0
    return coo;
97
0
}
98
99
/**************************************************************************************/
100
0
PJ_COORD pj_approx_3D_trans(PJ *P, PJ_DIRECTION direction, PJ_COORD coo) {
101
    /***************************************************************************************
102
    Companion to pj_approx_2D_trans.
103
104
    Behave mostly as proj_trans, but attempt to use 3D interfaces only.
105
    Used in gie.c, to enforce testing 3D code, and by PJ_pipeline.c to implement
106
    chained calls starting out with a call to its 3D interface.
107
    ***************************************************************************************/
108
0
    if (nullptr == P)
109
0
        return coo;
110
0
    if (P->inverted)
111
0
        direction = static_cast<PJ_DIRECTION>(-direction);
112
0
    switch (direction) {
113
0
    case PJ_FWD: {
114
0
        const auto xyz = pj_fwd3d(coo.lpz, P);
115
0
        coo.xyz = xyz;
116
0
        return coo;
117
0
    }
118
0
    case PJ_INV: {
119
0
        const auto lpz = pj_inv3d(coo.xyz, P);
120
0
        coo.lpz = lpz;
121
0
        return coo;
122
0
    }
123
0
    case PJ_IDENT:
124
0
        break;
125
0
    }
126
0
    return coo;
127
0
}
128
129
/**************************************************************************************/
130
27.5k
int pj_has_inverse(PJ *P) {
131
    /***************************************************************************************
132
    Check if a a PJ has an inverse.
133
    ***************************************************************************************/
134
27.5k
    return ((P->inverted && (P->fwd || P->fwd3d || P->fwd4d)) ||
135
27.5k
            (P->inv || P->inv3d || P->inv4d));
136
27.5k
}
137
138
/* Move P to a new context - or to the default context if 0 is specified */
139
0
void proj_context_set(PJ *P, PJ_CONTEXT *ctx) {
140
0
    if (nullptr == ctx)
141
0
        ctx = pj_get_default_ctx();
142
0
    proj_assign_context(P, ctx);
143
0
}
144
145
0
void proj_context_inherit(PJ *parent, PJ *child) {
146
0
    if (nullptr == parent)
147
0
        proj_assign_context(child, pj_get_default_ctx());
148
0
    else
149
0
        proj_assign_context(child, pj_get_ctx(parent));
150
0
}
151
152
/*****************************************************************************/
153
305k
char *pj_chomp(char *c) {
154
    /******************************************************************************
155
    Strip pre- and postfix whitespace. Inline comments (indicated by '#') are
156
    considered whitespace.
157
    ******************************************************************************/
158
305k
    size_t i, n;
159
305k
    char *comment;
160
305k
    char *start = c;
161
162
305k
    if (nullptr == c)
163
0
        return nullptr;
164
165
305k
    comment = strchr(c, '#');
166
305k
    if (comment)
167
3.74k
        *comment = 0;
168
169
305k
    n = strlen(c);
170
305k
    if (0 == n)
171
2.77k
        return c;
172
173
    /* Eliminate postfix whitespace */
174
370k
    for (i = n - 1; (i > 0) && (isspace(c[i]) || ';' == c[i]); i--)
175
68.3k
        c[i] = 0;
176
177
    /* Find start of non-whitespace */
178
319k
    while (0 != *start && (';' == *start || isspace(*start)))
179
16.5k
        start++;
180
181
302k
    n = strlen(start);
182
302k
    if (0 == n) {
183
7
        c[0] = 0;
184
7
        return c;
185
7
    }
186
187
302k
    memmove(c, start, n + 1);
188
302k
    return c;
189
302k
}
190
191
/*****************************************************************************/
192
297k
char *pj_shrink(char *c) {
193
    /******************************************************************************
194
    Collapse repeated whitespace. Remove '+' and ';'. Make ',' and '=' greedy,
195
    consuming their surrounding whitespace.
196
    ******************************************************************************/
197
297k
    size_t i, j, n;
198
199
    /* Flag showing that a whitespace (ws) has been written after last non-ws */
200
297k
    bool ws = false;
201
202
297k
    if (nullptr == c)
203
0
        return nullptr;
204
205
297k
    pj_chomp(c);
206
297k
    n = strlen(c);
207
297k
    if (n == 0)
208
8
        return c;
209
210
    /* First collapse repeated whitespace (including +/;) */
211
297k
    i = 0;
212
297k
    bool in_string = false;
213
59.7M
    for (j = 0; j < n; j++) {
214
215
59.4M
        if (in_string) {
216
12.4M
            if (c[j] == '"' && c[j + 1] == '"') {
217
1.23M
                c[i++] = c[j];
218
1.23M
                j++;
219
11.2M
            } else if (c[j] == '"') {
220
120k
                in_string = false;
221
120k
            }
222
12.4M
            c[i++] = c[j];
223
12.4M
            continue;
224
12.4M
        }
225
226
        /* Eliminate prefix '+', only if preceded by whitespace */
227
        /* (i.e. keep it in 1.23e+08) */
228
46.9M
        if ((i > 0) && ('+' == c[j]) && ws)
229
781k
            c[j] = ' ';
230
46.9M
        if ((i == 0) && ('+' == c[j]))
231
65.8k
            c[j] = ' ';
232
233
        // Detect a string beginning after '='
234
46.9M
        if (c[j] == '"' && i > 0 && c[i - 1] == '=') {
235
124k
            in_string = true;
236
124k
            ws = false;
237
124k
            c[i++] = c[j];
238
124k
            continue;
239
124k
        }
240
241
46.7M
        if (isspace(c[j]) || ';' == c[j]) {
242
5.38M
            if (false == ws && (i > 0))
243
3.31M
                c[i++] = ' ';
244
5.38M
            ws = true;
245
5.38M
            continue;
246
41.4M
        } else {
247
41.4M
            ws = false;
248
41.4M
            c[i++] = c[j];
249
41.4M
        }
250
46.7M
    }
251
297k
    c[i] = 0;
252
297k
    n = strlen(c);
253
254
    /* Then make ',' and '=' greedy */
255
297k
    i = 0;
256
58.8M
    for (j = 0; j < n; j++) {
257
58.5M
        if (i == 0) {
258
297k
            c[i++] = c[j];
259
297k
            continue;
260
297k
        }
261
262
        /* Skip space before '='/',' */
263
58.2M
        if ('=' == c[j] || ',' == c[j]) {
264
2.80M
            if (c[i - 1] == ' ')
265
28.4k
                c[i - 1] = c[j];
266
2.78M
            else
267
2.78M
                c[i++] = c[j];
268
2.80M
            continue;
269
2.80M
        }
270
271
55.4M
        if (' ' == c[j] && ('=' == c[i - 1] || ',' == c[i - 1]))
272
58.3k
            continue;
273
274
55.4M
        c[i++] = c[j];
275
55.4M
    }
276
297k
    c[i] = 0;
277
297k
    return c;
278
297k
}
279
280
/*****************************************************************************/
281
230k
size_t pj_trim_argc(char *args) {
282
    /******************************************************************************
283
    Trim all unnecessary whitespace (and non-essential syntactic tokens) from
284
    the argument string, args, and count its number of elements.
285
    ******************************************************************************/
286
230k
    size_t i, m, n;
287
230k
    pj_shrink(args);
288
230k
    n = strlen(args);
289
230k
    if (n == 0)
290
0
        return 0;
291
230k
    bool in_string = false;
292
43.4M
    for (i = m = 0; i < n; i++) {
293
43.1M
        if (in_string) {
294
7.93M
            if (args[i] == '"' && args[i + 1] == '"') {
295
820k
                i++;
296
7.11M
            } else if (args[i] == '"') {
297
80.9k
                in_string = false;
298
80.9k
            }
299
35.2M
        } else if (args[i] == '=' && args[i + 1] == '"') {
300
84.3k
            i++;
301
84.3k
            in_string = true;
302
35.1M
        } else if (' ' == args[i]) {
303
2.74M
            args[i] = 0;
304
2.74M
            m++;
305
2.74M
        }
306
43.1M
    }
307
230k
    return m + 1;
308
230k
}
309
310
2.97M
static void unquote_string(char *param_str) {
311
312
2.97M
    size_t len = strlen(param_str);
313
    // Remove leading and terminating spaces after equal sign
314
2.97M
    const char *equal = strstr(param_str, "=\"");
315
2.97M
    if (equal && equal - param_str + 1 >= 2 && param_str[len - 1] == '"') {
316
81.2k
        size_t dst = equal + 1 - param_str;
317
81.2k
        size_t src = dst + 1;
318
7.10M
        for (; param_str[src]; dst++, src++) {
319
7.10M
            if (param_str[src] == '"') {
320
502k
                if (param_str[src + 1] == '"') {
321
422k
                    src++;
322
422k
                } else {
323
80.5k
                    break;
324
80.5k
                }
325
502k
            }
326
7.02M
            param_str[dst] = param_str[src];
327
7.02M
        }
328
81.2k
        param_str[dst] = '\0';
329
81.2k
    }
330
2.97M
}
331
332
/*****************************************************************************/
333
230k
char **pj_trim_argv(size_t argc, char *args) {
334
    /******************************************************************************
335
    Create an argv-style array from elements placed in the argument string,
336
    args.
337
338
    args is a trimmed string as returned by pj_trim_argc(), and argc is the
339
    number of trimmed strings found (i.e. the return value of pj_trim_args()).
340
    Hence, int argc    = pj_trim_argc (args); char **argv = pj_trim_argv (argc,
341
    args); will produce a classic style (argc, argv) pair from a string of
342
    whitespace separated args. No new memory is allocated for storing the
343
    individual args (they stay in the args string), but for the pointers to the
344
    args a new array is allocated and returned.
345
346
    It is the duty of the caller to free this array.
347
    ******************************************************************************/
348
349
230k
    if (nullptr == args)
350
0
        return nullptr;
351
230k
    if (0 == argc)
352
0
        return nullptr;
353
354
    /* turn the input string into an array of strings */
355
230k
    char **argv = (char **)calloc(argc, sizeof(char *));
356
230k
    if (nullptr == argv)
357
0
        return nullptr;
358
3.20M
    for (size_t i = 0, j = 0; j < argc; j++) {
359
2.97M
        argv[j] = args + i;
360
2.97M
        char *str = argv[j];
361
2.97M
        size_t nLen = strlen(str);
362
2.97M
        i += nLen + 1;
363
2.97M
        unquote_string(str);
364
2.97M
    }
365
230k
    return argv;
366
230k
}
367
368
/*****************************************************************************/
369
518k
std::string pj_double_quote_string_param_if_needed(const std::string &str) {
370
    /*****************************************************************************/
371
518k
    if (str.find(' ') == std::string::npos) {
372
464k
        return str;
373
464k
    }
374
53.6k
    std::string ret;
375
53.6k
    ret += '"';
376
53.6k
    ret += replaceAll(str, "\"", "\"\"");
377
53.6k
    ret += '"';
378
53.6k
    return ret;
379
518k
}
380
381
/*****************************************************************************/
382
66.9k
char *pj_make_args(size_t argc, char **argv) {
383
    /******************************************************************************
384
    pj_make_args is the inverse of the pj_trim_argc/pj_trim_argv combo: It
385
    converts free format command line input to something proj_create can
386
    consume.
387
388
    Allocates, and returns, an array of char, large enough to hold a whitespace
389
    separated copy of the args in argv. It is the duty of the caller to free
390
    this array.
391
    ******************************************************************************/
392
66.9k
    try {
393
66.9k
        std::string s;
394
350k
        for (size_t i = 0; i < argc; i++) {
395
283k
            const char *equal = strchr(argv[i], '=');
396
283k
            if (equal) {
397
197k
                s += std::string(argv[i], equal - argv[i] + 1);
398
197k
                s += pj_double_quote_string_param_if_needed(equal + 1);
399
197k
            } else {
400
86.2k
                s += argv[i];
401
86.2k
            }
402
283k
            s += ' ';
403
283k
        }
404
405
66.9k
        char *p = pj_strdup(s.c_str());
406
66.9k
        return pj_shrink(p);
407
66.9k
    } catch (const std::exception &) {
408
0
        return nullptr;
409
0
    }
410
66.9k
}
411
412
/*****************************************************************************/
413
901k
void proj_context_errno_set(PJ_CONTEXT *ctx, int err) {
414
    /******************************************************************************
415
    Raise an error directly on a context, without going through a PJ belonging
416
    to that context.
417
    ******************************************************************************/
418
901k
    if (nullptr == ctx)
419
0
        ctx = pj_get_default_ctx();
420
901k
    ctx->last_errno = err;
421
901k
    if (err == 0)
422
698k
        return;
423
202k
    errno = err;
424
202k
}