Coverage Report

Created: 2026-10-28 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/proj/src/param.cpp
Line
Count
Source
1
/* put parameters in linked list and retrieve */
2
3
#include <ctime>
4
#include <ctype.h>
5
#include <stddef.h>
6
#include <stdio.h>
7
#include <stdlib.h>
8
#include <string.h>
9
10
#include "proj.h"
11
#include "proj_internal.h"
12
13
/* create parameter list entry */
14
2.30M
paralist *pj_mkparam(const char *str) {
15
2.30M
    paralist *newitem;
16
17
2.30M
    if ((newitem = (paralist *)malloc(sizeof(paralist) + strlen(str))) !=
18
2.30M
        nullptr) {
19
2.30M
        newitem->used = 0;
20
2.30M
        newitem->next = nullptr;
21
2.30M
        if (*str == '+')
22
432
            ++str;
23
2.30M
        (void)strcpy(newitem->param, str);
24
2.30M
    }
25
2.30M
    return newitem;
26
2.30M
}
27
28
/* As pj_mkparam, but payload ends at first whitespace, rather than at end of
29
 * <str> */
30
3.18k
paralist *pj_mkparam_ws(const char *str, const char **next_str) {
31
3.18k
    paralist *newitem;
32
3.18k
    size_t len = 0;
33
34
3.18k
    if (nullptr == str)
35
0
        return nullptr;
36
37
    /* Find start and length of string */
38
6.01k
    while (isspace(*str))
39
2.82k
        str++;
40
3.18k
    if (*str == '+')
41
3.18k
        str++;
42
3.18k
    bool in_string = false;
43
30.0k
    for (; str[len] != '\0'; len++) {
44
29.6k
        if (in_string) {
45
0
            if (str[len] == '"' && str[len + 1] == '"') {
46
0
                len++;
47
0
            } else if (str[len] == '"') {
48
0
                in_string = false;
49
0
            }
50
29.6k
        } else if (str[len] == '=' && str[len + 1] == '"') {
51
0
            in_string = true;
52
29.6k
        } else if (isspace(str[len])) {
53
2.82k
            break;
54
2.82k
        }
55
29.6k
    }
56
57
3.18k
    if (next_str)
58
3.18k
        *next_str = str + len;
59
60
    /* Use calloc to automagically 0-terminate the copy */
61
3.18k
    newitem = (paralist *)calloc(1, sizeof(paralist) + len + 1);
62
3.18k
    if (nullptr == newitem)
63
0
        return nullptr;
64
3.18k
    memcpy(newitem->param, str, len);
65
66
3.18k
    newitem->used = 0;
67
3.18k
    newitem->next = nullptr;
68
69
3.18k
    return newitem;
70
3.18k
}
71
72
/**************************************************************************************/
73
6.22M
paralist *pj_param_exists(paralist *list, const char *parameter) {
74
    /***************************************************************************************
75
        Determine whether a given parameter exists in a paralist. If it does,
76
    return a pointer to the corresponding list element - otherwise return 0.
77
78
        In support of the pipeline syntax, the search is terminated once a
79
    "+step" list element is reached, in which case a 0 is returned, unless the
80
    parameter searched for is actually "step", in which case a pointer to the
81
    "step" list element is returned.
82
83
        This function is equivalent to the pj_param (...) call with the "opt"
84
    argument set to the parameter name preceeeded by a 't'. But by using this
85
    one, one avoids writing the code allocating memory for a new copy of
86
    parameter name, and prepending the t (for compile time known names, this is
87
    obviously not an issue).
88
    ***************************************************************************************/
89
6.22M
    paralist *next = list;
90
6.22M
    const char *c = strchr(parameter, '=');
91
6.22M
    size_t len = strlen(parameter);
92
6.22M
    if (c)
93
0
        len = c - parameter;
94
6.22M
    if (list == nullptr)
95
0
        return nullptr;
96
97
71.6M
    for (next = list; next; next = next->next) {
98
66.3M
        if (0 == strncmp(parameter, next->param, len) &&
99
920k
            (next->param[len] == '=' || next->param[len] == 0)) {
100
860k
            next->used = 1;
101
860k
            return next;
102
860k
        }
103
65.4M
        if (0 == strcmp(parameter, "step"))
104
0
            return nullptr;
105
65.4M
    }
106
107
5.36M
    return nullptr;
108
6.22M
}
109
110
/************************************************************************/
111
/*                              pj_param()                              */
112
/*                                                                      */
113
/*      Test for presence or get parameter value.  The first            */
114
/*      character in `opt' is a parameter type which can take the       */
115
/*      values:                                                         */
116
/*                                                                      */
117
/*       `t' - test for presence, return TRUE/FALSE in PROJVALUE.i      */
118
/*       `i' - integer value returned in PROJVALUE.i                    */
119
/*       `d' - simple valued real input returned in PROJVALUE.f         */
120
/*       `r' - degrees (DMS translation applied), returned as           */
121
/*             radians in PROJVALUE.f                                   */
122
/*       `s' - string returned in PROJVALUE.s                           */
123
/*       `b' - test for t/T/f/F, return in PROJVALUE.i                  */
124
/*                                                                      */
125
/*      Search is terminated when "step" is found, in which case        */
126
/*      0 is returned, unless "step" was the target searched for.       */
127
/*                                                                      */
128
/************************************************************************/
129
130
3.96M
PROJVALUE pj_param(PJ_CONTEXT *ctx, paralist *pl, const char *opt) {
131
132
3.96M
    int type;
133
3.96M
    unsigned l;
134
3.96M
    PROJVALUE value = {0};
135
136
3.96M
    if (ctx == nullptr)
137
0
        ctx = pj_get_default_ctx();
138
139
3.96M
    type = *opt++;
140
141
3.96M
    if (nullptr == strchr("tbirds", type)) {
142
0
        fprintf(stderr, "invalid request to pj_param, fatal\n");
143
0
        exit(1);
144
0
    }
145
146
3.96M
    pl = pj_param_exists(pl, opt);
147
3.96M
    if (type == 't') {
148
912k
        value.i = pl != nullptr;
149
912k
        return value;
150
912k
    }
151
152
    /* Not found */
153
3.04M
    if (nullptr == pl) {
154
        /* Return value after the switch, so that the return path is */
155
        /* taken in all cases */
156
2.83M
        switch (type) {
157
425k
        case 'b':
158
425k
        case 'i':
159
425k
            value.i = 0;
160
425k
            break;
161
663k
        case 'd':
162
986k
        case 'r':
163
986k
            value.f = 0.;
164
986k
            break;
165
1.42M
        case 's':
166
1.42M
            value.s = nullptr;
167
1.42M
            break;
168
2.83M
        }
169
2.83M
        return value;
170
2.83M
    }
171
172
    /* Found parameter - now find its value */
173
208k
    pl->used |= 1;
174
208k
    l = (int)strlen(opt);
175
208k
    opt = pl->param + l;
176
208k
    if (*opt == '=')
177
198k
        ++opt;
178
179
208k
    switch (type) {
180
1.31k
    case 'i': /* integer input */
181
1.31k
        value.i = atoi(opt);
182
4.82k
        for (const char *ptr = opt; *ptr != '\0'; ++ptr) {
183
3.51k
            if (!(*ptr >= '0' && *ptr <= '9')) {
184
714
                proj_context_errno_set(ctx,
185
714
                                       PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
186
714
                value.i = 0;
187
714
            }
188
3.51k
        }
189
1.31k
        break;
190
60.4k
    case 'd': /* simple real input */
191
60.4k
        value.f = pj_atof(opt);
192
60.4k
        break;
193
44.4k
    case 'r': /* degrees input */
194
44.4k
        value.f = dmstor_ctx(ctx, opt, nullptr);
195
44.4k
        break;
196
94.9k
    case 's': /* char string */
197
94.9k
        value.s = (char *)opt;
198
94.9k
        break;
199
7.88k
    case 'b': /* boolean */
200
7.88k
        switch (*opt) {
201
85
        case 'F':
202
282
        case 'f':
203
282
            value.i = 0;
204
282
            break;
205
7.04k
        case '\0':
206
7.06k
        case 'T':
207
7.11k
        case 't':
208
7.11k
            value.i = 1;
209
7.11k
            break;
210
484
        default:
211
484
            proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
212
484
            value.i = 0;
213
484
            break;
214
7.88k
        }
215
7.88k
        break;
216
208k
    }
217
208k
    return value;
218
208k
}
219
220
/* Parse +t_final parameter with support for "now" keyword */
221
5.09k
double pj_parse_t_final(PJ *P) {
222
5.09k
    if (!pj_param(P->ctx, P->params, "tt_final").i) {
223
5.00k
        return 0.0;
224
5.00k
    }
225
226
95
    double t_final = pj_param(P->ctx, P->params, "dt_final").f;
227
95
    if (t_final != 0.0) {
228
44
        return t_final;
229
44
    }
230
231
    /* Check if "now" was specified instead of a numeric value */
232
51
    const char *t_final_str = pj_param(P->ctx, P->params, "st_final").s;
233
51
    if (!t_final_str || strcmp("now", t_final_str) != 0) {
234
51
        return 0.0;
235
51
    }
236
237
    /* Calculate t_final from current time */
238
0
    time_t now;
239
0
    struct tm date;
240
0
    time(&now);
241
#ifdef _WIN32
242
    localtime_s(&date, &now);
243
#else
244
0
    localtime_r(&now, &date);
245
0
#endif
246
247
0
    const int days_in_year =
248
0
        ((date.tm_year + 1900) % 4 == 0 &&
249
0
         ((date.tm_year + 1900) % 100 != 0 || (date.tm_year + 1900) % 400 == 0))
250
0
            ? 366
251
0
            : 365;
252
253
0
    return 1900.0 + date.tm_year +
254
0
           date.tm_yday / static_cast<double>(days_in_year);
255
51
}