/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 | } |