Coverage Report

Created: 2025-11-09 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PROJ/src/internal.cpp
Line
Count
Source
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
135k
enum pj_io_units pj_left(PJ *P) {
50
135k
    enum pj_io_units u = P->inverted ? P->right : P->left;
51
135k
    if (u == PJ_IO_UNITS_CLASSIC)
52
4.00k
        return PJ_IO_UNITS_PROJECTED;
53
131k
    return u;
54
135k
}
55
56
74.1k
enum pj_io_units pj_right(PJ *P) {
57
74.1k
    enum pj_io_units u = P->inverted ? P->left : P->right;
58
74.1k
    if (u == PJ_IO_UNITS_CLASSIC)
59
3.83k
        return PJ_IO_UNITS_PROJECTED;
60
70.3k
    return u;
61
74.1k
}
62
63
/* Work around non-constness of MSVC HUGE_VAL by providing functions rather than
64
 * constants */
65
533k
PJ_COORD proj_coord_error(void) {
66
533k
    PJ_COORD c;
67
533k
    c.v[0] = c.v[1] = c.v[2] = c.v[3] = HUGE_VAL;
68
533k
    return c;
69
533k
}
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
47.5k
int pj_has_inverse(PJ *P) {
131
    /***************************************************************************************
132
    Check if a a PJ has an inverse.
133
    ***************************************************************************************/
134
47.5k
    return ((P->inverted && (P->fwd || P->fwd3d || P->fwd4d)) ||
135
33.1k
            (P->inv || P->inv3d || P->inv4d));
136
47.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
360k
char *pj_chomp(char *c) {
154
    /******************************************************************************
155
    Strip pre- and postfix whitespace. Inline comments (indicated by '#') are
156
    considered whitespace.
157
    ******************************************************************************/
158
360k
    size_t i, n;
159
360k
    char *comment;
160
360k
    char *start = c;
161
162
360k
    if (nullptr == c)
163
0
        return nullptr;
164
165
360k
    comment = strchr(c, '#');
166
360k
    if (comment)
167
6.42k
        *comment = 0;
168
169
360k
    n = strlen(c);
170
360k
    if (0 == n)
171
12.9k
        return c;
172
173
    /* Eliminate postfix whitespace */
174
350k
    for (i = n - 1; (i > 0) && (isspace(c[i]) || ';' == c[i]); i--)
175
2.56k
        c[i] = 0;
176
177
    /* Find start of non-whitespace */
178
348k
    while (0 != *start && (';' == *start || isspace(*start)))
179
1.19k
        start++;
180
181
347k
    n = strlen(start);
182
347k
    if (0 == n) {
183
2
        c[0] = 0;
184
2
        return c;
185
2
    }
186
187
347k
    memmove(c, start, n + 1);
188
347k
    return c;
189
347k
}
190
191
/*****************************************************************************/
192
353k
char *pj_shrink(char *c) {
193
    /******************************************************************************
194
    Collapse repeated whitespace. Remove '+' and ';'. Make ',' and '=' greedy,
195
    consuming their surrounding whitespace.
196
    ******************************************************************************/
197
353k
    size_t i, j, n;
198
199
    /* Flag showing that a whitespace (ws) has been written after last non-ws */
200
353k
    bool ws = false;
201
202
353k
    if (nullptr == c)
203
0
        return nullptr;
204
205
353k
    pj_chomp(c);
206
353k
    n = strlen(c);
207
353k
    if (n == 0)
208
7.00k
        return c;
209
210
    /* First collapse repeated whitespace (including +/;) */
211
346k
    i = 0;
212
346k
    bool in_string = false;
213
105M
    for (j = 0; j < n; j++) {
214
215
104M
        if (in_string) {
216
9.94M
            if (c[j] == '"' && c[j + 1] == '"') {
217
123k
                c[i++] = c[j];
218
123k
                j++;
219
9.82M
            } else if (c[j] == '"') {
220
141k
                in_string = false;
221
141k
            }
222
9.94M
            c[i++] = c[j];
223
9.94M
            continue;
224
9.94M
        }
225
226
        /* Eliminate prefix '+', only if preceded by whitespace */
227
        /* (i.e. keep it in 1.23e+08) */
228
94.9M
        if ((i > 0) && ('+' == c[j]) && ws)
229
6.94M
            c[j] = ' ';
230
94.9M
        if ((i == 0) && ('+' == c[j]))
231
231k
            c[j] = ' ';
232
233
        // Detect a string beginning after '='
234
94.9M
        if (c[j] == '"' && i > 0 && c[i - 1] == '=') {
235
159k
            in_string = true;
236
159k
            ws = false;
237
159k
            c[i++] = c[j];
238
159k
            continue;
239
159k
        }
240
241
94.7M
        if (isspace(c[j]) || ';' == c[j]) {
242
17.3M
            if (false == ws && (i > 0))
243
7.63M
                c[i++] = ' ';
244
17.3M
            ws = true;
245
17.3M
            continue;
246
77.4M
        } else {
247
77.4M
            ws = false;
248
77.4M
            c[i++] = c[j];
249
77.4M
        }
250
94.7M
    }
251
346k
    c[i] = 0;
252
346k
    n = strlen(c);
253
254
    /* Then make ',' and '=' greedy */
255
346k
    i = 0;
256
95.6M
    for (j = 0; j < n; j++) {
257
95.2M
        if (i == 0) {
258
346k
            c[i++] = c[j];
259
346k
            continue;
260
346k
        }
261
262
        /* Skip space before '='/',' */
263
94.9M
        if ('=' == c[j] || ',' == c[j]) {
264
6.09M
            if (c[i - 1] == ' ')
265
30.7k
                c[i - 1] = c[j];
266
6.05M
            else
267
6.05M
                c[i++] = c[j];
268
6.09M
            continue;
269
6.09M
        }
270
271
88.8M
        if (' ' == c[j] && ('=' == c[i - 1] || ',' == c[i - 1]))
272
105k
            continue;
273
274
88.7M
        c[i++] = c[j];
275
88.7M
    }
276
346k
    c[i] = 0;
277
346k
    return c;
278
353k
}
279
280
/*****************************************************************************/
281
353k
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
353k
    size_t i, m, n;
287
353k
    pj_shrink(args);
288
353k
    n = strlen(args);
289
353k
    if (n == 0)
290
7.00k
        return 0;
291
346k
    bool in_string = false;
292
95.1M
    for (i = m = 0; i < n; i++) {
293
94.8M
        if (in_string) {
294
6.19M
            if (args[i] == '"' && args[i + 1] == '"') {
295
120k
                i++;
296
6.07M
            } else if (args[i] == '"') {
297
156k
                in_string = false;
298
156k
            }
299
88.6M
        } else if (args[i] == '=' && args[i + 1] == '"') {
300
161k
            i++;
301
161k
            in_string = true;
302
88.4M
        } else if (' ' == args[i]) {
303
8.14M
            args[i] = 0;
304
8.14M
            m++;
305
8.14M
        }
306
94.8M
    }
307
346k
    return m + 1;
308
353k
}
309
310
8.49M
static void unquote_string(char *param_str) {
311
312
8.49M
    size_t len = strlen(param_str);
313
    // Remove leading and terminating spaces after equal sign
314
8.49M
    const char *equal = strstr(param_str, "=\"");
315
8.49M
    if (equal && equal - param_str + 1 >= 2 && param_str[len - 1] == '"') {
316
68.7k
        size_t dst = equal + 1 - param_str;
317
68.7k
        size_t src = dst + 1;
318
3.70M
        for (; param_str[src]; dst++, src++) {
319
3.70M
            if (param_str[src] == '"') {
320
176k
                if (param_str[src + 1] == '"') {
321
107k
                    src++;
322
107k
                } else {
323
68.5k
                    break;
324
68.5k
                }
325
176k
            }
326
3.63M
            param_str[dst] = param_str[src];
327
3.63M
        }
328
68.7k
        param_str[dst] = '\0';
329
68.7k
    }
330
8.49M
}
331
332
/*****************************************************************************/
333
353k
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
353k
    if (nullptr == args)
350
0
        return nullptr;
351
353k
    if (0 == argc)
352
7.00k
        return nullptr;
353
354
    /* turn the input string into an array of strings */
355
346k
    char **argv = (char **)calloc(argc, sizeof(char *));
356
346k
    if (nullptr == argv)
357
0
        return nullptr;
358
8.83M
    for (size_t i = 0, j = 0; j < argc; j++) {
359
8.49M
        argv[j] = args + i;
360
8.49M
        char *str = argv[j];
361
8.49M
        size_t nLen = strlen(str);
362
8.49M
        i += nLen + 1;
363
8.49M
        unquote_string(str);
364
8.49M
    }
365
346k
    return argv;
366
346k
}
367
368
/*****************************************************************************/
369
2.67M
std::string pj_double_quote_string_param_if_needed(const std::string &str) {
370
    /*****************************************************************************/
371
2.67M
    if (str.find(' ') == std::string::npos) {
372
2.63M
        return str;
373
2.63M
    }
374
39.4k
    std::string ret;
375
39.4k
    ret += '"';
376
39.4k
    ret += replaceAll(str, "\"", "\"\"");
377
39.4k
    ret += '"';
378
39.4k
    return ret;
379
2.67M
}
380
381
/*****************************************************************************/
382
0
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
0
    try {
393
0
        std::string s;
394
0
        for (size_t i = 0; i < argc; i++) {
395
0
            const char *equal = strchr(argv[i], '=');
396
0
            if (equal) {
397
0
                s += std::string(argv[i], equal - argv[i] + 1);
398
0
                s += pj_double_quote_string_param_if_needed(equal + 1);
399
0
            } else {
400
0
                s += argv[i];
401
0
            }
402
0
            s += ' ';
403
0
        }
404
405
0
        char *p = pj_strdup(s.c_str());
406
0
        return pj_shrink(p);
407
0
    } catch (const std::exception &) {
408
0
        return nullptr;
409
0
    }
410
0
}
411
412
/*****************************************************************************/
413
1.82M
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
1.82M
    if (nullptr == ctx)
419
0
        ctx = pj_get_default_ctx();
420
1.82M
    ctx->last_errno = err;
421
1.82M
    if (err == 0)
422
997k
        return;
423
1.82M
    errno = err;
424
824k
}