Coverage Report

Created: 2025-10-13 06:59

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
158k
enum pj_io_units pj_left(PJ *P) {
50
158k
    enum pj_io_units u = P->inverted ? P->right : P->left;
51
158k
    if (u == PJ_IO_UNITS_CLASSIC)
52
5.60k
        return PJ_IO_UNITS_PROJECTED;
53
153k
    return u;
54
158k
}
55
56
85.2k
enum pj_io_units pj_right(PJ *P) {
57
85.2k
    enum pj_io_units u = P->inverted ? P->left : P->right;
58
85.2k
    if (u == PJ_IO_UNITS_CLASSIC)
59
5.27k
        return PJ_IO_UNITS_PROJECTED;
60
79.9k
    return u;
61
85.2k
}
62
63
/* Work around non-constness of MSVC HUGE_VAL by providing functions rather than
64
 * constants */
65
627k
PJ_COORD proj_coord_error(void) {
66
627k
    PJ_COORD c;
67
627k
    c.v[0] = c.v[1] = c.v[2] = c.v[3] = HUGE_VAL;
68
627k
    return c;
69
627k
}
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
56.8k
int pj_has_inverse(PJ *P) {
131
    /***************************************************************************************
132
    Check if a a PJ has an inverse.
133
    ***************************************************************************************/
134
56.8k
    return ((P->inverted && (P->fwd || P->fwd3d || P->fwd4d)) ||
135
39.6k
            (P->inv || P->inv3d || P->inv4d));
136
56.8k
}
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
433k
char *pj_chomp(char *c) {
154
    /******************************************************************************
155
    Strip pre- and postfix whitespace. Inline comments (indicated by '#') are
156
    considered whitespace.
157
    ******************************************************************************/
158
433k
    size_t i, n;
159
433k
    char *comment;
160
433k
    char *start = c;
161
162
433k
    if (nullptr == c)
163
0
        return nullptr;
164
165
433k
    comment = strchr(c, '#');
166
433k
    if (comment)
167
8.34k
        *comment = 0;
168
169
433k
    n = strlen(c);
170
433k
    if (0 == n)
171
17.7k
        return c;
172
173
    /* Eliminate postfix whitespace */
174
419k
    for (i = n - 1; (i > 0) && (isspace(c[i]) || ';' == c[i]); i--)
175
3.19k
        c[i] = 0;
176
177
    /* Find start of non-whitespace */
178
417k
    while (0 != *start && (';' == *start || isspace(*start)))
179
1.66k
        start++;
180
181
415k
    n = strlen(start);
182
415k
    if (0 == n) {
183
2
        c[0] = 0;
184
2
        return c;
185
2
    }
186
187
415k
    memmove(c, start, n + 1);
188
415k
    return c;
189
415k
}
190
191
/*****************************************************************************/
192
425k
char *pj_shrink(char *c) {
193
    /******************************************************************************
194
    Collapse repeated whitespace. Remove '+' and ';'. Make ',' and '=' greedy,
195
    consuming their surrounding whitespace.
196
    ******************************************************************************/
197
425k
    size_t i, j, n;
198
199
    /* Flag showing that a whitespace (ws) has been written after last non-ws */
200
425k
    bool ws = false;
201
202
425k
    if (nullptr == c)
203
0
        return nullptr;
204
205
425k
    pj_chomp(c);
206
425k
    n = strlen(c);
207
425k
    if (n == 0)
208
10.3k
        return c;
209
210
    /* First collapse repeated whitespace (including +/;) */
211
414k
    i = 0;
212
414k
    bool in_string = false;
213
130M
    for (j = 0; j < n; j++) {
214
215
129M
        if (in_string) {
216
14.0M
            if (c[j] == '"' && c[j + 1] == '"') {
217
192k
                c[i++] = c[j];
218
192k
                j++;
219
13.8M
            } else if (c[j] == '"') {
220
197k
                in_string = false;
221
197k
            }
222
14.0M
            c[i++] = c[j];
223
14.0M
            continue;
224
14.0M
        }
225
226
        /* Eliminate prefix '+', only if preceded by whitespace */
227
        /* (i.e. keep it in 1.23e+08) */
228
115M
        if ((i > 0) && ('+' == c[j]) && ws)
229
8.26M
            c[j] = ' ';
230
115M
        if ((i == 0) && ('+' == c[j]))
231
287k
            c[j] = ' ';
232
233
        // Detect a string beginning after '='
234
115M
        if (c[j] == '"' && i > 0 && c[i - 1] == '=') {
235
221k
            in_string = true;
236
221k
            ws = false;
237
221k
            c[i++] = c[j];
238
221k
            continue;
239
221k
        }
240
241
115M
        if (isspace(c[j]) || ';' == c[j]) {
242
20.7M
            if (false == ws && (i > 0))
243
9.26M
                c[i++] = ' ';
244
20.7M
            ws = true;
245
20.7M
            continue;
246
94.9M
        } else {
247
94.9M
            ws = false;
248
94.9M
            c[i++] = c[j];
249
94.9M
        }
250
115M
    }
251
414k
    c[i] = 0;
252
414k
    n = strlen(c);
253
254
    /* Then make ',' and '=' greedy */
255
414k
    i = 0;
256
119M
    for (j = 0; j < n; j++) {
257
118M
        if (i == 0) {
258
414k
            c[i++] = c[j];
259
414k
            continue;
260
414k
        }
261
262
        /* Skip space before '='/',' */
263
118M
        if ('=' == c[j] || ',' == c[j]) {
264
7.45M
            if (c[i - 1] == ' ')
265
40.5k
                c[i - 1] = c[j];
266
7.41M
            else
267
7.41M
                c[i++] = c[j];
268
7.45M
            continue;
269
7.45M
        }
270
271
110M
        if (' ' == c[j] && ('=' == c[i - 1] || ',' == c[i - 1]))
272
127k
            continue;
273
274
110M
        c[i++] = c[j];
275
110M
    }
276
414k
    c[i] = 0;
277
414k
    return c;
278
425k
}
279
280
/*****************************************************************************/
281
424k
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
424k
    size_t i, m, n;
287
424k
    pj_shrink(args);
288
424k
    n = strlen(args);
289
424k
    if (n == 0)
290
10.3k
        return 0;
291
414k
    bool in_string = false;
292
118M
    for (i = m = 0; i < n; i++) {
293
118M
        if (in_string) {
294
8.73M
            if (args[i] == '"' && args[i + 1] == '"') {
295
187k
                i++;
296
8.55M
            } else if (args[i] == '"') {
297
215k
                in_string = false;
298
215k
            }
299
109M
        } else if (args[i] == '=' && args[i + 1] == '"') {
300
223k
            i++;
301
223k
            in_string = true;
302
109M
        } else if (' ' == args[i]) {
303
9.86M
            args[i] = 0;
304
9.86M
            m++;
305
9.86M
        }
306
118M
    }
307
414k
    return m + 1;
308
424k
}
309
310
10.2M
static void unquote_string(char *param_str) {
311
312
10.2M
    size_t len = strlen(param_str);
313
    // Remove leading and terminating spaces after equal sign
314
10.2M
    const char *equal = strstr(param_str, "=\"");
315
10.2M
    if (equal && equal - param_str + 1 >= 2 && param_str[len - 1] == '"') {
316
103k
        size_t dst = equal + 1 - param_str;
317
103k
        size_t src = dst + 1;
318
5.38M
        for (; param_str[src]; dst++, src++) {
319
5.38M
            if (param_str[src] == '"') {
320
272k
                if (param_str[src + 1] == '"') {
321
169k
                    src++;
322
169k
                } else {
323
102k
                    break;
324
102k
                }
325
272k
            }
326
5.28M
            param_str[dst] = param_str[src];
327
5.28M
        }
328
103k
        param_str[dst] = '\0';
329
103k
    }
330
10.2M
}
331
332
/*****************************************************************************/
333
424k
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
424k
    if (nullptr == args)
350
0
        return nullptr;
351
424k
    if (0 == argc)
352
10.3k
        return nullptr;
353
354
    /* turn the input string into an array of strings */
355
414k
    char **argv = (char **)calloc(argc, sizeof(char *));
356
414k
    if (nullptr == argv)
357
0
        return nullptr;
358
10.6M
    for (size_t i = 0, j = 0; j < argc; j++) {
359
10.2M
        argv[j] = args + i;
360
10.2M
        char *str = argv[j];
361
10.2M
        size_t nLen = strlen(str);
362
10.2M
        i += nLen + 1;
363
10.2M
        unquote_string(str);
364
10.2M
    }
365
414k
    return argv;
366
414k
}
367
368
/*****************************************************************************/
369
3.12M
std::string pj_double_quote_string_param_if_needed(const std::string &str) {
370
    /*****************************************************************************/
371
3.12M
    if (str.find(' ') == std::string::npos) {
372
3.06M
        return str;
373
3.06M
    }
374
61.2k
    std::string ret;
375
61.2k
    ret += '"';
376
61.2k
    ret += replaceAll(str, "\"", "\"\"");
377
61.2k
    ret += '"';
378
61.2k
    return ret;
379
3.12M
}
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
2.02M
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
2.02M
    if (nullptr == ctx)
419
0
        ctx = pj_get_default_ctx();
420
2.02M
    ctx->last_errno = err;
421
2.02M
    if (err == 0)
422
1.08M
        return;
423
2.02M
    errno = err;
424
946k
}