Coverage Report

Created: 2026-06-30 08:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/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
4.96M
enum pj_io_units pj_left(PJ *P) {
50
4.96M
    enum pj_io_units u = P->inverted ? P->right : P->left;
51
4.96M
    if (u == PJ_IO_UNITS_CLASSIC)
52
139k
        return PJ_IO_UNITS_PROJECTED;
53
4.82M
    return u;
54
4.96M
}
55
56
2.63M
enum pj_io_units pj_right(PJ *P) {
57
2.63M
    enum pj_io_units u = P->inverted ? P->left : P->right;
58
2.63M
    if (u == PJ_IO_UNITS_CLASSIC)
59
134k
        return PJ_IO_UNITS_PROJECTED;
60
2.49M
    return u;
61
2.63M
}
62
63
/* Work around non-constness of MSVC HUGE_VAL by providing functions rather than
64
 * constants */
65
54.9k
PJ_COORD proj_coord_error(void) {
66
54.9k
    PJ_COORD c;
67
54.9k
    c.v[0] = c.v[1] = c.v[2] = c.v[3] = HUGE_VAL;
68
54.9k
    return c;
69
54.9k
}
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
1.63M
int pj_has_inverse(PJ *P) {
131
    /***************************************************************************************
132
    Check if a a PJ has an inverse.
133
    ***************************************************************************************/
134
1.63M
    return ((P->inverted && (P->fwd || P->fwd3d || P->fwd4d)) ||
135
1.36M
            (P->inv || P->inv3d || P->inv4d));
136
1.63M
}
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
4.13M
char *pj_chomp(char *c) {
154
    /******************************************************************************
155
    Strip pre- and postfix whitespace. Inline comments (indicated by '#') are
156
    considered whitespace.
157
    ******************************************************************************/
158
4.13M
    size_t i, n;
159
4.13M
    char *comment;
160
4.13M
    char *start = c;
161
162
4.13M
    if (nullptr == c)
163
0
        return nullptr;
164
165
4.13M
    comment = strchr(c, '#');
166
4.13M
    if (comment)
167
144k
        *comment = 0;
168
169
4.13M
    n = strlen(c);
170
4.13M
    if (0 == n)
171
43.3k
        return c;
172
173
    /* Eliminate postfix whitespace */
174
4.58M
    for (i = n - 1; (i > 0) && (isspace(c[i]) || ';' == c[i]); i--)
175
490k
        c[i] = 0;
176
177
    /* Find start of non-whitespace */
178
4.29M
    while (0 != *start && (';' == *start || isspace(*start)))
179
207k
        start++;
180
181
4.09M
    n = strlen(start);
182
4.09M
    if (0 == n) {
183
454
        c[0] = 0;
184
454
        return c;
185
454
    }
186
187
4.09M
    memmove(c, start, n + 1);
188
4.09M
    return c;
189
4.09M
}
190
191
/*****************************************************************************/
192
3.93M
char *pj_shrink(char *c) {
193
    /******************************************************************************
194
    Collapse repeated whitespace. Remove '+' and ';'. Make ',' and '=' greedy,
195
    consuming their surrounding whitespace.
196
    ******************************************************************************/
197
3.93M
    size_t i, j, n;
198
199
    /* Flag showing that a whitespace (ws) has been written after last non-ws */
200
3.93M
    bool ws = false;
201
202
3.93M
    if (nullptr == c)
203
0
        return nullptr;
204
205
3.93M
    pj_chomp(c);
206
3.93M
    n = strlen(c);
207
3.93M
    if (n == 0)
208
12.8k
        return c;
209
210
    /* First collapse repeated whitespace (including +/;) */
211
3.92M
    i = 0;
212
3.92M
    bool in_string = false;
213
8.71G
    for (j = 0; j < n; j++) {
214
215
8.71G
        if (in_string) {
216
1.29G
            if (c[j] == '"' && c[j + 1] == '"') {
217
5.42M
                c[i++] = c[j];
218
5.42M
                j++;
219
1.29G
            } else if (c[j] == '"') {
220
1.95M
                in_string = false;
221
1.95M
            }
222
1.29G
            c[i++] = c[j];
223
1.29G
            continue;
224
1.29G
        }
225
226
        /* Eliminate prefix '+', only if preceded by whitespace */
227
        /* (i.e. keep it in 1.23e+08) */
228
7.41G
        if ((i > 0) && ('+' == c[j]) && ws)
229
112M
            c[j] = ' ';
230
7.41G
        if ((i == 0) && ('+' == c[j]))
231
1.51M
            c[j] = ' ';
232
233
        // Detect a string beginning after '='
234
7.41G
        if (c[j] == '"' && i > 0 && c[i - 1] == '=') {
235
2.10M
            in_string = true;
236
2.10M
            ws = false;
237
2.10M
            c[i++] = c[j];
238
2.10M
            continue;
239
2.10M
        }
240
241
7.41G
        if (isspace(c[j]) || ';' == c[j]) {
242
1.28G
            if (false == ws && (i > 0))
243
291M
                c[i++] = ' ';
244
1.28G
            ws = true;
245
1.28G
            continue;
246
6.13G
        } else {
247
6.13G
            ws = false;
248
6.13G
            c[i++] = c[j];
249
6.13G
        }
250
7.41G
    }
251
3.92M
    c[i] = 0;
252
3.92M
    n = strlen(c);
253
254
    /* Then make ',' and '=' greedy */
255
3.92M
    i = 0;
256
7.73G
    for (j = 0; j < n; j++) {
257
7.72G
        if (i == 0) {
258
3.92M
            c[i++] = c[j];
259
3.92M
            continue;
260
3.92M
        }
261
262
        /* Skip space before '='/',' */
263
7.72G
        if ('=' == c[j] || ',' == c[j]) {
264
447M
            if (c[i - 1] == ' ')
265
5.67M
                c[i - 1] = c[j];
266
441M
            else
267
441M
                c[i++] = c[j];
268
447M
            continue;
269
447M
        }
270
271
7.27G
        if (' ' == c[j] && ('=' == c[i - 1] || ',' == c[i - 1]))
272
14.0M
            continue;
273
274
7.26G
        c[i++] = c[j];
275
7.26G
    }
276
3.92M
    c[i] = 0;
277
3.92M
    return c;
278
3.93M
}
279
280
/*****************************************************************************/
281
3.93M
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
3.93M
    size_t i, m, n;
287
3.93M
    pj_shrink(args);
288
3.93M
    n = strlen(args);
289
3.93M
    if (n == 0)
290
13.4k
        return 0;
291
3.92M
    bool in_string = false;
292
7.70G
    for (i = m = 0; i < n; i++) {
293
7.70G
        if (in_string) {
294
1.25G
            if (args[i] == '"' && args[i + 1] == '"') {
295
4.72M
                i++;
296
1.25G
            } else if (args[i] == '"') {
297
2.06M
                in_string = false;
298
2.06M
            }
299
6.44G
        } else if (args[i] == '=' && args[i + 1] == '"') {
300
2.21M
            i++;
301
2.21M
            in_string = true;
302
6.44G
        } else if (' ' == args[i]) {
303
371M
            args[i] = 0;
304
371M
            m++;
305
371M
        }
306
7.70G
    }
307
3.92M
    return m + 1;
308
3.93M
}
309
310
375M
static void unquote_string(char *param_str) {
311
312
375M
    size_t len = strlen(param_str);
313
    // Remove leading and terminating spaces after equal sign
314
375M
    const char *equal = strstr(param_str, "=\"");
315
375M
    if (equal && equal - param_str + 1 >= 2 && param_str[len - 1] == '"') {
316
660k
        size_t dst = equal + 1 - param_str;
317
660k
        size_t src = dst + 1;
318
510M
        for (; param_str[src]; dst++, src++) {
319
510M
            if (param_str[src] == '"') {
320
2.51M
                if (param_str[src + 1] == '"') {
321
1.87M
                    src++;
322
1.87M
                } else {
323
640k
                    break;
324
640k
                }
325
2.51M
            }
326
509M
            param_str[dst] = param_str[src];
327
509M
        }
328
660k
        param_str[dst] = '\0';
329
660k
    }
330
375M
}
331
332
/*****************************************************************************/
333
3.93M
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
3.93M
    if (nullptr == args)
350
0
        return nullptr;
351
3.93M
    if (0 == argc)
352
12.7k
        return nullptr;
353
354
    /* turn the input string into an array of strings */
355
3.92M
    char **argv = (char **)calloc(argc, sizeof(char *));
356
3.92M
    if (nullptr == argv)
357
0
        return nullptr;
358
379M
    for (size_t i = 0, j = 0; j < argc; j++) {
359
375M
        argv[j] = args + i;
360
375M
        char *str = argv[j];
361
375M
        size_t nLen = strlen(str);
362
375M
        i += nLen + 1;
363
375M
        unquote_string(str);
364
375M
    }
365
3.92M
    return argv;
366
3.92M
}
367
368
/*****************************************************************************/
369
18.1M
std::string pj_double_quote_string_param_if_needed(const std::string &str) {
370
    /*****************************************************************************/
371
18.1M
    if (str.find(' ') == std::string::npos) {
372
17.6M
        return str;
373
17.6M
    }
374
444k
    std::string ret;
375
444k
    ret += '"';
376
444k
    ret += replaceAll(str, "\"", "\"\"");
377
444k
    ret += '"';
378
444k
    return ret;
379
18.1M
}
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
46.9M
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
46.9M
    if (nullptr == ctx)
419
0
        ctx = pj_get_default_ctx();
420
46.9M
    ctx->last_errno = err;
421
46.9M
    if (err == 0)
422
26.9M
        return;
423
46.9M
    errno = err;
424
19.9M
}