1 | /* commands.c |
---|
2 | * Code for directives |
---|
3 | * Copyright (C) 1991-2025 Olly Betts |
---|
4 | * |
---|
5 | * This program is free software; you can redistribute it and/or modify |
---|
6 | * it under the terms of the GNU General Public License as published by |
---|
7 | * the Free Software Foundation; either version 2 of the License, or |
---|
8 | * (at your option) any later version. |
---|
9 | * |
---|
10 | * This program is distributed in the hope that it will be useful, |
---|
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
13 | * GNU General Public License for more details. |
---|
14 | * |
---|
15 | * You should have received a copy of the GNU General Public License |
---|
16 | * along with this program; if not, write to the Free Software |
---|
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
---|
18 | */ |
---|
19 | |
---|
20 | #include <config.h> |
---|
21 | |
---|
22 | #include <limits.h> |
---|
23 | #include <stddef.h> /* for offsetof */ |
---|
24 | #include <string.h> |
---|
25 | |
---|
26 | #include <proj.h> |
---|
27 | #if PROJ_VERSION_MAJOR < 8 |
---|
28 | # define proj_context_errno_string(CTX, ERR) proj_errno_string(ERR) |
---|
29 | #endif |
---|
30 | |
---|
31 | #include "cavern.h" |
---|
32 | #include "commands.h" |
---|
33 | #include "datain.h" |
---|
34 | #include "date.h" |
---|
35 | #include "debug.h" |
---|
36 | #include "filename.h" |
---|
37 | #include "message.h" |
---|
38 | #include "netbits.h" |
---|
39 | #include "netskel.h" |
---|
40 | #include "osalloc.h" |
---|
41 | #include "out.h" |
---|
42 | #include "readval.h" |
---|
43 | #include "str.h" |
---|
44 | |
---|
45 | static void |
---|
46 | move_to_fixedlist(node *stn, int ignore_dirn) |
---|
47 | { |
---|
48 | remove_stn_from_list(&stnlist, stn); |
---|
49 | add_stn_to_list(&fixedlist, stn); |
---|
50 | pos *p = stn->name->pos; |
---|
51 | for (int d = 0; d < 3; d++) { |
---|
52 | if (d == ignore_dirn) continue; |
---|
53 | linkfor *leg = stn->leg[d]; |
---|
54 | if (!leg) break; |
---|
55 | node *to = leg->l.to; |
---|
56 | if (to->name->pos == p) { |
---|
57 | move_to_fixedlist(to, reverse_leg_dirn(leg)); |
---|
58 | } |
---|
59 | } |
---|
60 | } |
---|
61 | |
---|
62 | int fix_station(prefix *fix_name, const double* coords, long offset) { |
---|
63 | bool new_stn = (fix_name->stn == NULL && |
---|
64 | !TSTBIT(fix_name->sflags, SFLAGS_SOLVED)); |
---|
65 | fix_name->sflags |= BIT(SFLAGS_FIXED); |
---|
66 | if (new_stn) fix_name->sflags |= BIT(SFLAGS_UNUSED_FIXED_POINT); |
---|
67 | node *stn = StnFromPfx(fix_name); |
---|
68 | if (fixed(stn)) { |
---|
69 | if (coords[0] != POS(stn, 0) || |
---|
70 | coords[1] != POS(stn, 1) || |
---|
71 | coords[2] != POS(stn, 2)) { |
---|
72 | return -1; |
---|
73 | } |
---|
74 | return 1; |
---|
75 | } |
---|
76 | |
---|
77 | POS(stn, 0) = coords[0]; |
---|
78 | POS(stn, 1) = coords[1]; |
---|
79 | POS(stn, 2) = coords[2]; |
---|
80 | |
---|
81 | if (new_stn) { |
---|
82 | remove_stn_from_list(&stnlist, stn); |
---|
83 | add_stn_to_list(&fixedlist, stn); |
---|
84 | } else { |
---|
85 | move_to_fixedlist(stn, -1); |
---|
86 | } |
---|
87 | |
---|
88 | // Make the station's file:line location reflect where it was fixed. |
---|
89 | fix_name->filename = file.filename; |
---|
90 | fix_name->line = file.line; |
---|
91 | fix_name->column = offset >= 0 ? offset - file.lpos : 0; |
---|
92 | return 0; |
---|
93 | } |
---|
94 | |
---|
95 | void fix_station_with_variance(prefix *fix_name, const double* coords, |
---|
96 | real var_x, real var_y, real var_z, |
---|
97 | #ifndef NO_COVARIANCES |
---|
98 | real cxy, real cyz, real czx |
---|
99 | #endif |
---|
100 | ) |
---|
101 | { |
---|
102 | bool new_stn = (fix_name->stn == NULL && |
---|
103 | !TSTBIT(fix_name->sflags, SFLAGS_SOLVED)); |
---|
104 | if (new_stn) fix_name->sflags |= BIT(SFLAGS_UNUSED_FIXED_POINT); |
---|
105 | |
---|
106 | node *stn = StnFromPfx(fix_name); |
---|
107 | if (!fixed(stn)) { |
---|
108 | node *fixpt = osnew(node); |
---|
109 | prefix *name; |
---|
110 | name = osnew(prefix); |
---|
111 | name->pos = osnew(pos); |
---|
112 | name->ident.p = NULL; |
---|
113 | fixpt->name = name; |
---|
114 | name->stn = fixpt; |
---|
115 | name->up = NULL; |
---|
116 | if (TSTBIT(pcs->infer, INFER_EXPORTS)) { |
---|
117 | name->min_export = USHRT_MAX; |
---|
118 | } else { |
---|
119 | name->min_export = 0; |
---|
120 | } |
---|
121 | name->max_export = 0; |
---|
122 | name->sflags = 0; |
---|
123 | add_stn_to_list(&fixedlist, fixpt); |
---|
124 | POS(fixpt, 0) = coords[0]; |
---|
125 | POS(fixpt, 1) = coords[1]; |
---|
126 | POS(fixpt, 2) = coords[2]; |
---|
127 | fixpt->leg[0] = fixpt->leg[1] = fixpt->leg[2] = NULL; |
---|
128 | addfakeleg(fixpt, stn, 0, 0, 0, |
---|
129 | var_x, var_y, var_z |
---|
130 | #ifndef NO_COVARIANCES |
---|
131 | , cxy, cyz, czx |
---|
132 | #endif |
---|
133 | ); |
---|
134 | } |
---|
135 | } |
---|
136 | |
---|
137 | static void |
---|
138 | default_grade(settings *s) |
---|
139 | { |
---|
140 | /* Values correspond to those in bcra5.svx */ |
---|
141 | s->Var[Q_POS] = (real)sqrd(0.05); |
---|
142 | s->Var[Q_LENGTH] = (real)sqrd(0.05); |
---|
143 | s->Var[Q_BACKLENGTH] = (real)sqrd(0.05); |
---|
144 | s->Var[Q_COUNT] = (real)sqrd(0.05); |
---|
145 | s->Var[Q_DX] = s->Var[Q_DY] = s->Var[Q_DZ] = (real)sqrd(0.05); |
---|
146 | s->Var[Q_BEARING] = (real)sqrd(rad(0.5)); |
---|
147 | s->Var[Q_GRADIENT] = (real)sqrd(rad(0.5)); |
---|
148 | s->Var[Q_BACKBEARING] = (real)sqrd(rad(0.5)); |
---|
149 | s->Var[Q_BACKGRADIENT] = (real)sqrd(rad(0.5)); |
---|
150 | /* SD of plumbed legs (0.25 degrees?) */ |
---|
151 | s->Var[Q_PLUMB] = (real)sqrd(rad(0.25)); |
---|
152 | /* SD of level legs (0.25 degrees?) */ |
---|
153 | s->Var[Q_LEVEL] = (real)sqrd(rad(0.25)); |
---|
154 | s->Var[Q_DEPTH] = (real)sqrd(0.05); |
---|
155 | } |
---|
156 | |
---|
157 | static void |
---|
158 | default_truncate(settings *s) |
---|
159 | { |
---|
160 | s->Truncate = INT_MAX; |
---|
161 | } |
---|
162 | |
---|
163 | static void |
---|
164 | default_case(settings *s) |
---|
165 | { |
---|
166 | s->Case = LOWER; |
---|
167 | } |
---|
168 | |
---|
169 | static reading default_order[] = { Fr, To, Tape, Comp, Clino, End }; |
---|
170 | |
---|
171 | static void |
---|
172 | default_style(settings *s) |
---|
173 | { |
---|
174 | s->recorded_style = s->style = STYLE_NORMAL; |
---|
175 | s->ordering = default_order; |
---|
176 | s->dash_for_anon_wall_station = false; |
---|
177 | } |
---|
178 | |
---|
179 | static void |
---|
180 | default_prefix(settings *s) |
---|
181 | { |
---|
182 | s->Prefix = root; |
---|
183 | } |
---|
184 | |
---|
185 | static void |
---|
186 | init_default_translate_map(short * t) |
---|
187 | { |
---|
188 | int i; |
---|
189 | for (i = '0'; i <= '9'; i++) t[i] |= SPECIAL_NAMES; |
---|
190 | for (i = 'A'; i <= 'Z'; i++) t[i] |= SPECIAL_NAMES; |
---|
191 | for (i = 'a'; i <= 'z'; i++) t[i] |= SPECIAL_NAMES; |
---|
192 | |
---|
193 | t['\t'] |= SPECIAL_BLANK; |
---|
194 | t[' '] |= SPECIAL_BLANK; |
---|
195 | t[','] |= SPECIAL_BLANK; |
---|
196 | t[';'] |= SPECIAL_COMMENT; |
---|
197 | t['\032'] |= SPECIAL_EOL; /* Ctrl-Z, so olde DOS text files are handled ok */ |
---|
198 | t['\n'] |= SPECIAL_EOL; |
---|
199 | t['\r'] |= SPECIAL_EOL; |
---|
200 | t['*'] |= SPECIAL_KEYWORD; |
---|
201 | t['-'] |= SPECIAL_OMIT; |
---|
202 | t['\\'] |= SPECIAL_ROOT; |
---|
203 | t['.'] |= SPECIAL_SEPARATOR; |
---|
204 | t['_'] |= SPECIAL_NAMES; |
---|
205 | t['-'] |= SPECIAL_NAMES; /* Added in 0.97 prerelease 4 */ |
---|
206 | t['.'] |= SPECIAL_DECIMAL; |
---|
207 | t['-'] |= SPECIAL_MINUS; |
---|
208 | t['+'] |= SPECIAL_PLUS; |
---|
209 | #if 0 /* FIXME */ |
---|
210 | t['{'] |= SPECIAL_OPEN; |
---|
211 | t['}'] |= SPECIAL_CLOSE; |
---|
212 | #endif |
---|
213 | } |
---|
214 | |
---|
215 | static void |
---|
216 | default_translate(settings *s) |
---|
217 | { |
---|
218 | if (s->next && s->next->Translate == s->Translate) { |
---|
219 | /* We're currently using the same character translation map as our parent |
---|
220 | * scope so allocate a new one before we modify it. |
---|
221 | */ |
---|
222 | s->Translate = ((short*)osmalloc(sizeof(short) * 257)) + 1; |
---|
223 | } else { |
---|
224 | /* SVX_ASSERT(EOF==-1);*/ /* important, since we rely on this */ |
---|
225 | } |
---|
226 | s->Translate[EOF] = SPECIAL_EOL; |
---|
227 | memset(s->Translate, 0, sizeof(short) * 256); |
---|
228 | init_default_translate_map(s->Translate); |
---|
229 | } |
---|
230 | |
---|
231 | /* Flag anything used in SPECIAL_* cumulatively to help us pick a suitable |
---|
232 | * separator to use in the .3d file. */ |
---|
233 | static short separator_map[256]; |
---|
234 | |
---|
235 | void |
---|
236 | scan_compass_station_name(prefix *stn) |
---|
237 | { |
---|
238 | /* We only need to scan the leaf station name - any survey hierarchy above |
---|
239 | * that must have been set up in .svx files for which we update |
---|
240 | * separator_map via cmd_set() plus adding the defaults in |
---|
241 | * find_output_separator(). |
---|
242 | */ |
---|
243 | for (const char *p = prefix_ident(stn); *p; ++p) { |
---|
244 | separator_map[(unsigned char)*p] |= SPECIAL_NAMES; |
---|
245 | } |
---|
246 | } |
---|
247 | |
---|
248 | static char |
---|
249 | find_output_separator(void) |
---|
250 | { |
---|
251 | // Fast path to handle most common cases where we'd pick '.'. |
---|
252 | if ((separator_map['.'] & SPECIAL_NAMES) == 0) { |
---|
253 | return '.'; |
---|
254 | } |
---|
255 | |
---|
256 | static bool added_defaults = false; |
---|
257 | if (!added_defaults) { |
---|
258 | /* Add the default settings to separator_map. */ |
---|
259 | init_default_translate_map(separator_map); |
---|
260 | added_defaults = true; |
---|
261 | } |
---|
262 | |
---|
263 | /* 30 punctuation characters plus space to try arranged in a sensible order |
---|
264 | * of decreasing preference (these are all the ASCII punctuation characters |
---|
265 | * excluding '_' and '-' since those are allowed in names by default so are |
---|
266 | * poor choices for the separator). |
---|
267 | */ |
---|
268 | int best = -1; |
---|
269 | for (const char *p = "./:;,!|\\ ~+*^='`\"#$%&?@<>()[]{}"; *p; ++p) { |
---|
270 | unsigned char candidate = *p; |
---|
271 | int mask = separator_map[candidate]; |
---|
272 | switch (mask & (SPECIAL_SEPARATOR|SPECIAL_NAMES)) { |
---|
273 | case SPECIAL_SEPARATOR: |
---|
274 | /* A character which is set as a separator character at some |
---|
275 | * point but never set as a name character is perfect. |
---|
276 | */ |
---|
277 | return candidate; |
---|
278 | case 0: |
---|
279 | /* A character which is never set as either a separator |
---|
280 | * character or a name character is a reasonable option. |
---|
281 | */ |
---|
282 | if (best < 0) best = candidate; |
---|
283 | break; |
---|
284 | } |
---|
285 | } |
---|
286 | if (best < 0) { |
---|
287 | /* Argh, no plausible choice! Just return the default for now. */ |
---|
288 | return '.'; |
---|
289 | } |
---|
290 | return best; |
---|
291 | } |
---|
292 | |
---|
293 | void |
---|
294 | default_units(settings *s) |
---|
295 | { |
---|
296 | int quantity; |
---|
297 | for (quantity = 0; quantity < Q_MAC; quantity++) { |
---|
298 | if (TSTBIT(ANG_QMASK, quantity)) |
---|
299 | s->units[quantity] = (real)(M_PI / 180.0); /* degrees */ |
---|
300 | else |
---|
301 | s->units[quantity] = (real)1.0; /* metres */ |
---|
302 | } |
---|
303 | s->f_clino_percent = s->f_backclino_percent = false; |
---|
304 | s->f_bearing_quadrants = s->f_backbearing_quadrants = false; |
---|
305 | } |
---|
306 | |
---|
307 | void |
---|
308 | default_calib(settings *s) |
---|
309 | { |
---|
310 | int quantity; |
---|
311 | for (quantity = 0; quantity < Q_MAC; quantity++) { |
---|
312 | s->z[quantity] = (real)0.0; |
---|
313 | s->sc[quantity] = (real)1.0; |
---|
314 | } |
---|
315 | } |
---|
316 | |
---|
317 | static void |
---|
318 | default_flags(settings *s) |
---|
319 | { |
---|
320 | s->flags = 0; |
---|
321 | } |
---|
322 | |
---|
323 | extern void |
---|
324 | default_all(settings *s) |
---|
325 | { |
---|
326 | default_truncate(s); |
---|
327 | s->infer = 0; |
---|
328 | default_case(s); |
---|
329 | default_style(s); |
---|
330 | default_prefix(s); |
---|
331 | default_translate(s); |
---|
332 | default_grade(s); |
---|
333 | default_units(s); |
---|
334 | default_calib(s); |
---|
335 | default_flags(s); |
---|
336 | } |
---|
337 | |
---|
338 | string token = S_INIT; |
---|
339 | |
---|
340 | string uctoken = S_INIT; |
---|
341 | |
---|
342 | extern void |
---|
343 | get_token_legacy(void) |
---|
344 | { |
---|
345 | skipblanks(); |
---|
346 | get_token_legacy_no_blanks(); |
---|
347 | } |
---|
348 | |
---|
349 | extern void |
---|
350 | get_token_legacy_no_blanks(void) |
---|
351 | { |
---|
352 | s_clear(&token); |
---|
353 | s_clear(&uctoken); |
---|
354 | while (isalpha(ch)) { |
---|
355 | s_appendch(&token, ch); |
---|
356 | s_appendch(&uctoken, toupper(ch)); |
---|
357 | nextch(); |
---|
358 | } |
---|
359 | |
---|
360 | #if 0 |
---|
361 | printf("get_token_legacy_no_blanks() got “%s”\n", s_str(&token)); |
---|
362 | #endif |
---|
363 | } |
---|
364 | |
---|
365 | void |
---|
366 | do_legacy_token_warning(void) |
---|
367 | { |
---|
368 | if (!s_empty(&token)) { |
---|
369 | if (!isBlank(ch) && !isComm(ch) && !isEol(ch)) { |
---|
370 | compile_diagnostic(DIAG_WARN|DIAG_COL, /*No blank after token*/74); |
---|
371 | } |
---|
372 | } |
---|
373 | } |
---|
374 | |
---|
375 | extern void |
---|
376 | get_token(void) |
---|
377 | { |
---|
378 | skipblanks(); |
---|
379 | get_token_no_blanks(); |
---|
380 | } |
---|
381 | |
---|
382 | extern void |
---|
383 | get_token_no_blanks(void) |
---|
384 | { |
---|
385 | s_clear(&token); |
---|
386 | s_clear(&uctoken); |
---|
387 | if (isalpha(ch)) { |
---|
388 | do { |
---|
389 | s_appendch(&token, ch); |
---|
390 | s_appendch(&uctoken, toupper(ch)); |
---|
391 | nextch(); |
---|
392 | } while (isalnum(ch)); |
---|
393 | } |
---|
394 | } |
---|
395 | |
---|
396 | /* read word */ |
---|
397 | void |
---|
398 | get_word(void) |
---|
399 | { |
---|
400 | s_clear(&token); |
---|
401 | skipblanks(); |
---|
402 | while (!isBlank(ch) && !isComm(ch) && !isEol(ch)) { |
---|
403 | s_appendch(&token, ch); |
---|
404 | nextch(); |
---|
405 | } |
---|
406 | #if 0 |
---|
407 | printf("get_word() got “%s”\n", s_str(&token)); |
---|
408 | #endif |
---|
409 | } |
---|
410 | |
---|
411 | /* match_tok() now uses binary chop |
---|
412 | * tab argument should be alphabetically sorted (ascending) |
---|
413 | */ |
---|
414 | extern int |
---|
415 | match_tok(const sztok *tab, int tab_size) |
---|
416 | { |
---|
417 | int a = 0, b = tab_size - 1, c; |
---|
418 | int r; |
---|
419 | const char* tok = s_str(&uctoken); |
---|
420 | SVX_ASSERT(tab_size > 0); /* catch empty table */ |
---|
421 | /* printf("[%d,%d]",a,b); */ |
---|
422 | while (a <= b) { |
---|
423 | c = (unsigned)(a + b) / 2; |
---|
424 | /* printf(" %d",c); */ |
---|
425 | r = strcmp(tab[c].sz, tok); |
---|
426 | if (r == 0) return tab[c].tok; /* match */ |
---|
427 | if (r < 0) |
---|
428 | a = c + 1; |
---|
429 | else |
---|
430 | b = c - 1; |
---|
431 | } |
---|
432 | return tab[tab_size].tok; /* no match */ |
---|
433 | } |
---|
434 | |
---|
435 | typedef enum { |
---|
436 | CMD_NULL = -1, CMD_ALIAS, CMD_BEGIN, CMD_CALIBRATE, CMD_CARTESIAN, CMD_CASE, |
---|
437 | CMD_COPYRIGHT, CMD_CS, CMD_DATA, CMD_DATE, CMD_DECLINATION, CMD_DEFAULT, |
---|
438 | CMD_END, CMD_ENTRANCE, CMD_EQUATE, CMD_EXPORT, CMD_FIX, CMD_FLAGS, |
---|
439 | CMD_INCLUDE, CMD_INFER, CMD_INSTRUMENT, CMD_PREFIX, CMD_REF, CMD_REQUIRE, |
---|
440 | CMD_SD, CMD_SET, CMD_SOLVE, CMD_TEAM, CMD_TITLE, CMD_TRUNCATE, CMD_UNITS |
---|
441 | } cmds; |
---|
442 | |
---|
443 | static const sztok cmd_tab[] = { |
---|
444 | {"ALIAS", CMD_ALIAS}, |
---|
445 | {"BEGIN", CMD_BEGIN}, |
---|
446 | {"CALIBRATE", CMD_CALIBRATE}, |
---|
447 | {"CARTESIAN", CMD_CARTESIAN}, |
---|
448 | {"CASE", CMD_CASE}, |
---|
449 | {"COPYRIGHT", CMD_COPYRIGHT}, |
---|
450 | {"CS", CMD_CS}, |
---|
451 | {"DATA", CMD_DATA}, |
---|
452 | {"DATE", CMD_DATE}, |
---|
453 | {"DECLINATION", CMD_DECLINATION}, |
---|
454 | #ifndef NO_DEPRECATED |
---|
455 | {"DEFAULT", CMD_DEFAULT}, |
---|
456 | #endif |
---|
457 | {"END", CMD_END}, |
---|
458 | {"ENTRANCE", CMD_ENTRANCE}, |
---|
459 | {"EQUATE", CMD_EQUATE}, |
---|
460 | {"EXPORT", CMD_EXPORT}, |
---|
461 | {"FIX", CMD_FIX}, |
---|
462 | {"FLAGS", CMD_FLAGS}, |
---|
463 | {"INCLUDE", CMD_INCLUDE}, |
---|
464 | {"INFER", CMD_INFER}, |
---|
465 | {"INSTRUMENT",CMD_INSTRUMENT}, |
---|
466 | #ifndef NO_DEPRECATED |
---|
467 | {"PREFIX", CMD_PREFIX}, |
---|
468 | #endif |
---|
469 | {"REF", CMD_REF}, |
---|
470 | {"REQUIRE", CMD_REQUIRE}, |
---|
471 | {"SD", CMD_SD}, |
---|
472 | {"SET", CMD_SET}, |
---|
473 | {"SOLVE", CMD_SOLVE}, |
---|
474 | {"TEAM", CMD_TEAM}, |
---|
475 | {"TITLE", CMD_TITLE}, |
---|
476 | {"TRUNCATE", CMD_TRUNCATE}, |
---|
477 | {"UNITS", CMD_UNITS}, |
---|
478 | {NULL, CMD_NULL} |
---|
479 | }; |
---|
480 | |
---|
481 | /* masks for units which are length and angles respectively */ |
---|
482 | #define LEN_UMASK (BIT(UNITS_METRES) | BIT(UNITS_FEET) | BIT(UNITS_YARDS)) |
---|
483 | #define ANG_UMASK (BIT(UNITS_DEGS) | BIT(UNITS_GRADS) | BIT(UNITS_MINUTES)) |
---|
484 | |
---|
485 | /* ordering must be the same as the units enum */ |
---|
486 | const real factor_tab[] = { |
---|
487 | 1.0, METRES_PER_FOOT, (METRES_PER_FOOT*3.0), |
---|
488 | (M_PI/180.0), (M_PI/180.0), (M_PI/200.0), 0.01, (M_PI/180.0/60.0) |
---|
489 | }; |
---|
490 | |
---|
491 | const int units_to_msgno[] = { |
---|
492 | /*m*/424, |
---|
493 | /*′*/428, |
---|
494 | -1, /* yards */ |
---|
495 | /*°*/344, /* quadrants */ |
---|
496 | /*°*/344, |
---|
497 | /*ᵍ*/345, |
---|
498 | /*%*/96, |
---|
499 | -1 /* minutes */ |
---|
500 | }; |
---|
501 | |
---|
502 | int get_length_units(int quantity) { |
---|
503 | double factor = pcs->units[quantity]; |
---|
504 | if (fabs(factor - METRES_PER_FOOT) <= REAL_EPSILON || |
---|
505 | fabs(factor - METRES_PER_FOOT * 3.0) <= REAL_EPSILON) { |
---|
506 | return UNITS_FEET; |
---|
507 | } |
---|
508 | return UNITS_METRES; |
---|
509 | } |
---|
510 | |
---|
511 | int get_angle_units(int quantity) { |
---|
512 | double factor = pcs->units[quantity]; |
---|
513 | if (fabs(factor - M_PI / 200.0) <= REAL_EPSILON) { |
---|
514 | return UNITS_GRADS; |
---|
515 | } |
---|
516 | return UNITS_DEGS; |
---|
517 | } |
---|
518 | |
---|
519 | static int |
---|
520 | get_units(unsigned long qmask, bool percent_ok) |
---|
521 | { |
---|
522 | static const sztok utab[] = { |
---|
523 | {"DEGREES", UNITS_DEGS }, |
---|
524 | {"DEGS", UNITS_DEGS }, |
---|
525 | {"FEET", UNITS_FEET }, |
---|
526 | {"GRADS", UNITS_GRADS }, |
---|
527 | {"METERS", UNITS_METRES }, |
---|
528 | {"METRES", UNITS_METRES }, |
---|
529 | {"METRIC", UNITS_METRES }, |
---|
530 | {"MILS", UNITS_DEPRECATED_ALIAS_FOR_GRADS }, |
---|
531 | {"MINUTES", UNITS_MINUTES }, |
---|
532 | {"PERCENT", UNITS_PERCENT }, |
---|
533 | {"PERCENTAGE", UNITS_PERCENT }, |
---|
534 | {"QUADRANTS", UNITS_QUADRANTS }, |
---|
535 | {"QUADS", UNITS_QUADRANTS }, |
---|
536 | {"YARDS", UNITS_YARDS }, |
---|
537 | {NULL, UNITS_NULL } |
---|
538 | }; |
---|
539 | get_token(); |
---|
540 | int units = match_tok(utab, TABSIZE(utab)); |
---|
541 | if (units == UNITS_NULL) { |
---|
542 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Unknown units “%s”*/35, |
---|
543 | s_str(&token)); |
---|
544 | return UNITS_NULL; |
---|
545 | } |
---|
546 | /* Survex has long misdefined "mils" as an alias for "grads", of which |
---|
547 | * there are 400 in a circle. There are several definitions of "mils" |
---|
548 | * with a circle containing 2000π SI milliradians, 6400 NATO mils, 6000 |
---|
549 | * Warsaw Pact mils, and 6300 Swedish streck, and they aren't in common |
---|
550 | * use by cave surveyors, so we now just warn if mils are used. |
---|
551 | */ |
---|
552 | if (units == UNITS_DEPRECATED_ALIAS_FOR_GRADS) { |
---|
553 | compile_diagnostic(DIAG_WARN|DIAG_TOKEN|DIAG_SKIP, |
---|
554 | /*Units “%s” are deprecated, assuming “grads” - see manual for details*/479, |
---|
555 | s_str(&token)); |
---|
556 | units = UNITS_GRADS; |
---|
557 | } |
---|
558 | if (units == UNITS_PERCENT && percent_ok && |
---|
559 | !(qmask & ~(BIT(Q_GRADIENT)|BIT(Q_BACKGRADIENT)))) { |
---|
560 | return units; |
---|
561 | } |
---|
562 | if (units == UNITS_QUADRANTS && |
---|
563 | !(qmask & ~(BIT(Q_BEARING)|BIT(Q_BACKBEARING)))) { |
---|
564 | return units; |
---|
565 | } |
---|
566 | if (((qmask & LEN_QMASK) && !TSTBIT(LEN_UMASK, units)) || |
---|
567 | ((qmask & ANG_QMASK) && !TSTBIT(ANG_UMASK, units))) { |
---|
568 | /* TRANSLATORS: Note: In English you talk about the *units* of a single |
---|
569 | * measurement, but the correct term in other languages may be singular. |
---|
570 | */ |
---|
571 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Invalid units “%s” for quantity*/37, s_str(&token)); |
---|
572 | return UNITS_NULL; |
---|
573 | } |
---|
574 | return units; |
---|
575 | } |
---|
576 | |
---|
577 | /* returns mask with bit x set to indicate quantity x specified */ |
---|
578 | static unsigned long |
---|
579 | get_qlist(unsigned long mask_bad) |
---|
580 | { |
---|
581 | static const sztok qtab[] = { |
---|
582 | {"ALTITUDE", Q_DZ }, |
---|
583 | {"BACKBEARING", Q_BACKBEARING }, |
---|
584 | {"BACKCLINO", Q_BACKGRADIENT }, /* alternative name */ |
---|
585 | {"BACKCOMPASS", Q_BACKBEARING }, /* alternative name */ |
---|
586 | {"BACKGRADIENT", Q_BACKGRADIENT }, |
---|
587 | {"BACKLENGTH", Q_BACKLENGTH }, |
---|
588 | {"BACKTAPE", Q_BACKLENGTH }, /* alternative name */ |
---|
589 | {"BEARING", Q_BEARING }, |
---|
590 | {"CEILING", Q_UP }, /* alternative name */ |
---|
591 | {"CLINO", Q_GRADIENT }, /* alternative name */ |
---|
592 | {"COMPASS", Q_BEARING }, /* alternative name */ |
---|
593 | {"COUNT", Q_COUNT }, |
---|
594 | {"COUNTER", Q_COUNT }, /* alternative name */ |
---|
595 | {"DECLINATION", Q_DECLINATION }, |
---|
596 | {"DEFAULT", Q_DEFAULT }, /* not a real quantity... */ |
---|
597 | {"DEPTH", Q_DEPTH }, |
---|
598 | {"DOWN", Q_DOWN }, |
---|
599 | {"DX", Q_DX }, /* alternative name */ |
---|
600 | {"DY", Q_DY }, /* alternative name */ |
---|
601 | {"DZ", Q_DZ }, /* alternative name */ |
---|
602 | {"EASTING", Q_DX }, |
---|
603 | {"FLOOR", Q_DOWN }, /* alternative name */ |
---|
604 | {"GRADIENT", Q_GRADIENT }, |
---|
605 | {"LEFT", Q_LEFT }, |
---|
606 | {"LENGTH", Q_LENGTH }, |
---|
607 | {"LEVEL", Q_LEVEL}, |
---|
608 | {"NORTHING", Q_DY }, |
---|
609 | {"PLUMB", Q_PLUMB}, |
---|
610 | {"POSITION", Q_POS }, |
---|
611 | {"RIGHT", Q_RIGHT }, |
---|
612 | {"TAPE", Q_LENGTH }, /* alternative name */ |
---|
613 | {"UP", Q_UP }, |
---|
614 | {NULL, Q_NULL } |
---|
615 | }; |
---|
616 | unsigned long qmask = 0; |
---|
617 | int tok; |
---|
618 | filepos fp; |
---|
619 | |
---|
620 | while (1) { |
---|
621 | get_pos(&fp); |
---|
622 | get_token_legacy(); |
---|
623 | tok = match_tok(qtab, TABSIZE(qtab)); |
---|
624 | if (tok == Q_DEFAULT && !(mask_bad & BIT(Q_DEFAULT))) { |
---|
625 | /* Only recognise DEFAULT if it is the first quantity, and then don't |
---|
626 | * look for any more. */ |
---|
627 | if (qmask == 0) |
---|
628 | return BIT(Q_DEFAULT); |
---|
629 | break; |
---|
630 | } |
---|
631 | /* bail out if we reach the table end with no match */ |
---|
632 | if (tok == Q_NULL) break; |
---|
633 | qmask |= BIT(tok); |
---|
634 | if (qmask & mask_bad) { |
---|
635 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Unknown instrument “%s”*/39, s_str(&token)); |
---|
636 | return 0; |
---|
637 | } |
---|
638 | } |
---|
639 | |
---|
640 | if (qmask == 0) { |
---|
641 | /* TRANSLATORS: A "quantity" is something measured like "LENGTH", |
---|
642 | * "BEARING", "ALTITUDE", etc. */ |
---|
643 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Unknown quantity “%s”*/34, s_str(&token)); |
---|
644 | } else { |
---|
645 | set_pos(&fp); |
---|
646 | } |
---|
647 | |
---|
648 | return qmask; |
---|
649 | } |
---|
650 | |
---|
651 | #define SPECIAL_UNKNOWN 0 |
---|
652 | static void |
---|
653 | cmd_set(void) |
---|
654 | { |
---|
655 | static const sztok chartab[] = { |
---|
656 | {"BLANK", SPECIAL_BLANK }, |
---|
657 | /*FIXME {"CLOSE", SPECIAL_CLOSE }, */ |
---|
658 | {"COMMENT", SPECIAL_COMMENT }, |
---|
659 | {"DECIMAL", SPECIAL_DECIMAL }, |
---|
660 | {"EOL", SPECIAL_EOL }, /* EOL won't work well */ |
---|
661 | {"KEYWORD", SPECIAL_KEYWORD }, |
---|
662 | {"MINUS", SPECIAL_MINUS }, |
---|
663 | {"NAMES", SPECIAL_NAMES }, |
---|
664 | {"OMIT", SPECIAL_OMIT }, |
---|
665 | /*FIXME {"OPEN", SPECIAL_OPEN }, */ |
---|
666 | {"PLUS", SPECIAL_PLUS }, |
---|
667 | #ifndef NO_DEPRECATED |
---|
668 | {"ROOT", SPECIAL_ROOT }, |
---|
669 | #endif |
---|
670 | {"SEPARATOR", SPECIAL_SEPARATOR }, |
---|
671 | {NULL, SPECIAL_UNKNOWN } |
---|
672 | }; |
---|
673 | int i; |
---|
674 | |
---|
675 | get_token(); |
---|
676 | int mask = match_tok(chartab, TABSIZE(chartab)); |
---|
677 | |
---|
678 | if (mask == SPECIAL_UNKNOWN) { |
---|
679 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Unknown character class “%s”*/42, |
---|
680 | s_str(&token)); |
---|
681 | return; |
---|
682 | } |
---|
683 | |
---|
684 | #ifndef NO_DEPRECATED |
---|
685 | if (mask == SPECIAL_ROOT) { |
---|
686 | if (root_depr_count < 5) { |
---|
687 | /* TRANSLATORS: Use of the ROOT character (which is "\" by default) is |
---|
688 | * deprecated, so this error would be generated by: |
---|
689 | * |
---|
690 | * *equate \foo.7 1 |
---|
691 | * |
---|
692 | * If you're unsure what "deprecated" means, see: |
---|
693 | * https://en.wikipedia.org/wiki/Deprecation */ |
---|
694 | compile_diagnostic(DIAG_WARN|DIAG_TOKEN, /*ROOT is deprecated*/25); |
---|
695 | if (++root_depr_count == 5) |
---|
696 | /* TRANSLATORS: If you're unsure what "deprecated" means, see: |
---|
697 | * https://en.wikipedia.org/wiki/Deprecation */ |
---|
698 | compile_diagnostic(DIAG_INFO, /*Further uses of this deprecated feature will not be reported*/95); |
---|
699 | } |
---|
700 | } |
---|
701 | #endif |
---|
702 | |
---|
703 | /* if we're currently using an inherited translation table, allocate a new |
---|
704 | * table, and copy old one into it */ |
---|
705 | if (pcs->next && pcs->next->Translate == pcs->Translate) { |
---|
706 | short *p; |
---|
707 | p = ((short*)osmalloc(sizeof(short) * 257)) + 1; |
---|
708 | memcpy(p - 1, pcs->Translate - 1, sizeof(short) * 257); |
---|
709 | pcs->Translate = p; |
---|
710 | } |
---|
711 | |
---|
712 | skipblanks(); |
---|
713 | |
---|
714 | /* clear this flag for all non-alphanums */ |
---|
715 | for (i = 0; i < 256; i++) |
---|
716 | if (!isalnum(i)) pcs->Translate[i] &= ~mask; |
---|
717 | |
---|
718 | /* now set this flag for all specified chars */ |
---|
719 | while (!isEol(ch)) { |
---|
720 | int char_to_set; |
---|
721 | if (!isalnum(ch)) { |
---|
722 | char_to_set = ch; |
---|
723 | } else if (tolower(ch) == 'x') { |
---|
724 | int hex; |
---|
725 | filepos fp; |
---|
726 | get_pos(&fp); |
---|
727 | nextch(); |
---|
728 | if (!isxdigit(ch)) { |
---|
729 | set_pos(&fp); |
---|
730 | break; |
---|
731 | } |
---|
732 | hex = isdigit(ch) ? ch - '0' : tolower(ch) - 'a'; |
---|
733 | nextch(); |
---|
734 | if (!isxdigit(ch)) { |
---|
735 | set_pos(&fp); |
---|
736 | break; |
---|
737 | } |
---|
738 | hex = hex << 4 | (isdigit(ch) ? ch - '0' : tolower(ch) - 'a'); |
---|
739 | char_to_set = hex; |
---|
740 | } else { |
---|
741 | break; |
---|
742 | } |
---|
743 | pcs->Translate[char_to_set] |= mask; |
---|
744 | separator_map[char_to_set] |= mask; |
---|
745 | nextch(); |
---|
746 | } |
---|
747 | |
---|
748 | output_separator = find_output_separator(); |
---|
749 | } |
---|
750 | |
---|
751 | void |
---|
752 | update_output_separator(void) |
---|
753 | { |
---|
754 | output_separator = find_output_separator(); |
---|
755 | } |
---|
756 | |
---|
757 | static void |
---|
758 | check_reentry(prefix *survey, const filepos* fpos_ptr) |
---|
759 | { |
---|
760 | /* Don't try to check "*prefix \" or "*begin \" */ |
---|
761 | if (!survey->up) return; |
---|
762 | if (TSTBIT(survey->sflags, SFLAGS_PREFIX_ENTERED)) { |
---|
763 | static int reenter_depr_count = 0; |
---|
764 | filepos fp_tmp; |
---|
765 | |
---|
766 | if (reenter_depr_count >= 5) |
---|
767 | return; |
---|
768 | |
---|
769 | get_pos(&fp_tmp); |
---|
770 | set_pos(fpos_ptr); |
---|
771 | /* TRANSLATORS: The first of two warnings given when a survey which has |
---|
772 | * already been completed is reentered. This example file crawl.svx: |
---|
773 | * |
---|
774 | * *begin crawl ; <- second warning here |
---|
775 | * 1 2 9.45 234 -01 |
---|
776 | * *end crawl |
---|
777 | * *begin crawl ; <- first warning here |
---|
778 | * 2 3 7.67 223 -03 |
---|
779 | * *end crawl |
---|
780 | * |
---|
781 | * Would lead to: |
---|
782 | * |
---|
783 | * crawl.svx:4:8: warning: Reentering an existing survey is deprecated |
---|
784 | * crawl.svx:1: info: Originally entered here |
---|
785 | * |
---|
786 | * If you're unsure what "deprecated" means, see: |
---|
787 | * https://en.wikipedia.org/wiki/Deprecation */ |
---|
788 | compile_diagnostic(DIAG_WARN|DIAG_WORD, /*Reentering an existing survey is deprecated*/29); |
---|
789 | set_pos(&fp_tmp); |
---|
790 | /* TRANSLATORS: The second of two warnings given when a survey which has |
---|
791 | * already been completed is reentered. This example file crawl.svx: |
---|
792 | * |
---|
793 | * *begin crawl ; <- second warning here |
---|
794 | * 1 2 9.45 234 -01 |
---|
795 | * *end crawl |
---|
796 | * *begin crawl ; <- first warning here |
---|
797 | * 2 3 7.67 223 -03 |
---|
798 | * *end crawl |
---|
799 | * |
---|
800 | * Would lead to: |
---|
801 | * |
---|
802 | * crawl.svx:4:8: warning: Reentering an existing survey is deprecated |
---|
803 | * crawl.svx:1: info: Originally entered here |
---|
804 | * |
---|
805 | * If you're unsure what "deprecated" means, see: |
---|
806 | * https://en.wikipedia.org/wiki/Deprecation */ |
---|
807 | compile_diagnostic_pfx(DIAG_INFO, survey, /*Originally entered here*/30); |
---|
808 | if (++reenter_depr_count == 5) { |
---|
809 | /* After we've warned about 5 uses of the same deprecated feature, we |
---|
810 | * give up for the rest of the current processing run. |
---|
811 | * |
---|
812 | * If you're unsure what "deprecated" means, see: |
---|
813 | * https://en.wikipedia.org/wiki/Deprecation */ |
---|
814 | compile_diagnostic(DIAG_INFO, /*Further uses of this deprecated feature will not be reported*/95); |
---|
815 | } |
---|
816 | } else { |
---|
817 | survey->sflags |= BIT(SFLAGS_PREFIX_ENTERED); |
---|
818 | survey->filename = file.filename; |
---|
819 | survey->line = file.line; |
---|
820 | survey->column = fpos_ptr->offset - file.lpos; |
---|
821 | } |
---|
822 | } |
---|
823 | |
---|
824 | #ifndef NO_DEPRECATED |
---|
825 | static void |
---|
826 | cmd_prefix(void) |
---|
827 | { |
---|
828 | filepos fp; |
---|
829 | get_pos(&fp); |
---|
830 | prefix *survey = read_prefix(PFX_SURVEY|PFX_ALLOW_ROOT); |
---|
831 | pcs->Prefix = survey; |
---|
832 | check_reentry(survey, &fp); |
---|
833 | } |
---|
834 | #endif |
---|
835 | |
---|
836 | static void |
---|
837 | cmd_alias(void) |
---|
838 | { |
---|
839 | /* Currently only two forms are supported: |
---|
840 | * *alias station - .. |
---|
841 | * *alias station - |
---|
842 | */ |
---|
843 | get_token(); |
---|
844 | if (!S_EQ(&uctoken, "STATION")) { |
---|
845 | compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_TOKEN, /*Bad *alias command*/397); |
---|
846 | return; |
---|
847 | } |
---|
848 | get_word(); |
---|
849 | if (!S_EQ(&token, "-")) |
---|
850 | goto bad_word; |
---|
851 | get_word(); |
---|
852 | if (!s_empty(&token) && !S_EQ(&token, "..")) |
---|
853 | goto bad_word; |
---|
854 | pcs->dash_for_anon_wall_station = !s_empty(&token); |
---|
855 | return; |
---|
856 | bad_word: |
---|
857 | compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_TOKEN, /*Bad *alias command*/397); |
---|
858 | } |
---|
859 | |
---|
860 | static void |
---|
861 | cmd_begin(void) |
---|
862 | { |
---|
863 | settings *pcsNew; |
---|
864 | |
---|
865 | pcsNew = osnew(settings); |
---|
866 | *pcsNew = *pcs; /* copy contents */ |
---|
867 | pcsNew->begin_lineno = file.line; |
---|
868 | pcsNew->begin_lpos = file.lpos; |
---|
869 | pcsNew->next = pcs; |
---|
870 | pcs = pcsNew; |
---|
871 | |
---|
872 | pcs->begin_survey = NULL; |
---|
873 | pcs->begin_col = 0; |
---|
874 | if (!isEol(ch) && !isComm(ch)) { |
---|
875 | filepos fp; |
---|
876 | prefix *survey; |
---|
877 | get_pos(&fp); |
---|
878 | int begin_col = fp.offset - file.lpos; |
---|
879 | survey = read_prefix(PFX_SURVEY|PFX_ALLOW_ROOT|PFX_WARN_SEPARATOR); |
---|
880 | // read_prefix() might fail and longjmp() so only set begin_col if |
---|
881 | // it succeeds. |
---|
882 | pcs->begin_col = begin_col; |
---|
883 | pcs->begin_survey = survey; |
---|
884 | pcs->Prefix = survey; |
---|
885 | check_reentry(survey, &fp); |
---|
886 | f_export_ok = true; |
---|
887 | } |
---|
888 | } |
---|
889 | |
---|
890 | void |
---|
891 | invalidate_pj_cached(void) |
---|
892 | { |
---|
893 | /* Invalidate the cached PJ. */ |
---|
894 | if (pj_cached) { |
---|
895 | proj_destroy(pj_cached); |
---|
896 | pj_cached = NULL; |
---|
897 | } |
---|
898 | } |
---|
899 | |
---|
900 | void |
---|
901 | report_declination(settings *p) |
---|
902 | { |
---|
903 | if (p->min_declination <= p->max_declination) { |
---|
904 | int y, m, d; |
---|
905 | char range[128]; |
---|
906 | const char* deg_sign = msg(/*°*/344); |
---|
907 | ymd_from_days_since_1900(p->min_declination_days, &y, &m, &d); |
---|
908 | snprintf(range, sizeof(range), |
---|
909 | "%.1f%s @ %04d-%02d-%02d", |
---|
910 | deg(p->min_declination), deg_sign, y, m, d); |
---|
911 | if (p->min_declination_days != p->max_declination_days) { |
---|
912 | size_t len = strlen(range); |
---|
913 | ymd_from_days_since_1900(p->max_declination_days, &y, &m, &d); |
---|
914 | snprintf(range + len, sizeof(range) - len, |
---|
915 | " / %.1f%s @ %04d-%02d-%02d", |
---|
916 | deg(p->max_declination), deg_sign, y, m, d); |
---|
917 | } |
---|
918 | /* TRANSLATORS: This message gives information about the range of |
---|
919 | * declination values and the grid convergence value calculated for |
---|
920 | * each "*declination auto ..." command. |
---|
921 | * |
---|
922 | * The first %s will be replaced by the declination range (or single |
---|
923 | * value), and %.1f%s by the grid convergence angle. |
---|
924 | */ |
---|
925 | compile_diagnostic_at(DIAG_INFO, p->dec_filename, p->dec_line, |
---|
926 | /*Declination: %s, grid convergence: %.1f%s*/484, |
---|
927 | range, |
---|
928 | deg(p->convergence), deg_sign); |
---|
929 | PUTC(' ', STDERR); |
---|
930 | fputs(p->dec_context, STDERR); |
---|
931 | fputnl(STDERR); |
---|
932 | if (p->next && p->dec_context != p->next->dec_context) |
---|
933 | free(p->dec_context); |
---|
934 | p->dec_context = NULL; |
---|
935 | p->min_declination = HUGE_VAL; |
---|
936 | p->max_declination = -HUGE_VAL; |
---|
937 | } |
---|
938 | } |
---|
939 | |
---|
940 | void |
---|
941 | pop_settings(void) |
---|
942 | { |
---|
943 | settings * p = pcs; |
---|
944 | pcs = pcs->next; |
---|
945 | |
---|
946 | SVX_ASSERT(pcs); |
---|
947 | |
---|
948 | if (pcs->dec_lat != p->dec_lat || |
---|
949 | pcs->dec_lon != p->dec_lon || |
---|
950 | pcs->dec_alt != p->dec_alt) { |
---|
951 | report_declination(p); |
---|
952 | } else { |
---|
953 | pcs->min_declination_days = p->min_declination_days; |
---|
954 | pcs->max_declination_days = p->max_declination_days; |
---|
955 | pcs->min_declination = p->min_declination; |
---|
956 | pcs->max_declination = p->max_declination; |
---|
957 | pcs->convergence = p->convergence; |
---|
958 | } |
---|
959 | |
---|
960 | if (p->proj_str != pcs->proj_str) { |
---|
961 | if (!p->proj_str || !pcs->proj_str || |
---|
962 | strcmp(p->proj_str, pcs->proj_str) != 0) { |
---|
963 | invalidate_pj_cached(); |
---|
964 | } |
---|
965 | /* free proj_str if not used by parent */ |
---|
966 | free(p->proj_str); |
---|
967 | } |
---|
968 | |
---|
969 | /* don't free default ordering or ordering used by parent */ |
---|
970 | if (p->ordering != default_order && p->ordering != pcs->ordering) |
---|
971 | free((reading*)p->ordering); |
---|
972 | |
---|
973 | /* free Translate if not used by parent */ |
---|
974 | if (p->Translate != pcs->Translate) |
---|
975 | free(p->Translate - 1); |
---|
976 | |
---|
977 | /* free meta if not used by parent, or in this block */ |
---|
978 | if (p->meta && p->meta != pcs->meta && p->meta->ref_count == 0) |
---|
979 | free(p->meta); |
---|
980 | |
---|
981 | free(p); |
---|
982 | } |
---|
983 | |
---|
984 | static void |
---|
985 | cmd_end(void) |
---|
986 | { |
---|
987 | filepos fp; |
---|
988 | |
---|
989 | int begin_lineno = pcs->begin_lineno; |
---|
990 | if (begin_lineno == 0) { |
---|
991 | if (pcs->next == NULL) { |
---|
992 | /* more ENDs than BEGINs */ |
---|
993 | /* TRANSLATORS: %s is replaced with e.g. BEGIN or .BOOK or #[ */ |
---|
994 | compile_diagnostic(DIAG_ERR|DIAG_SKIP, /*No matching %s*/192, "BEGIN"); |
---|
995 | } else { |
---|
996 | /* TRANSLATORS: %s and %s are replaced with e.g. BEGIN and END |
---|
997 | * or END and BEGIN or #[ and #] */ |
---|
998 | compile_diagnostic(DIAG_ERR|DIAG_SKIP, |
---|
999 | /*%s with no matching %s in this file*/23, "END", "BEGIN"); |
---|
1000 | } |
---|
1001 | return; |
---|
1002 | } |
---|
1003 | |
---|
1004 | prefix *begin_survey = pcs->begin_survey; |
---|
1005 | long begin_lpos = pcs->begin_lpos; |
---|
1006 | int begin_col = pcs->begin_col; |
---|
1007 | |
---|
1008 | pop_settings(); |
---|
1009 | |
---|
1010 | /* note need to read using root *before* BEGIN */ |
---|
1011 | prefix *survey = NULL; |
---|
1012 | if (!isEol(ch) && !isComm(ch)) { |
---|
1013 | get_pos(&fp); |
---|
1014 | survey = read_prefix(PFX_SURVEY|PFX_ALLOW_ROOT); |
---|
1015 | } |
---|
1016 | |
---|
1017 | if (survey != begin_survey) { |
---|
1018 | filepos fp_save; |
---|
1019 | get_pos(&fp_save); |
---|
1020 | if (survey) { |
---|
1021 | set_pos(&fp); |
---|
1022 | if (!begin_survey) { |
---|
1023 | /* TRANSLATORS: Used when a BEGIN command has no survey, but the |
---|
1024 | * END command does, e.g.: |
---|
1025 | * |
---|
1026 | * *begin |
---|
1027 | * 1 2 10.00 178 -01 |
---|
1028 | * *end entrance <--[Message given here] */ |
---|
1029 | compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Matching BEGIN command has no survey name*/36); |
---|
1030 | } else { |
---|
1031 | /* TRANSLATORS: *BEGIN <survey> and *END <survey> should have the |
---|
1032 | * same <survey> if it’s given at all */ |
---|
1033 | compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Survey name doesn’t match BEGIN*/193); |
---|
1034 | } |
---|
1035 | } else { |
---|
1036 | /* TRANSLATORS: Used when a BEGIN command has a survey name, but the |
---|
1037 | * END command omits it, e.g.: |
---|
1038 | * |
---|
1039 | * *begin entrance |
---|
1040 | * 1 2 10.00 178 -01 |
---|
1041 | * *end <--[Message given here] */ |
---|
1042 | compile_diagnostic(DIAG_WARN|DIAG_COL, /*Survey name omitted from END*/194); |
---|
1043 | } |
---|
1044 | parse file_save = file; |
---|
1045 | file.line = begin_lineno; |
---|
1046 | file.lpos = begin_lpos; |
---|
1047 | int word_flag = 0; |
---|
1048 | if (begin_col) { |
---|
1049 | word_flag = DIAG_WORD; |
---|
1050 | fseek(file.fh, begin_lpos + begin_col - 1, SEEK_SET); |
---|
1051 | nextch(); |
---|
1052 | } |
---|
1053 | compile_diagnostic(DIAG_INFO|word_flag, /*Corresponding %s was here*/22, "BEGIN"); |
---|
1054 | file = file_save; |
---|
1055 | set_pos(&fp_save); |
---|
1056 | } |
---|
1057 | } |
---|
1058 | |
---|
1059 | static void |
---|
1060 | cmd_entrance(void) |
---|
1061 | { |
---|
1062 | prefix *pfx = read_prefix(PFX_STATION); |
---|
1063 | pfx->sflags |= BIT(SFLAGS_ENTRANCE); |
---|
1064 | pfx->sflags &= ~BIT(SFLAGS_UNUSED_FIXED_POINT); |
---|
1065 | } |
---|
1066 | |
---|
1067 | static const prefix * first_fix_name = NULL; |
---|
1068 | static const char * first_fix_filename; |
---|
1069 | static unsigned first_fix_line; |
---|
1070 | |
---|
1071 | static void |
---|
1072 | cmd_fix(void) |
---|
1073 | { |
---|
1074 | static prefix *name_omit_already = NULL; |
---|
1075 | static const char * name_omit_already_filename = NULL; |
---|
1076 | static unsigned int name_omit_already_line; |
---|
1077 | PJ_COORD coord; |
---|
1078 | filepos fp_stn, fp; |
---|
1079 | |
---|
1080 | get_pos(&fp_stn); |
---|
1081 | prefix *fix_name = read_prefix(PFX_STATION|PFX_ALLOW_ROOT); |
---|
1082 | |
---|
1083 | get_pos(&fp); |
---|
1084 | get_token_legacy(); |
---|
1085 | bool reference = S_EQ(&uctoken, "REFERENCE"); |
---|
1086 | if (reference) { |
---|
1087 | do_legacy_token_warning(); |
---|
1088 | } else { |
---|
1089 | if (!s_empty(&uctoken)) set_pos(&fp); |
---|
1090 | } |
---|
1091 | |
---|
1092 | skipblanks(); |
---|
1093 | get_pos(&fp); |
---|
1094 | |
---|
1095 | // If `REFERENCE` is specified the coordinates can't be omitted. |
---|
1096 | coord.v[0] = read_numeric(!reference); |
---|
1097 | if (coord.v[0] == HUGE_REAL) { |
---|
1098 | /* If the end of the line isn't blank, read a number after all to |
---|
1099 | * get a more helpful error message */ |
---|
1100 | if (!isEol(ch) && !isComm(ch)) coord.v[0] = read_numeric(false); |
---|
1101 | } |
---|
1102 | if (coord.v[0] == HUGE_REAL) { |
---|
1103 | if (pcs->proj_str || proj_str_out) { |
---|
1104 | compile_diagnostic(DIAG_ERR|DIAG_COL|DIAG_SKIP, /*Coordinates can't be omitted when coordinate system has been specified*/439); |
---|
1105 | return; |
---|
1106 | } |
---|
1107 | |
---|
1108 | if (fix_name == name_omit_already) { |
---|
1109 | compile_diagnostic(DIAG_WARN|DIAG_COL, /*Same station fixed twice with no coordinates*/61); |
---|
1110 | return; |
---|
1111 | } |
---|
1112 | |
---|
1113 | if (name_omit_already) { |
---|
1114 | /* TRANSLATORS: Emitted after second and subsequent "FIX" command |
---|
1115 | * with no coordinates. |
---|
1116 | */ |
---|
1117 | compile_diagnostic_at(DIAG_ERR, |
---|
1118 | name_omit_already_filename, |
---|
1119 | name_omit_already_line, |
---|
1120 | /*Already had FIX command with no coordinates for station “%s”*/441, |
---|
1121 | sprint_prefix(name_omit_already)); |
---|
1122 | } else { |
---|
1123 | /* TRANSLATORS: " *fix a " gives this message: */ |
---|
1124 | compile_diagnostic(DIAG_INFO|DIAG_COL, /*FIX command with no coordinates - fixing at (0,0,0)*/54); |
---|
1125 | |
---|
1126 | name_omit_already = fix_name; |
---|
1127 | name_omit_already_filename = file.filename; |
---|
1128 | name_omit_already_line = file.line; |
---|
1129 | } |
---|
1130 | |
---|
1131 | coord.v[0] = coord.v[1] = coord.v[2] = (real)0.0; |
---|
1132 | } else { |
---|
1133 | coord.v[1] = read_numeric(false); |
---|
1134 | coord.v[2] = read_numeric(false); |
---|
1135 | |
---|
1136 | if (pcs->proj_str && proj_str_out) { |
---|
1137 | PJ *transform = pj_cached; |
---|
1138 | if (!transform) { |
---|
1139 | transform = proj_create_crs_to_crs(PJ_DEFAULT_CTX, |
---|
1140 | pcs->proj_str, |
---|
1141 | proj_str_out, |
---|
1142 | NULL); |
---|
1143 | if (transform) { |
---|
1144 | /* Normalise the output order so x is longitude and y latitude - by |
---|
1145 | * default new PROJ has them switched for EPSG:4326 which just seems |
---|
1146 | * confusing. |
---|
1147 | */ |
---|
1148 | PJ* pj_norm = proj_normalize_for_visualization(PJ_DEFAULT_CTX, |
---|
1149 | transform); |
---|
1150 | proj_destroy(transform); |
---|
1151 | transform = pj_norm; |
---|
1152 | } |
---|
1153 | |
---|
1154 | pj_cached = transform; |
---|
1155 | } |
---|
1156 | |
---|
1157 | if (proj_angular_input(transform, PJ_FWD)) { |
---|
1158 | /* Input coordinate system expects radians. */ |
---|
1159 | coord.v[0] = rad(coord.v[0]); |
---|
1160 | coord.v[1] = rad(coord.v[1]); |
---|
1161 | } |
---|
1162 | |
---|
1163 | coord.v[3] = HUGE_VAL; |
---|
1164 | coord = proj_trans(transform, PJ_FWD, coord); |
---|
1165 | |
---|
1166 | if (coord.v[0] == HUGE_VAL || |
---|
1167 | coord.v[1] == HUGE_VAL || |
---|
1168 | coord.v[2] == HUGE_VAL) { |
---|
1169 | compile_diagnostic(DIAG_ERR|DIAG_FROM(fp), |
---|
1170 | /*Failed to convert coordinates: %s*/436, |
---|
1171 | proj_context_errno_string(PJ_DEFAULT_CTX, |
---|
1172 | proj_errno(transform))); |
---|
1173 | /* Set dummy values which are finite. */ |
---|
1174 | coord.v[0] = coord.v[1] = coord.v[2] = 0; |
---|
1175 | } |
---|
1176 | } else if (pcs->proj_str) { |
---|
1177 | compile_diagnostic(DIAG_ERR, /*The input projection is set but the output projection isn't*/437); |
---|
1178 | } else if (proj_str_out) { |
---|
1179 | compile_diagnostic(DIAG_ERR, /*The output projection is set but the input projection isn't*/438); |
---|
1180 | } |
---|
1181 | |
---|
1182 | get_pos(&fp); |
---|
1183 | real sdx = read_numeric(true); |
---|
1184 | if (sdx <= 0) { |
---|
1185 | set_pos(&fp); |
---|
1186 | compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_NUM, /*Standard deviation must be positive*/48); |
---|
1187 | return; |
---|
1188 | } |
---|
1189 | if (sdx != HUGE_REAL) { |
---|
1190 | real sdy, sdz; |
---|
1191 | real cxy = 0, cyz = 0, czx = 0; |
---|
1192 | get_pos(&fp); |
---|
1193 | sdy = read_numeric(true); |
---|
1194 | if (sdy == HUGE_REAL) { |
---|
1195 | /* only one variance given */ |
---|
1196 | sdy = sdz = sdx; |
---|
1197 | } else { |
---|
1198 | if (sdy <= 0) { |
---|
1199 | set_pos(&fp); |
---|
1200 | compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_NUM, /*Standard deviation must be positive*/48); |
---|
1201 | return; |
---|
1202 | } |
---|
1203 | get_pos(&fp); |
---|
1204 | sdz = read_numeric(true); |
---|
1205 | if (sdz == HUGE_REAL) { |
---|
1206 | /* two variances given - horizontal & vertical */ |
---|
1207 | sdz = sdy; |
---|
1208 | sdy = sdx; |
---|
1209 | } else { |
---|
1210 | if (sdz <= 0) { |
---|
1211 | set_pos(&fp); |
---|
1212 | compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_NUM, /*Standard deviation must be positive*/48); |
---|
1213 | return; |
---|
1214 | } |
---|
1215 | cxy = read_numeric(true); |
---|
1216 | if (cxy != HUGE_REAL) { |
---|
1217 | /* covariances given */ |
---|
1218 | cyz = read_numeric(false); |
---|
1219 | czx = read_numeric(false); |
---|
1220 | } else { |
---|
1221 | cxy = 0; |
---|
1222 | } |
---|
1223 | } |
---|
1224 | } |
---|
1225 | fix_station_with_variance(fix_name, coord.v, |
---|
1226 | sdx * sdx, sdy * sdy, sdz * sdz |
---|
1227 | #ifndef NO_COVARIANCES |
---|
1228 | , cxy, cyz, czx |
---|
1229 | #endif |
---|
1230 | ); |
---|
1231 | |
---|
1232 | if (reference) { |
---|
1233 | // `*fix reference` so suppress "unused fixed point" warning. |
---|
1234 | fix_name->sflags &= ~BIT(SFLAGS_UNUSED_FIXED_POINT); |
---|
1235 | } |
---|
1236 | if (!first_fix_name) { |
---|
1237 | /* We track if we've fixed a station yet, and if so what the name |
---|
1238 | * of the first fix was, so that we can issue an error if the |
---|
1239 | * output coordinate system is set after fixing a station. */ |
---|
1240 | first_fix_name = fix_name; |
---|
1241 | first_fix_filename = file.filename; |
---|
1242 | first_fix_line = file.line; |
---|
1243 | } |
---|
1244 | |
---|
1245 | return; |
---|
1246 | } |
---|
1247 | } |
---|
1248 | |
---|
1249 | if (!first_fix_name) { |
---|
1250 | /* We track if we've fixed a station yet, and if so what the name of the |
---|
1251 | * first fix was, so that we can issue an error if the output coordinate |
---|
1252 | * system is set after fixing a station. */ |
---|
1253 | first_fix_name = fix_name; |
---|
1254 | first_fix_filename = file.filename; |
---|
1255 | first_fix_line = file.line; |
---|
1256 | } |
---|
1257 | |
---|
1258 | int fix_result = fix_station(fix_name, coord.v, fp_stn.offset); |
---|
1259 | if (reference) { |
---|
1260 | // `*fix reference` so suppress "unused fixed point" warning. |
---|
1261 | fix_name->sflags &= ~BIT(SFLAGS_UNUSED_FIXED_POINT); |
---|
1262 | } |
---|
1263 | if (fix_result == 0) { |
---|
1264 | return; |
---|
1265 | } |
---|
1266 | |
---|
1267 | get_pos(&fp); |
---|
1268 | set_pos(&fp_stn); |
---|
1269 | if (fix_result < 0) { |
---|
1270 | compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Station already fixed or equated to a fixed point*/46); |
---|
1271 | } else { |
---|
1272 | /* TRANSLATORS: *fix a 1 2 3 / *fix a 1 2 3 */ |
---|
1273 | compile_diagnostic(DIAG_WARN|DIAG_WORD, /*Station already fixed at the same coordinates*/55); |
---|
1274 | } |
---|
1275 | compile_diagnostic_pfx(DIAG_INFO, fix_name, /*Previously fixed or equated here*/493); |
---|
1276 | set_pos(&fp); |
---|
1277 | } |
---|
1278 | |
---|
1279 | static void |
---|
1280 | cmd_flags(void) |
---|
1281 | { |
---|
1282 | static const sztok flagtab[] = { |
---|
1283 | {"DUPLICATE", FLAGS_DUPLICATE }, |
---|
1284 | {"NOT", FLAGS_NOT }, |
---|
1285 | {"SPLAY", FLAGS_SPLAY }, |
---|
1286 | {"SURFACE", FLAGS_SURFACE }, |
---|
1287 | {NULL, FLAGS_UNKNOWN } |
---|
1288 | }; |
---|
1289 | bool fNot = false; |
---|
1290 | bool fEmpty = true; |
---|
1291 | while (1) { |
---|
1292 | int flag; |
---|
1293 | get_token(); |
---|
1294 | /* If token is empty, it could mean end of line, or maybe |
---|
1295 | * some non-alphanumeric junk which is better reported later */ |
---|
1296 | if (s_empty(&token)) break; |
---|
1297 | |
---|
1298 | fEmpty = false; |
---|
1299 | flag = match_tok(flagtab, TABSIZE(flagtab)); |
---|
1300 | /* treat the second NOT in "NOT NOT" as an unknown flag */ |
---|
1301 | if (flag == FLAGS_UNKNOWN || (fNot && flag == FLAGS_NOT)) { |
---|
1302 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*FLAG “%s” unknown*/68, |
---|
1303 | s_str(&token)); |
---|
1304 | /* Recover from “*FLAGS NOT BOGUS SURFACE” by ignoring "NOT BOGUS" */ |
---|
1305 | fNot = false; |
---|
1306 | } else if (flag == FLAGS_NOT) { |
---|
1307 | fNot = true; |
---|
1308 | } else if (fNot) { |
---|
1309 | pcs->flags &= ~BIT(flag); |
---|
1310 | fNot = false; |
---|
1311 | } else { |
---|
1312 | pcs->flags |= BIT(flag); |
---|
1313 | } |
---|
1314 | } |
---|
1315 | |
---|
1316 | if (fNot) { |
---|
1317 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN, |
---|
1318 | /*Expecting “%s”, “%s”, or “%s”*/188, |
---|
1319 | "DUPLICATE", "SPLAY", "SURFACE"); |
---|
1320 | } else if (fEmpty) { |
---|
1321 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN, |
---|
1322 | /*Expecting “%s”, “%s”, “%s”, or “%s”*/189, |
---|
1323 | "NOT", "DUPLICATE", "SPLAY", "SURFACE"); |
---|
1324 | } |
---|
1325 | } |
---|
1326 | |
---|
1327 | static void |
---|
1328 | cmd_equate(void) |
---|
1329 | { |
---|
1330 | filepos fp; |
---|
1331 | get_pos(&fp); |
---|
1332 | prefix *prev_name = NULL; |
---|
1333 | prefix *name = read_prefix(PFX_STATION|PFX_ALLOW_ROOT|PFX_SUSPECT_TYPO); |
---|
1334 | while (true) { |
---|
1335 | if (!name->stn || !fixed(name->stn)) { |
---|
1336 | // If the station isn't already fixed, make its file:line location |
---|
1337 | // reflect this *equate. |
---|
1338 | name->filename = file.filename; |
---|
1339 | name->line = file.line; |
---|
1340 | name->column = fp.offset - file.lpos; |
---|
1341 | } |
---|
1342 | skipblanks(); |
---|
1343 | if (isEol(ch) || isComm(ch)) { |
---|
1344 | if (prev_name == NULL) { |
---|
1345 | /* E.g. *equate entrance.6 */ |
---|
1346 | set_pos(&fp); |
---|
1347 | /* TRANSLATORS: EQUATE is a command name, so shouldn’t be |
---|
1348 | * translated. |
---|
1349 | * |
---|
1350 | * Here "station" is a survey station, not a train station. |
---|
1351 | */ |
---|
1352 | compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_WORD, /*Only one station in EQUATE command*/33); |
---|
1353 | } |
---|
1354 | return; |
---|
1355 | } |
---|
1356 | |
---|
1357 | prev_name = name; |
---|
1358 | name = read_prefix(PFX_STATION|PFX_ALLOW_ROOT|PFX_SUSPECT_TYPO); |
---|
1359 | process_equate(prev_name, name); |
---|
1360 | } |
---|
1361 | } |
---|
1362 | |
---|
1363 | static void |
---|
1364 | report_missing_export(prefix *pfx, int depth) |
---|
1365 | { |
---|
1366 | char *s; |
---|
1367 | const char *p; |
---|
1368 | prefix *survey = pfx; |
---|
1369 | int i; |
---|
1370 | for (i = depth + 1; i; i--) { |
---|
1371 | survey = survey->up; |
---|
1372 | SVX_ASSERT(survey); |
---|
1373 | } |
---|
1374 | s = osstrdup(sprint_prefix(survey)); |
---|
1375 | p = sprint_prefix(pfx); |
---|
1376 | if (survey->filename) { |
---|
1377 | /* TRANSLATORS: A station must be exported out of each level it is in, so |
---|
1378 | * this would give "Station “\outer.inner.1” not exported from survey |
---|
1379 | * “\outer”)": |
---|
1380 | * |
---|
1381 | * *equate entrance outer.inner.1 |
---|
1382 | * *begin outer |
---|
1383 | * *begin inner |
---|
1384 | * *export 1 |
---|
1385 | * 1 2 1.23 045 -6 |
---|
1386 | * *end inner |
---|
1387 | * *end outer |
---|
1388 | * |
---|
1389 | * Here "survey" is a "cave map" rather than list of questions - it should be |
---|
1390 | * translated to the terminology that cavers using the language would use. |
---|
1391 | */ |
---|
1392 | compile_diagnostic_pfx(DIAG_ERR, survey, |
---|
1393 | /*Station “%s” not exported from survey “%s”*/26, p, s); |
---|
1394 | } else { |
---|
1395 | compile_diagnostic(DIAG_ERR, /*Station “%s” not exported from survey “%s”*/26, p, s); |
---|
1396 | } |
---|
1397 | free(s); |
---|
1398 | } |
---|
1399 | |
---|
1400 | static void |
---|
1401 | cmd_export(void) |
---|
1402 | { |
---|
1403 | prefix *pfx; |
---|
1404 | |
---|
1405 | fExportUsed = true; |
---|
1406 | do { |
---|
1407 | int depth = 0; |
---|
1408 | pfx = read_prefix(PFX_STATION); |
---|
1409 | { |
---|
1410 | prefix *p = pfx; |
---|
1411 | while (p != NULL && p != pcs->Prefix) { |
---|
1412 | depth++; |
---|
1413 | p = p->up; |
---|
1414 | } |
---|
1415 | /* Something like: *export \foo, but we've excluded use of root */ |
---|
1416 | SVX_ASSERT(p); |
---|
1417 | } |
---|
1418 | /* *export \ or similar bogus stuff */ |
---|
1419 | SVX_ASSERT(depth); |
---|
1420 | #if 0 |
---|
1421 | printf("C min %d max %d depth %d pfx %s\n", |
---|
1422 | pfx->min_export, pfx->max_export, depth, sprint_prefix(pfx)); |
---|
1423 | #endif |
---|
1424 | if (pfx->min_export == 0) { |
---|
1425 | /* not encountered *export for this name before */ |
---|
1426 | if (pfx->max_export > depth) report_missing_export(pfx, depth); |
---|
1427 | pfx->min_export = pfx->max_export = depth; |
---|
1428 | } else if (pfx->min_export != USHRT_MAX) { |
---|
1429 | /* FIXME: what to do if a station is marked for inferred exports |
---|
1430 | * but is then explicitly exported? Currently we just ignore the |
---|
1431 | * explicit export... */ |
---|
1432 | if (pfx->min_export - 1 > depth) { |
---|
1433 | report_missing_export(pfx, depth); |
---|
1434 | } else if (pfx->min_export - 1 < depth) { |
---|
1435 | /* TRANSLATORS: Here "station" is a survey station, not a train station. |
---|
1436 | * |
---|
1437 | * Exporting a station twice gives this error: |
---|
1438 | * |
---|
1439 | * *begin example |
---|
1440 | * *export 1 |
---|
1441 | * *export 1 |
---|
1442 | * 1 2 1.24 045 -6 |
---|
1443 | * *end example */ |
---|
1444 | compile_diagnostic(DIAG_ERR, /*Station “%s” already exported*/66, |
---|
1445 | sprint_prefix(pfx)); |
---|
1446 | } |
---|
1447 | pfx->min_export = depth; |
---|
1448 | } |
---|
1449 | skipblanks(); |
---|
1450 | } while (!isEol(ch) && !isComm(ch)); |
---|
1451 | } |
---|
1452 | |
---|
1453 | static void |
---|
1454 | cmd_data(void) |
---|
1455 | { |
---|
1456 | static const sztok dtab[] = { |
---|
1457 | {"ALTITUDE", Dz }, |
---|
1458 | {"BACKBEARING", BackComp }, |
---|
1459 | {"BACKCLINO", BackClino }, /* alternative name */ |
---|
1460 | {"BACKCOMPASS", BackComp }, /* alternative name */ |
---|
1461 | {"BACKGRADIENT", BackClino }, |
---|
1462 | {"BACKLENGTH", BackTape }, |
---|
1463 | {"BACKTAPE", BackTape }, /* alternative name */ |
---|
1464 | {"BEARING", Comp }, |
---|
1465 | {"CEILING", Up }, /* alternative name */ |
---|
1466 | {"CLINO", Clino }, /* alternative name */ |
---|
1467 | {"COMPASS", Comp }, /* alternative name */ |
---|
1468 | {"COUNT", Count }, /* FrCount&ToCount in multiline */ |
---|
1469 | {"DEPTH", Depth }, /* FrDepth&ToDepth in multiline */ |
---|
1470 | {"DEPTHCHANGE", DepthChange }, |
---|
1471 | {"DIRECTION", Dir }, |
---|
1472 | {"DOWN", Down }, |
---|
1473 | {"DX", Dx }, |
---|
1474 | {"DY", Dy }, |
---|
1475 | {"DZ", Dz }, |
---|
1476 | {"EASTING", Dx }, |
---|
1477 | {"FLOOR", Down }, /* alternative name */ |
---|
1478 | {"FROM", Fr }, |
---|
1479 | {"FROMCOUNT", FrCount }, |
---|
1480 | {"FROMDEPTH", FrDepth }, |
---|
1481 | {"GRADIENT", Clino }, |
---|
1482 | {"IGNORE", Ignore }, |
---|
1483 | {"IGNOREALL", IgnoreAll }, |
---|
1484 | {"LEFT", Left }, |
---|
1485 | {"LENGTH", Tape }, |
---|
1486 | {"NEWLINE", Newline }, |
---|
1487 | {"NORTHING", Dy }, |
---|
1488 | {"RIGHT", Right }, |
---|
1489 | {"STATION", Station }, /* Fr&To in multiline */ |
---|
1490 | {"TAPE", Tape }, /* alternative name */ |
---|
1491 | {"TO", To }, |
---|
1492 | {"TOCOUNT", ToCount }, |
---|
1493 | {"TODEPTH", ToDepth }, |
---|
1494 | {"UP", Up }, |
---|
1495 | {NULL, End } |
---|
1496 | }; |
---|
1497 | |
---|
1498 | #define MASK_stns BIT(Fr) | BIT(To) | BIT(Station) |
---|
1499 | #define MASK_tape BIT(Tape) | BIT(BackTape) | BIT(FrCount) | BIT(ToCount) | BIT(Count) |
---|
1500 | #define MASK_dpth BIT(FrDepth) | BIT(ToDepth) | BIT(Depth) | BIT(DepthChange) |
---|
1501 | #define MASK_comp BIT(Comp) | BIT(BackComp) |
---|
1502 | #define MASK_clin BIT(Clino) | BIT(BackClino) |
---|
1503 | |
---|
1504 | #define MASK_NORMAL MASK_stns | BIT(Dir) | MASK_tape | MASK_comp | MASK_clin |
---|
1505 | #define MASK_DIVING MASK_NORMAL | MASK_dpth |
---|
1506 | #define MASK_CARTESIAN MASK_stns | BIT(Dx) | BIT(Dy) | BIT(Dz) |
---|
1507 | #define MASK_CYLPOLAR MASK_stns | BIT(Dir) | MASK_tape | MASK_comp | MASK_dpth |
---|
1508 | #define MASK_NOSURVEY MASK_stns |
---|
1509 | #define MASK_PASSAGE BIT(Station) | BIT(Left) | BIT(Right) | BIT(Up) | BIT(Down) |
---|
1510 | #define MASK_IGNORE 0 // No readings in this style. |
---|
1511 | |
---|
1512 | // readings which may be given for each style (index is STYLE_*) |
---|
1513 | static const unsigned long mask[] = { |
---|
1514 | MASK_NORMAL, MASK_DIVING, MASK_CARTESIAN, MASK_CYLPOLAR, MASK_NOSURVEY, |
---|
1515 | MASK_PASSAGE, MASK_IGNORE |
---|
1516 | }; |
---|
1517 | |
---|
1518 | // readings which may be omitted for each style (index is STYLE_*) |
---|
1519 | static const unsigned long mask_optional[] = { |
---|
1520 | BIT(Dir) | BIT(Clino) | BIT(BackClino), |
---|
1521 | BIT(Dir) | BIT(Clino) | BIT(BackClino), |
---|
1522 | 0, |
---|
1523 | BIT(Dir), |
---|
1524 | 0, |
---|
1525 | 0, /* BIT(Left) | BIT(Right) | BIT(Up) | BIT(Down), */ |
---|
1526 | 0 |
---|
1527 | }; |
---|
1528 | |
---|
1529 | /* all valid readings */ |
---|
1530 | static const unsigned long mask_all[] = { |
---|
1531 | MASK_NORMAL | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End), |
---|
1532 | MASK_DIVING | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End), |
---|
1533 | MASK_CARTESIAN | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End), |
---|
1534 | MASK_CYLPOLAR | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End), |
---|
1535 | MASK_NOSURVEY | BIT(Ignore) | BIT(IgnoreAll) | BIT(End), |
---|
1536 | MASK_PASSAGE | BIT(Ignore) | BIT(IgnoreAll) | BIT(End), |
---|
1537 | MASK_IGNORE |
---|
1538 | }; |
---|
1539 | #define STYLE_DEFAULT -2 |
---|
1540 | #define STYLE_UNKNOWN -1 |
---|
1541 | |
---|
1542 | static const sztok styletab[] = { |
---|
1543 | {"CARTESIAN", STYLE_CARTESIAN }, |
---|
1544 | {"CYLPOLAR", STYLE_CYLPOLAR }, |
---|
1545 | {"DEFAULT", STYLE_DEFAULT }, |
---|
1546 | {"DIVING", STYLE_DIVING }, |
---|
1547 | {"IGNORE", STYLE_IGNORE }, |
---|
1548 | {"NORMAL", STYLE_NORMAL }, |
---|
1549 | {"NOSURVEY", STYLE_NOSURVEY }, |
---|
1550 | {"PASSAGE", STYLE_PASSAGE }, |
---|
1551 | {"TOPOFIL", STYLE_NORMAL }, |
---|
1552 | {NULL, STYLE_UNKNOWN } |
---|
1553 | }; |
---|
1554 | |
---|
1555 | #define m_multi (BIT(Station) | BIT(Count) | BIT(Depth)) |
---|
1556 | |
---|
1557 | int style, k = 0; |
---|
1558 | reading d; |
---|
1559 | unsigned long mUsed = 0; |
---|
1560 | int old_style = pcs->style; |
---|
1561 | |
---|
1562 | /* after a bad *data command ignore survey data until the next |
---|
1563 | * *data command to avoid an avalanche of errors */ |
---|
1564 | pcs->recorded_style = pcs->style = STYLE_IGNORE; |
---|
1565 | |
---|
1566 | get_token(); |
---|
1567 | style = match_tok(styletab, TABSIZE(styletab)); |
---|
1568 | |
---|
1569 | if (style == STYLE_DEFAULT) { |
---|
1570 | default_style(pcs); |
---|
1571 | return; |
---|
1572 | } |
---|
1573 | |
---|
1574 | if (style == STYLE_IGNORE) { |
---|
1575 | return; |
---|
1576 | } |
---|
1577 | |
---|
1578 | if (style == STYLE_UNKNOWN) { |
---|
1579 | if (s_empty(&token)) { |
---|
1580 | /* "*data" without arguments reinitialises the current style - useful |
---|
1581 | * when using *data passage as it provides a way to break the passage |
---|
1582 | * tube without having to repeat the full *data passage command. |
---|
1583 | */ |
---|
1584 | pcs->recorded_style = pcs->style = style = old_style; |
---|
1585 | goto reinit_style; |
---|
1586 | } |
---|
1587 | /* TRANSLATORS: e.g. trying to refer to an invalid FNORD data style */ |
---|
1588 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Data style “%s” unknown*/65, s_str(&token)); |
---|
1589 | return; |
---|
1590 | } |
---|
1591 | |
---|
1592 | skipblanks(); |
---|
1593 | #ifndef NO_DEPRECATED |
---|
1594 | /* Olde syntax had optional field for survey grade, so allow an omit |
---|
1595 | * but issue a warning about it */ |
---|
1596 | if (isOmit(ch)) { |
---|
1597 | static int data_depr_count = 0; |
---|
1598 | if (data_depr_count < 5) { |
---|
1599 | compile_diagnostic(DIAG_WARN|DIAG_TOKEN, /*“*data %s %c …” is deprecated - use “*data %s …” instead*/104, |
---|
1600 | s_str(&token), ch, s_str(&token)); |
---|
1601 | if (++data_depr_count == 5) |
---|
1602 | compile_diagnostic(DIAG_INFO, /*Further uses of this deprecated feature will not be reported*/95); |
---|
1603 | } |
---|
1604 | nextch(); |
---|
1605 | } |
---|
1606 | #endif |
---|
1607 | |
---|
1608 | int kMac = 6; /* minimum for NORMAL style */ |
---|
1609 | reading *new_order = osmalloc(kMac * sizeof(reading)); |
---|
1610 | char *style_name = s_steal(&token); |
---|
1611 | do { |
---|
1612 | filepos fp; |
---|
1613 | get_pos(&fp); |
---|
1614 | get_token(); |
---|
1615 | d = match_tok(dtab, TABSIZE(dtab)); |
---|
1616 | if (d == End && !s_empty(&token)) { |
---|
1617 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, |
---|
1618 | /*Reading “%s” not allowed in data style “%s”*/63, |
---|
1619 | s_str(&token), style_name); |
---|
1620 | free(style_name); |
---|
1621 | free(new_order); |
---|
1622 | return; |
---|
1623 | } |
---|
1624 | |
---|
1625 | /* only token allowed after IGNOREALL is NEWLINE */ |
---|
1626 | if (k && new_order[k - 1] == IgnoreAll && d != Newline) { |
---|
1627 | set_pos(&fp); |
---|
1628 | break; |
---|
1629 | } |
---|
1630 | /* Note: an unknown token is reported as trailing garbage */ |
---|
1631 | if (!TSTBIT(mask_all[style], d)) { |
---|
1632 | /* TRANSLATORS: a data "style" is something like NORMAL, DIVING, etc. |
---|
1633 | * a "reading" is one of FROM, TO, TAPE, COMPASS, CLINO for NORMAL |
---|
1634 | * style. Neither "style" nor "reading" is a keyword in the program. |
---|
1635 | * |
---|
1636 | * This error complains about a "DEPTH" gauge reading in "NORMAL" |
---|
1637 | * style, for example. |
---|
1638 | */ |
---|
1639 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, |
---|
1640 | /*Reading “%s” not allowed in data style “%s”*/63, |
---|
1641 | s_str(&token), style_name); |
---|
1642 | free(style_name); |
---|
1643 | free(new_order); |
---|
1644 | return; |
---|
1645 | } |
---|
1646 | if (TSTBIT(mUsed, Newline) && TSTBIT(m_multi, d)) { |
---|
1647 | /* TRANSLATORS: caused by e.g. |
---|
1648 | * |
---|
1649 | * *data diving station newline depth tape compass |
---|
1650 | * |
---|
1651 | * ("depth" needs to occur before "newline"). */ |
---|
1652 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, |
---|
1653 | /*Reading “%s” must occur before NEWLINE*/225, s_str(&token)); |
---|
1654 | free(style_name); |
---|
1655 | free(new_order); |
---|
1656 | return; |
---|
1657 | } |
---|
1658 | /* Check for duplicates unless it's a special reading: |
---|
1659 | * IGNOREALL,IGNORE (duplicates allowed) ; END (not possible) |
---|
1660 | */ |
---|
1661 | if (!((BIT(Ignore) | BIT(End) | BIT(IgnoreAll)) & BIT(d))) { |
---|
1662 | if (TSTBIT(mUsed, d)) { |
---|
1663 | /* TRANSLATORS: complains about a situation like trying to define |
---|
1664 | * two from stations per leg */ |
---|
1665 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Duplicate reading “%s”*/67, s_str(&token)); |
---|
1666 | free(style_name); |
---|
1667 | free(new_order); |
---|
1668 | return; |
---|
1669 | } else { |
---|
1670 | /* Check for previously listed readings which are incompatible |
---|
1671 | * with this one - e.g. Count vs FrCount */ |
---|
1672 | bool fBad = false; |
---|
1673 | switch (d) { |
---|
1674 | case Station: |
---|
1675 | if (mUsed & (BIT(Fr) | BIT(To))) fBad = true; |
---|
1676 | break; |
---|
1677 | case Fr: case To: |
---|
1678 | if (TSTBIT(mUsed, Station)) fBad = true; |
---|
1679 | break; |
---|
1680 | case Count: |
---|
1681 | if (mUsed & (BIT(FrCount) | BIT(ToCount) | BIT(Tape))) |
---|
1682 | fBad = true; |
---|
1683 | break; |
---|
1684 | case FrCount: case ToCount: |
---|
1685 | if (mUsed & (BIT(Count) | BIT(Tape))) |
---|
1686 | fBad = true; |
---|
1687 | break; |
---|
1688 | case Depth: |
---|
1689 | if (mUsed & (BIT(FrDepth) | BIT(ToDepth) | BIT(DepthChange))) |
---|
1690 | fBad = true; |
---|
1691 | break; |
---|
1692 | case FrDepth: case ToDepth: |
---|
1693 | if (mUsed & (BIT(Depth) | BIT(DepthChange))) fBad = true; |
---|
1694 | break; |
---|
1695 | case DepthChange: |
---|
1696 | if (mUsed & (BIT(FrDepth) | BIT(ToDepth) | BIT(Depth))) |
---|
1697 | fBad = true; |
---|
1698 | break; |
---|
1699 | case Newline: |
---|
1700 | if (mUsed & ~m_multi) { |
---|
1701 | /* TRANSLATORS: e.g. |
---|
1702 | * |
---|
1703 | * *data normal from to tape newline compass clino */ |
---|
1704 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*NEWLINE can only be preceded by STATION, DEPTH, and COUNT*/226); |
---|
1705 | free(style_name); |
---|
1706 | free(new_order); |
---|
1707 | return; |
---|
1708 | } |
---|
1709 | if (k == 0) { |
---|
1710 | /* TRANSLATORS: error from: |
---|
1711 | * |
---|
1712 | * *data normal newline from to tape compass clino */ |
---|
1713 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*NEWLINE can’t be the first reading*/222); |
---|
1714 | free(style_name); |
---|
1715 | free(new_order); |
---|
1716 | return; |
---|
1717 | } |
---|
1718 | break; |
---|
1719 | default: /* avoid compiler warnings about unhandled enums */ |
---|
1720 | break; |
---|
1721 | } |
---|
1722 | if (fBad) { |
---|
1723 | /* Not entirely happy with phrasing this... */ |
---|
1724 | /* TRANSLATORS: This is an error from the *DATA command. It |
---|
1725 | * means that a reading (which will appear where %s is isn't |
---|
1726 | * valid as the list of readings has already included the same |
---|
1727 | * reading, or an equivalent one (e.g. you can't have both |
---|
1728 | * DEPTH and DEPTHCHANGE together). */ |
---|
1729 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Reading “%s” duplicates previous reading(s)*/77, |
---|
1730 | s_str(&token)); |
---|
1731 | free(style_name); |
---|
1732 | free(new_order); |
---|
1733 | return; |
---|
1734 | } |
---|
1735 | mUsed |= BIT(d); /* used to catch duplicates */ |
---|
1736 | } |
---|
1737 | } |
---|
1738 | if (k && new_order[k - 1] == IgnoreAll) { |
---|
1739 | SVX_ASSERT(d == Newline); |
---|
1740 | k--; |
---|
1741 | d = IgnoreAllAndNewLine; |
---|
1742 | } |
---|
1743 | if (k >= kMac) { |
---|
1744 | kMac = kMac * 2; |
---|
1745 | new_order = osrealloc(new_order, kMac * sizeof(reading)); |
---|
1746 | } |
---|
1747 | new_order[k++] = d; |
---|
1748 | } while (d != End); |
---|
1749 | |
---|
1750 | if (k >= 2 && new_order[k - 2] == Newline) { |
---|
1751 | /* TRANSLATORS: error from: |
---|
1752 | * |
---|
1753 | * *data normal from to tape compass clino newline */ |
---|
1754 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*NEWLINE can’t be the last reading*/223); |
---|
1755 | free(style_name); |
---|
1756 | free(new_order); |
---|
1757 | return; |
---|
1758 | } |
---|
1759 | |
---|
1760 | if (style == STYLE_NOSURVEY) { |
---|
1761 | if (TSTBIT(mUsed, Station)) { |
---|
1762 | if (k >= kMac) { |
---|
1763 | kMac = kMac * 2; |
---|
1764 | new_order = osrealloc(new_order, kMac * sizeof(reading)); |
---|
1765 | } |
---|
1766 | new_order[k - 1] = Newline; |
---|
1767 | new_order[k++] = End; |
---|
1768 | } |
---|
1769 | } else if (style == STYLE_PASSAGE) { |
---|
1770 | /* Station doesn't mean "multiline" for STYLE_PASSAGE. */ |
---|
1771 | } else if (!TSTBIT(mUsed, Newline) && (m_multi & mUsed)) { |
---|
1772 | /* TRANSLATORS: Error given by something like: |
---|
1773 | * |
---|
1774 | * *data normal station tape compass clino |
---|
1775 | * |
---|
1776 | * ("station" signifies interleaved data). */ |
---|
1777 | compile_diagnostic(DIAG_ERR|DIAG_SKIP, /*Interleaved readings, but no NEWLINE*/224); |
---|
1778 | free(style_name); |
---|
1779 | free(new_order); |
---|
1780 | return; |
---|
1781 | } |
---|
1782 | |
---|
1783 | #if 0 |
---|
1784 | printf("mUsed = 0x%x\n", mUsed); |
---|
1785 | #endif |
---|
1786 | |
---|
1787 | /* Check the supplied readings form a sufficient set. */ |
---|
1788 | if (style != STYLE_PASSAGE) { |
---|
1789 | if ((mUsed & (BIT(Fr) | BIT(To))) == (BIT(Fr) | BIT(To))) |
---|
1790 | mUsed |= BIT(Station); |
---|
1791 | else if (TSTBIT(mUsed, Station)) |
---|
1792 | mUsed |= BIT(Fr) | BIT(To); |
---|
1793 | } |
---|
1794 | |
---|
1795 | if (mUsed & (BIT(Comp) | BIT(BackComp))) |
---|
1796 | mUsed |= BIT(Comp) | BIT(BackComp); |
---|
1797 | |
---|
1798 | if (mUsed & (BIT(Clino) | BIT(BackClino))) |
---|
1799 | mUsed |= BIT(Clino) | BIT(BackClino); |
---|
1800 | |
---|
1801 | if ((mUsed & (BIT(FrDepth) | BIT(ToDepth))) == (BIT(FrDepth) | BIT(ToDepth))) |
---|
1802 | mUsed |= BIT(Depth) | BIT(DepthChange); |
---|
1803 | else if (mUsed & (BIT(Depth) | BIT(DepthChange))) |
---|
1804 | mUsed |= BIT(FrDepth) | BIT(ToDepth) | BIT(Depth) | BIT(DepthChange); |
---|
1805 | |
---|
1806 | if ((mUsed & (BIT(FrCount) | BIT(ToCount))) == (BIT(FrCount) | BIT(ToCount))) |
---|
1807 | mUsed |= BIT(Count) | BIT(Tape) | BIT(BackTape); |
---|
1808 | else if (mUsed & (BIT(Count) | BIT(Tape) | BIT(BackTape))) |
---|
1809 | mUsed |= BIT(FrCount) | BIT(ToCount) | BIT(Count) | BIT(Tape) | BIT(BackTape); |
---|
1810 | |
---|
1811 | #if 0 |
---|
1812 | printf("mUsed = 0x%x, opt = 0x%x, mask = 0x%x\n", mUsed, |
---|
1813 | mask_optional[style], mask[style]); |
---|
1814 | #endif |
---|
1815 | |
---|
1816 | if (((mUsed &~ BIT(Newline)) | mask_optional[style]) != mask[style]) { |
---|
1817 | /* Test should only fail with too few bits set, not too many */ |
---|
1818 | SVX_ASSERT((((mUsed &~ BIT(Newline)) | mask_optional[style]) |
---|
1819 | &~ mask[style]) == 0); |
---|
1820 | /* TRANSLATORS: i.e. not enough readings for the style. */ |
---|
1821 | compile_diagnostic(DIAG_ERR|DIAG_SKIP, /*Too few readings for data style “%s”*/64, style_name); |
---|
1822 | free(style_name); |
---|
1823 | free(new_order); |
---|
1824 | return; |
---|
1825 | } |
---|
1826 | |
---|
1827 | /* don't free default ordering or ordering used by parent */ |
---|
1828 | if (pcs->ordering != default_order && |
---|
1829 | !(pcs->next && pcs->next->ordering == pcs->ordering)) |
---|
1830 | free((reading*)pcs->ordering); |
---|
1831 | |
---|
1832 | pcs->recorded_style = pcs->style = style; |
---|
1833 | pcs->ordering = new_order; |
---|
1834 | |
---|
1835 | free(style_name); |
---|
1836 | |
---|
1837 | reinit_style: |
---|
1838 | if (style == STYLE_PASSAGE) { |
---|
1839 | lrudlist * new_psg = osnew(lrudlist); |
---|
1840 | new_psg->tube = NULL; |
---|
1841 | new_psg->next = model; |
---|
1842 | model = new_psg; |
---|
1843 | next_lrud = &(new_psg->tube); |
---|
1844 | } |
---|
1845 | } |
---|
1846 | |
---|
1847 | static void |
---|
1848 | cmd_units(void) |
---|
1849 | { |
---|
1850 | int units, quantity; |
---|
1851 | unsigned long qmask; |
---|
1852 | unsigned long m; /* mask with bit x set to indicate quantity x specified */ |
---|
1853 | real factor; |
---|
1854 | filepos fp; |
---|
1855 | |
---|
1856 | qmask = get_qlist(BIT(Q_POS)|BIT(Q_PLUMB)|BIT(Q_LEVEL)); |
---|
1857 | |
---|
1858 | if (!qmask) return; |
---|
1859 | if (qmask == BIT(Q_DEFAULT)) { |
---|
1860 | default_units(pcs); |
---|
1861 | return; |
---|
1862 | } |
---|
1863 | |
---|
1864 | get_pos(&fp); |
---|
1865 | factor = read_numeric(true); |
---|
1866 | if (factor == 0.0) { |
---|
1867 | set_pos(&fp); |
---|
1868 | /* TRANSLATORS: error message given by "*units tape 0 feet" - it’s |
---|
1869 | * meaningless to say your tape is marked in "0 feet" (but you might |
---|
1870 | * measure distance by counting knots on a diving line, and tie them |
---|
1871 | * every "2 feet"). */ |
---|
1872 | compile_diagnostic(DIAG_ERR|DIAG_WORD, /**UNITS factor must be non-zero*/200); |
---|
1873 | skipline(); |
---|
1874 | return; |
---|
1875 | } |
---|
1876 | |
---|
1877 | units = get_units(qmask, true); |
---|
1878 | if (units == UNITS_NULL) return; |
---|
1879 | if (TSTBIT(qmask, Q_GRADIENT)) |
---|
1880 | pcs->f_clino_percent = (units == UNITS_PERCENT); |
---|
1881 | if (TSTBIT(qmask, Q_BACKGRADIENT)) |
---|
1882 | pcs->f_backclino_percent = (units == UNITS_PERCENT); |
---|
1883 | |
---|
1884 | if (TSTBIT(qmask, Q_BEARING)) { |
---|
1885 | pcs->f_bearing_quadrants = (units == UNITS_QUADRANTS); |
---|
1886 | } |
---|
1887 | if (TSTBIT(qmask, Q_BACKBEARING)) { |
---|
1888 | pcs->f_backbearing_quadrants = (units == UNITS_QUADRANTS); |
---|
1889 | } |
---|
1890 | |
---|
1891 | if (factor == HUGE_REAL) { |
---|
1892 | factor = factor_tab[units]; |
---|
1893 | } else { |
---|
1894 | factor *= factor_tab[units]; |
---|
1895 | } |
---|
1896 | |
---|
1897 | for (quantity = 0, m = BIT(quantity); m <= qmask; quantity++, m <<= 1) |
---|
1898 | if (qmask & m) pcs->units[quantity] = factor; |
---|
1899 | } |
---|
1900 | |
---|
1901 | static void |
---|
1902 | cmd_calibrate(void) |
---|
1903 | { |
---|
1904 | real sc, z; |
---|
1905 | unsigned long qmask, m; |
---|
1906 | int quantity; |
---|
1907 | filepos fp; |
---|
1908 | |
---|
1909 | qmask = get_qlist(BIT(Q_POS)|BIT(Q_PLUMB)|BIT(Q_LEVEL)); |
---|
1910 | if (!qmask) return; /* error already reported */ |
---|
1911 | |
---|
1912 | if (qmask == BIT(Q_DEFAULT)) { |
---|
1913 | default_calib(pcs); |
---|
1914 | return; |
---|
1915 | } |
---|
1916 | |
---|
1917 | if (((qmask & LEN_QMASK)) && ((qmask & ANG_QMASK))) { |
---|
1918 | /* TRANSLATORS: e.g. |
---|
1919 | * |
---|
1920 | * *calibrate tape compass 1 1 |
---|
1921 | */ |
---|
1922 | compile_diagnostic(DIAG_ERR|DIAG_SKIP, /*Can’t calibrate angular and length quantities together*/227); |
---|
1923 | return; |
---|
1924 | } |
---|
1925 | |
---|
1926 | z = read_numeric(false); |
---|
1927 | get_pos(&fp); |
---|
1928 | sc = read_numeric(true); |
---|
1929 | if (sc == HUGE_REAL) { |
---|
1930 | if (isalpha(ch)) { |
---|
1931 | int units = get_units(qmask, false); |
---|
1932 | if (units == UNITS_NULL) { |
---|
1933 | return; |
---|
1934 | } |
---|
1935 | z *= factor_tab[units]; |
---|
1936 | sc = read_numeric(true); |
---|
1937 | if (sc == HUGE_REAL) { |
---|
1938 | sc = (real)1.0; |
---|
1939 | } else { |
---|
1940 | /* Adjustment applied is: (reading - z) * sc |
---|
1941 | * We want: reading * sc - z |
---|
1942 | * So divide z by sc so the applied adjustment does what we want. |
---|
1943 | */ |
---|
1944 | z /= sc; |
---|
1945 | } |
---|
1946 | } else { |
---|
1947 | sc = (real)1.0; |
---|
1948 | } |
---|
1949 | } |
---|
1950 | |
---|
1951 | if (sc == HUGE_REAL) sc = (real)1.0; |
---|
1952 | /* check for declination scale */ |
---|
1953 | if (TSTBIT(qmask, Q_DECLINATION) && sc != 1.0) { |
---|
1954 | set_pos(&fp); |
---|
1955 | /* TRANSLATORS: DECLINATION is a built-in keyword, so best not to |
---|
1956 | * translate */ |
---|
1957 | compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Scale factor must be 1.0 for DECLINATION*/40); |
---|
1958 | skipline(); |
---|
1959 | return; |
---|
1960 | } |
---|
1961 | if (sc == 0.0) { |
---|
1962 | set_pos(&fp); |
---|
1963 | /* TRANSLATORS: If the scale factor for an instrument is zero, then any |
---|
1964 | * reading would be mapped to zero, which doesn't make sense. */ |
---|
1965 | compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Scale factor must be non-zero*/391); |
---|
1966 | skipline(); |
---|
1967 | return; |
---|
1968 | } |
---|
1969 | for (quantity = 0, m = BIT(quantity); m <= qmask; quantity++, m <<= 1) { |
---|
1970 | if (qmask & m) { |
---|
1971 | pcs->z[quantity] = pcs->units[quantity] * z; |
---|
1972 | pcs->sc[quantity] = sc; |
---|
1973 | } |
---|
1974 | } |
---|
1975 | } |
---|
1976 | |
---|
1977 | static const sztok north_tab[] = { |
---|
1978 | { "GRID", GRID_NORTH }, |
---|
1979 | { "MAGNETIC", MAGNETIC_NORTH }, |
---|
1980 | { "TRUE", TRUE_NORTH }, |
---|
1981 | { NULL, -1 } |
---|
1982 | }; |
---|
1983 | |
---|
1984 | static void |
---|
1985 | cmd_cartesian(void) |
---|
1986 | { |
---|
1987 | get_token(); |
---|
1988 | int north = match_tok(north_tab, TABSIZE(north_tab)); |
---|
1989 | if (north < 0) { |
---|
1990 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, |
---|
1991 | /*Expecting “%s”, “%s”, or “%s”*/188, |
---|
1992 | "GRID", "MAGNETIC", "TRUE"); |
---|
1993 | return; |
---|
1994 | } |
---|
1995 | pcs->cartesian_north = north; |
---|
1996 | pcs->cartesian_rotation = 0.0; |
---|
1997 | |
---|
1998 | skipblanks(); |
---|
1999 | if (!isEol(ch) && !isComm(ch)) { |
---|
2000 | real rotation = read_numeric(false); |
---|
2001 | // Accept the same units as *declination does. |
---|
2002 | int units = get_units(BIT(Q_DECLINATION), false); |
---|
2003 | if (units == UNITS_NULL) { |
---|
2004 | return; |
---|
2005 | } |
---|
2006 | pcs->cartesian_rotation = rotation * factor_tab[units]; |
---|
2007 | } |
---|
2008 | } |
---|
2009 | |
---|
2010 | static void |
---|
2011 | cmd_declination(void) |
---|
2012 | { |
---|
2013 | real v = read_numeric(true); |
---|
2014 | if (v == HUGE_REAL) { |
---|
2015 | get_token_legacy_no_blanks(); |
---|
2016 | if (!S_EQ(&uctoken, "AUTO")) { |
---|
2017 | compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_COL, /*Expected number or “AUTO”*/309); |
---|
2018 | return; |
---|
2019 | } |
---|
2020 | do_legacy_token_warning(); |
---|
2021 | if (!pcs->proj_str) { |
---|
2022 | // TRANSLATORS: %s is replaced by the command that requires it, e.g. |
---|
2023 | // *DECLINATION AUTO |
---|
2024 | compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_TOKEN, |
---|
2025 | /*Input coordinate system must be specified for “%s”*/301, |
---|
2026 | "*DECLINATION AUTO"); |
---|
2027 | return; |
---|
2028 | } |
---|
2029 | |
---|
2030 | /* *declination auto X Y Z */ |
---|
2031 | filepos fp; |
---|
2032 | get_pos(&fp); |
---|
2033 | real x = read_numeric(false); |
---|
2034 | real y = read_numeric(false); |
---|
2035 | real z = read_numeric(false); |
---|
2036 | set_declination_location(x, y, z, pcs->proj_str, &fp); |
---|
2037 | } else { |
---|
2038 | /* *declination D UNITS */ |
---|
2039 | int units = get_units(BIT(Q_DECLINATION), false); |
---|
2040 | if (units == UNITS_NULL) { |
---|
2041 | return; |
---|
2042 | } |
---|
2043 | pcs->z[Q_DECLINATION] = -v * factor_tab[units]; |
---|
2044 | pcs->convergence = 0; |
---|
2045 | } |
---|
2046 | } |
---|
2047 | |
---|
2048 | #ifndef NO_DEPRECATED |
---|
2049 | static void |
---|
2050 | cmd_default(void) |
---|
2051 | { |
---|
2052 | static const sztok defaulttab[] = { |
---|
2053 | { "CALIBRATE", CMD_CALIBRATE }, |
---|
2054 | { "DATA", CMD_DATA }, |
---|
2055 | { "UNITS", CMD_UNITS }, |
---|
2056 | { NULL, CMD_NULL } |
---|
2057 | }; |
---|
2058 | |
---|
2059 | get_token(); |
---|
2060 | switch (match_tok(defaulttab, TABSIZE(defaulttab))) { |
---|
2061 | case CMD_CALIBRATE: |
---|
2062 | default_calib(pcs); |
---|
2063 | break; |
---|
2064 | case CMD_DATA: |
---|
2065 | default_style(pcs); |
---|
2066 | default_grade(pcs); |
---|
2067 | pcs->cartesian_north = TRUE_NORTH; |
---|
2068 | pcs->cartesian_rotation = 0.0; |
---|
2069 | break; |
---|
2070 | case CMD_UNITS: |
---|
2071 | default_units(pcs); |
---|
2072 | break; |
---|
2073 | default: |
---|
2074 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Unknown setting “%s”*/41, |
---|
2075 | s_str(&token)); |
---|
2076 | } |
---|
2077 | } |
---|
2078 | #endif |
---|
2079 | |
---|
2080 | static void |
---|
2081 | cmd_include(void) |
---|
2082 | { |
---|
2083 | char *pth = NULL; |
---|
2084 | string fnm = S_INIT; |
---|
2085 | #ifndef NO_DEPRECATED |
---|
2086 | prefix *root_store; |
---|
2087 | #endif |
---|
2088 | int ch_store; |
---|
2089 | |
---|
2090 | pth = path_from_fnm(file.filename); |
---|
2091 | |
---|
2092 | read_string(&fnm); |
---|
2093 | |
---|
2094 | #ifndef NO_DEPRECATED |
---|
2095 | /* Since *begin / *end nesting cannot cross file boundaries we only |
---|
2096 | * need to preserve the prefix if the deprecated *prefix command |
---|
2097 | * can be used */ |
---|
2098 | root_store = root; |
---|
2099 | root = pcs->Prefix; /* Root for include file is current prefix */ |
---|
2100 | #endif |
---|
2101 | ch_store = ch; |
---|
2102 | |
---|
2103 | data_file(pth, s_str(&fnm)); |
---|
2104 | |
---|
2105 | #ifndef NO_DEPRECATED |
---|
2106 | root = root_store; /* and restore root */ |
---|
2107 | #endif |
---|
2108 | ch = ch_store; |
---|
2109 | |
---|
2110 | s_free(&fnm); |
---|
2111 | free(pth); |
---|
2112 | } |
---|
2113 | |
---|
2114 | static void |
---|
2115 | cmd_sd(void) |
---|
2116 | { |
---|
2117 | real sd, variance; |
---|
2118 | int units; |
---|
2119 | unsigned long qmask, m; |
---|
2120 | int quantity; |
---|
2121 | qmask = get_qlist(BIT(Q_DECLINATION)); |
---|
2122 | if (!qmask) return; /* no quantities found - error already reported */ |
---|
2123 | |
---|
2124 | if (qmask == BIT(Q_DEFAULT)) { |
---|
2125 | default_grade(pcs); |
---|
2126 | return; |
---|
2127 | } |
---|
2128 | sd = read_numeric(false); |
---|
2129 | if (sd <= (real)0.0) { |
---|
2130 | compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_COL, /*Standard deviation must be positive*/48); |
---|
2131 | return; |
---|
2132 | } |
---|
2133 | units = get_units(qmask, false); |
---|
2134 | if (units == UNITS_NULL) return; |
---|
2135 | |
---|
2136 | sd *= factor_tab[units]; |
---|
2137 | variance = sqrd(sd); |
---|
2138 | |
---|
2139 | for (quantity = 0, m = BIT(quantity); m <= qmask; quantity++, m <<= 1) |
---|
2140 | if (qmask & m) pcs->Var[quantity] = variance; |
---|
2141 | } |
---|
2142 | |
---|
2143 | enum { |
---|
2144 | ROLE_BACKTAPE, |
---|
2145 | ROLE_BACKCOMPASS, |
---|
2146 | ROLE_BACKCLINO, |
---|
2147 | ROLE_TAPE, |
---|
2148 | ROLE_COMPASS, |
---|
2149 | ROLE_CLINO, |
---|
2150 | ROLE_COUNTER, |
---|
2151 | ROLE_DEPTH, |
---|
2152 | ROLE_STATION, |
---|
2153 | ROLE_POSITION, |
---|
2154 | ROLE_NOTES, |
---|
2155 | ROLE_PICTURES, |
---|
2156 | ROLE_INSTRUMENTS, |
---|
2157 | ROLE_ASSISTANT, |
---|
2158 | ROLE_ALTITUDE, |
---|
2159 | ROLE_DIMENSIONS, |
---|
2160 | ROLE_LEFT, |
---|
2161 | ROLE_RIGHT, |
---|
2162 | ROLE_UP, |
---|
2163 | ROLE_DOWN, |
---|
2164 | ROLE_EXPLORER |
---|
2165 | }; |
---|
2166 | |
---|
2167 | static const sztok role_tab[] = { |
---|
2168 | {"ALTITUDE", ROLE_ALTITUDE}, |
---|
2169 | {"ASSISTANT", ROLE_ASSISTANT}, |
---|
2170 | {"BACKBEARING", ROLE_BACKCOMPASS}, |
---|
2171 | {"BACKCLINO", ROLE_BACKCLINO}, |
---|
2172 | {"BACKCOMPASS", ROLE_BACKCOMPASS}, |
---|
2173 | {"BACKGRADIENT", ROLE_BACKCLINO}, |
---|
2174 | {"BACKLENGTH", ROLE_BACKTAPE}, |
---|
2175 | {"BACKTAPE", ROLE_BACKTAPE}, |
---|
2176 | {"BEARING", ROLE_COMPASS}, |
---|
2177 | {"CEILING", ROLE_UP}, |
---|
2178 | {"CLINO", ROLE_CLINO}, |
---|
2179 | {"COMPASS", ROLE_COMPASS}, |
---|
2180 | {"COUNT", ROLE_COUNTER}, |
---|
2181 | {"COUNTER", ROLE_COUNTER}, |
---|
2182 | {"DEPTH", ROLE_DEPTH}, |
---|
2183 | {"DIMENSIONS", ROLE_DIMENSIONS}, |
---|
2184 | {"DOG", ROLE_ASSISTANT}, |
---|
2185 | {"DOWN", ROLE_DOWN}, |
---|
2186 | {"DZ", ROLE_ALTITUDE}, |
---|
2187 | {"EXPLORER", ROLE_EXPLORER}, |
---|
2188 | {"FLOOR", ROLE_DOWN}, |
---|
2189 | {"GRADIENT", ROLE_CLINO}, |
---|
2190 | {"INSTRUMENTS", ROLE_INSTRUMENTS}, |
---|
2191 | {"INSTS", ROLE_INSTRUMENTS}, |
---|
2192 | {"LEFT", ROLE_LEFT}, |
---|
2193 | {"LENGTH", ROLE_TAPE}, |
---|
2194 | {"NOTEBOOK", ROLE_NOTES}, |
---|
2195 | {"NOTES", ROLE_NOTES}, |
---|
2196 | {"PICS", ROLE_PICTURES}, |
---|
2197 | {"PICTURES", ROLE_PICTURES}, |
---|
2198 | {"POSITION", ROLE_POSITION}, |
---|
2199 | {"RIGHT", ROLE_RIGHT}, |
---|
2200 | {"STATION", ROLE_STATION}, |
---|
2201 | {"TAPE", ROLE_TAPE}, |
---|
2202 | {"UP", ROLE_UP}, |
---|
2203 | {NULL, -1} |
---|
2204 | }; |
---|
2205 | |
---|
2206 | static void |
---|
2207 | cmd_team(void) |
---|
2208 | { |
---|
2209 | string name = S_INIT; |
---|
2210 | if (!read_string_warning(&name)) { |
---|
2211 | skipline(); |
---|
2212 | return; |
---|
2213 | } |
---|
2214 | s_free(&name); |
---|
2215 | |
---|
2216 | while (true) { |
---|
2217 | skipblanks(); |
---|
2218 | if (isComm(ch) || isEol(ch)) return; |
---|
2219 | get_token(); |
---|
2220 | int role = match_tok(role_tab, TABSIZE(role_tab)); |
---|
2221 | if (role < 0) { |
---|
2222 | // Skip after a bad role to avoid triggering multiple warnings for |
---|
2223 | // one *team command in existing data from before this check was |
---|
2224 | // implemented. |
---|
2225 | compile_diagnostic(DIAG_WARN|DIAG_TOKEN|DIAG_SKIP, /*Unknown team role “%s”*/532, |
---|
2226 | s_str(&token)); |
---|
2227 | return; |
---|
2228 | } |
---|
2229 | } |
---|
2230 | } |
---|
2231 | |
---|
2232 | static void |
---|
2233 | cmd_title(void) |
---|
2234 | { |
---|
2235 | if (!fExplicitTitle && pcs->Prefix == root) { |
---|
2236 | /* If we don't have an explicit title yet, and we're currently in the |
---|
2237 | * root prefix, use this title explicitly. */ |
---|
2238 | fExplicitTitle = true; |
---|
2239 | read_string(&survey_title); |
---|
2240 | } else { |
---|
2241 | /* parse and throw away this title (but still check rest of line) */ |
---|
2242 | string s = S_INIT; |
---|
2243 | read_string(&s); |
---|
2244 | s_free(&s); |
---|
2245 | } |
---|
2246 | } |
---|
2247 | |
---|
2248 | static const sztok case_tab[] = { |
---|
2249 | {"PRESERVE", OFF}, |
---|
2250 | {"TOLOWER", LOWER}, |
---|
2251 | {"TOUPPER", UPPER}, |
---|
2252 | {NULL, -1} |
---|
2253 | }; |
---|
2254 | |
---|
2255 | static void |
---|
2256 | cmd_case(void) |
---|
2257 | { |
---|
2258 | int setting; |
---|
2259 | get_token(); |
---|
2260 | setting = match_tok(case_tab, TABSIZE(case_tab)); |
---|
2261 | if (setting != -1) { |
---|
2262 | pcs->Case = setting; |
---|
2263 | } else { |
---|
2264 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Found “%s”, expecting “PRESERVE”, “TOUPPER”, or “TOLOWER”*/10, s_str(&token)); |
---|
2265 | } |
---|
2266 | } |
---|
2267 | |
---|
2268 | static void |
---|
2269 | cmd_copyright(void) |
---|
2270 | { |
---|
2271 | filepos fp; |
---|
2272 | get_pos(&fp); |
---|
2273 | unsigned y1 = read_uint_raw(DIAG_WARN|DIAG_UINT, /*Invalid year*/534, NULL); |
---|
2274 | if (y1 < 1000) { |
---|
2275 | set_pos(&fp); |
---|
2276 | compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid year*/534, y1); |
---|
2277 | } else if (y1 > current_year) { |
---|
2278 | set_pos(&fp); |
---|
2279 | compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Date is in the future!*/80); |
---|
2280 | } |
---|
2281 | if (ch == '-') { |
---|
2282 | nextch(); |
---|
2283 | get_pos(&fp); |
---|
2284 | unsigned y2 = read_uint_raw(DIAG_WARN|DIAG_UINT, /*Invalid year*/534, NULL); |
---|
2285 | if (y2 < 1000) { |
---|
2286 | set_pos(&fp); |
---|
2287 | compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid year*/534, y2); |
---|
2288 | } else if (y2 < y1) { |
---|
2289 | set_pos(&fp); |
---|
2290 | compile_diagnostic(DIAG_WARN|DIAG_UINT, /*End of date range is before the start*/81); |
---|
2291 | } else if (y2 > current_year) { |
---|
2292 | set_pos(&fp); |
---|
2293 | compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Date is in the future!*/80); |
---|
2294 | } |
---|
2295 | } |
---|
2296 | |
---|
2297 | string text = S_INIT; |
---|
2298 | if (!read_string_warning(&text)) { |
---|
2299 | skipline(); |
---|
2300 | return; |
---|
2301 | } |
---|
2302 | s_free(&text); |
---|
2303 | |
---|
2304 | skipblanks(); |
---|
2305 | if (!isComm(ch) && !isEol(ch)) |
---|
2306 | compile_diagnostic(DIAG_WARN|DIAG_TAIL, /*End of line not blank*/15); |
---|
2307 | } |
---|
2308 | |
---|
2309 | typedef enum { |
---|
2310 | CS_NONE = -1, |
---|
2311 | CS_CUSTOM, |
---|
2312 | CS_EPSG, |
---|
2313 | CS_ESRI, |
---|
2314 | CS_EUR79Z30, |
---|
2315 | CS_IJTSK, |
---|
2316 | CS_IJTSK03, |
---|
2317 | CS_JTSK, |
---|
2318 | CS_JTSK03, |
---|
2319 | CS_LAT, |
---|
2320 | CS_LOCAL, |
---|
2321 | CS_LONG, |
---|
2322 | CS_OSGB, |
---|
2323 | CS_S_MERC, |
---|
2324 | CS_UTM |
---|
2325 | } cs_class; |
---|
2326 | |
---|
2327 | static const sztok cs_tab[] = { |
---|
2328 | {"CUSTOM", CS_CUSTOM}, |
---|
2329 | {"EPSG", CS_EPSG}, /* EPSG:<number> */ |
---|
2330 | {"ESRI", CS_ESRI}, /* ESRI:<number> */ |
---|
2331 | {"EUR79Z30", CS_EUR79Z30}, |
---|
2332 | {"IJTSK", CS_IJTSK}, |
---|
2333 | {"IJTSK03", CS_IJTSK03}, |
---|
2334 | {"JTSK", CS_JTSK}, |
---|
2335 | {"JTSK03", CS_JTSK03}, |
---|
2336 | {"LAT", CS_LAT}, /* LAT-LONG */ |
---|
2337 | {"LOCAL", CS_LOCAL}, |
---|
2338 | {"LONG", CS_LONG}, /* LONG-LAT */ |
---|
2339 | {"OSGB", CS_OSGB}, /* OSGB:<H, N, O, S or T><A-Z except I> */ |
---|
2340 | {"S", CS_S_MERC}, /* S-MERC */ |
---|
2341 | // UTM<zone><N or S or nothing> is handled separately to avoid needing 180 |
---|
2342 | // entries in this lookup table. |
---|
2343 | {NULL, CS_NONE} |
---|
2344 | }; |
---|
2345 | |
---|
2346 | static void |
---|
2347 | cmd_cs(void) |
---|
2348 | { |
---|
2349 | char *proj_str = NULL; |
---|
2350 | cs_class cs; |
---|
2351 | int cs_sub = INT_MIN; |
---|
2352 | filepos fp; |
---|
2353 | bool output = false; |
---|
2354 | enum { YES, NO, MAYBE } ok_for_output = YES; |
---|
2355 | static bool had_cs = false; |
---|
2356 | |
---|
2357 | if (!had_cs) { |
---|
2358 | had_cs = true; |
---|
2359 | if (first_fix_name) { |
---|
2360 | compile_diagnostic_at(DIAG_ERR, |
---|
2361 | first_fix_filename, first_fix_line, |
---|
2362 | /*Station “%s” fixed before CS command first used*/442, |
---|
2363 | sprint_prefix(first_fix_name)); |
---|
2364 | } |
---|
2365 | } |
---|
2366 | |
---|
2367 | skipblanks(); |
---|
2368 | get_pos(&fp); |
---|
2369 | get_token_no_blanks(); |
---|
2370 | if (S_EQ(&uctoken, "OUT")) { |
---|
2371 | output = true; |
---|
2372 | skipblanks(); |
---|
2373 | get_pos(&fp); |
---|
2374 | get_token_no_blanks(); |
---|
2375 | } |
---|
2376 | |
---|
2377 | if (s_len(&uctoken) > 3 && |
---|
2378 | memcmp(s_str(&uctoken), "UTM", 3) == 0 && |
---|
2379 | isdigit((unsigned char)s_str(&uctoken)[3])) { |
---|
2380 | // The token starts "UTM" followed by a digit so handle that separately |
---|
2381 | // to avoid needing 180 entries for UTM zones in the cs_tab lookup |
---|
2382 | // table. |
---|
2383 | cs = CS_UTM; |
---|
2384 | // Reposition on the digit after "UTM". |
---|
2385 | set_pos(&fp); |
---|
2386 | nextch(); |
---|
2387 | nextch(); |
---|
2388 | nextch(); |
---|
2389 | unsigned n = read_uint(); |
---|
2390 | if (n >= 1 && n <= 60) { |
---|
2391 | int uch = toupper(ch); |
---|
2392 | cs_sub = (int)n; |
---|
2393 | if (uch == 'S') { |
---|
2394 | nextch(); |
---|
2395 | cs_sub = -cs_sub; |
---|
2396 | } else if (uch == 'N') { |
---|
2397 | nextch(); |
---|
2398 | } |
---|
2399 | } |
---|
2400 | } else { |
---|
2401 | cs = match_tok(cs_tab, TABSIZE(cs_tab)); |
---|
2402 | switch (cs) { |
---|
2403 | case CS_NONE: |
---|
2404 | break; |
---|
2405 | case CS_CUSTOM: |
---|
2406 | ok_for_output = MAYBE; |
---|
2407 | get_pos(&fp); |
---|
2408 | string str = S_INIT; |
---|
2409 | read_string(&str); |
---|
2410 | proj_str = s_steal(&str); |
---|
2411 | cs_sub = 0; |
---|
2412 | break; |
---|
2413 | case CS_EPSG: case CS_ESRI: |
---|
2414 | ok_for_output = MAYBE; |
---|
2415 | if (ch == ':' && isdigit(nextch())) { |
---|
2416 | unsigned n = read_uint(); |
---|
2417 | if (n < 1000000) { |
---|
2418 | cs_sub = (int)n; |
---|
2419 | } |
---|
2420 | } |
---|
2421 | break; |
---|
2422 | case CS_EUR79Z30: |
---|
2423 | cs_sub = 0; |
---|
2424 | break; |
---|
2425 | case CS_JTSK: |
---|
2426 | case CS_JTSK03: |
---|
2427 | ok_for_output = NO; |
---|
2428 | cs_sub = 0; |
---|
2429 | break; |
---|
2430 | case CS_IJTSK: |
---|
2431 | case CS_IJTSK03: |
---|
2432 | cs_sub = 0; |
---|
2433 | break; |
---|
2434 | case CS_LAT: case CS_LONG: |
---|
2435 | ok_for_output = NO; |
---|
2436 | if (ch == '-') { |
---|
2437 | nextch(); |
---|
2438 | get_token_no_blanks(); |
---|
2439 | cs_class cs2 = match_tok(cs_tab, TABSIZE(cs_tab)); |
---|
2440 | if ((cs ^ cs2) == (CS_LAT ^ CS_LONG)) { |
---|
2441 | cs_sub = 0; |
---|
2442 | } |
---|
2443 | } |
---|
2444 | break; |
---|
2445 | case CS_LOCAL: |
---|
2446 | cs_sub = 0; |
---|
2447 | break; |
---|
2448 | case CS_OSGB: |
---|
2449 | if (ch == ':') { |
---|
2450 | int uch1 = toupper(nextch()); |
---|
2451 | if (strchr("HNOST", uch1)) { |
---|
2452 | int uch2 = toupper(nextch()); |
---|
2453 | if (uch2 >= 'A' && uch2 <= 'Z' && uch2 != 'I') { |
---|
2454 | int x, y; |
---|
2455 | nextch(); |
---|
2456 | if (uch1 > 'I') --uch1; |
---|
2457 | uch1 -= 'A'; |
---|
2458 | if (uch2 > 'I') --uch2; |
---|
2459 | uch2 -= 'A'; |
---|
2460 | x = uch1 % 5; |
---|
2461 | y = uch1 / 5; |
---|
2462 | x = (x * 5) + uch2 % 5; |
---|
2463 | y = (y * 5) + uch2 / 5; |
---|
2464 | cs_sub = y * 25 + x; |
---|
2465 | } |
---|
2466 | } |
---|
2467 | } |
---|
2468 | break; |
---|
2469 | case CS_S_MERC: |
---|
2470 | if (ch == '-') { |
---|
2471 | nextch(); |
---|
2472 | get_token_no_blanks(); |
---|
2473 | if (S_EQ(&uctoken, "MERC")) { |
---|
2474 | cs_sub = 0; |
---|
2475 | } |
---|
2476 | } |
---|
2477 | break; |
---|
2478 | case CS_UTM: |
---|
2479 | // Handled outside of this switch, but avoid compiler warning about |
---|
2480 | // unhandled enumeration value. |
---|
2481 | break; |
---|
2482 | } |
---|
2483 | } |
---|
2484 | if (cs_sub == INT_MIN || isalnum(ch)) { |
---|
2485 | set_pos(&fp); |
---|
2486 | compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Unknown coordinate system*/434); |
---|
2487 | skipline(); |
---|
2488 | return; |
---|
2489 | } |
---|
2490 | /* Actually handle the cs */ |
---|
2491 | switch (cs) { |
---|
2492 | case CS_NONE: |
---|
2493 | break; |
---|
2494 | case CS_CUSTOM: |
---|
2495 | /* proj_str already set */ |
---|
2496 | break; |
---|
2497 | case CS_EPSG: |
---|
2498 | proj_str = osmalloc(32); |
---|
2499 | snprintf(proj_str, 32, "EPSG:%d", cs_sub); |
---|
2500 | break; |
---|
2501 | case CS_ESRI: |
---|
2502 | proj_str = osmalloc(32); |
---|
2503 | snprintf(proj_str, 32, "ESRI:%d", cs_sub); |
---|
2504 | break; |
---|
2505 | case CS_EUR79Z30: |
---|
2506 | proj_str = osstrdup("+proj=utm +zone=30 +ellps=intl +towgs84=-86,-98,-119,0,0,0,0 +no_defs"); |
---|
2507 | break; |
---|
2508 | case CS_IJTSK: |
---|
2509 | proj_str = osstrdup("+proj=krovak +ellps=bessel +towgs84=570.8285,85.6769,462.842,4.9984,1.5867,5.2611,3.5623 +no_defs"); |
---|
2510 | break; |
---|
2511 | case CS_IJTSK03: |
---|
2512 | proj_str = osstrdup("+proj=krovak +ellps=bessel +towgs84=485.021,169.465,483.839,7.786342,4.397554,4.102655,0 +no_defs"); |
---|
2513 | break; |
---|
2514 | case CS_JTSK: |
---|
2515 | proj_str = osstrdup("+proj=krovak +czech +ellps=bessel +towgs84=570.8285,85.6769,462.842,4.9984,1.5867,5.2611,3.5623 +no_defs"); |
---|
2516 | break; |
---|
2517 | case CS_JTSK03: |
---|
2518 | proj_str = osstrdup("+proj=krovak +czech +ellps=bessel +towgs84=485.021,169.465,483.839,7.786342,4.397554,4.102655,0 +no_defs"); |
---|
2519 | break; |
---|
2520 | case CS_LAT: |
---|
2521 | /* FIXME: Requires PROJ >= 4.8.0 for +axis, and the SDs will be |
---|
2522 | * misapplied, so we may want to swap ourselves. Also, while |
---|
2523 | * therion supports lat-long, I'm not totally convinced that it is |
---|
2524 | * sensible to do so - people often say "lat-long", but probably |
---|
2525 | * don't think that that's actually "Northing, Easting". This |
---|
2526 | * seems like it'll result in people accidentally getting X and Y |
---|
2527 | * swapped in their fixed points... |
---|
2528 | */ |
---|
2529 | #if 0 |
---|
2530 | proj_str = osstrdup("+proj=longlat +ellps=WGS84 +datum=WGS84 +axis=neu +no_defs"); |
---|
2531 | #endif |
---|
2532 | break; |
---|
2533 | case CS_LOCAL: |
---|
2534 | /* FIXME: Is it useful to be able to explicitly specify this? */ |
---|
2535 | break; |
---|
2536 | case CS_LONG: |
---|
2537 | proj_str = osstrdup("EPSG:4326"); |
---|
2538 | break; |
---|
2539 | case CS_OSGB: { |
---|
2540 | int x = 14 - (cs_sub % 25); |
---|
2541 | int y = (cs_sub / 25) - 20; |
---|
2542 | proj_str = osmalloc(160); |
---|
2543 | snprintf(proj_str, 160, |
---|
2544 | "+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 " |
---|
2545 | "+x_0=%d +y_0=%d +ellps=airy +datum=OSGB36 +units=m +no_defs", |
---|
2546 | x * 100000, y * 100000); |
---|
2547 | break; |
---|
2548 | } |
---|
2549 | case CS_S_MERC: |
---|
2550 | proj_str = osstrdup("EPSG:3857"); |
---|
2551 | break; |
---|
2552 | case CS_UTM: |
---|
2553 | proj_str = osmalloc(32); |
---|
2554 | if (cs_sub > 0) { |
---|
2555 | snprintf(proj_str, 32, "EPSG:%d", 32600 + cs_sub); |
---|
2556 | } else { |
---|
2557 | snprintf(proj_str, 32, "EPSG:%d", 32700 - cs_sub); |
---|
2558 | } |
---|
2559 | break; |
---|
2560 | } |
---|
2561 | |
---|
2562 | if (!proj_str) { |
---|
2563 | /* printf("CS %d:%d\n", (int)cs, cs_sub); */ |
---|
2564 | set_pos(&fp); |
---|
2565 | compile_diagnostic(DIAG_ERR|DIAG_STRING, /*Unknown coordinate system*/434); |
---|
2566 | skipline(); |
---|
2567 | return; |
---|
2568 | } |
---|
2569 | |
---|
2570 | if (output) { |
---|
2571 | if (ok_for_output == NO) { |
---|
2572 | set_pos(&fp); |
---|
2573 | compile_diagnostic(DIAG_ERR|DIAG_STRING, /*Coordinate system unsuitable for output*/435); |
---|
2574 | skipline(); |
---|
2575 | return; |
---|
2576 | } |
---|
2577 | |
---|
2578 | if (proj_str_out && strcmp(proj_str, proj_str_out) == 0) { |
---|
2579 | /* Same as the output cs that's already set, so nothing to do. */ |
---|
2580 | free(proj_str); |
---|
2581 | return; |
---|
2582 | } |
---|
2583 | |
---|
2584 | if (ok_for_output == MAYBE) { |
---|
2585 | /* We only actually create the transformation from input to output when |
---|
2586 | * we need it, but for a custom proj string or EPSG/ESRI code we need |
---|
2587 | * to check that the specified coordinate system is valid and also if |
---|
2588 | * it's suitable for output so we need to test creating it here. |
---|
2589 | */ |
---|
2590 | proj_errno_reset(NULL); |
---|
2591 | PJ* pj = proj_create(PJ_DEFAULT_CTX, proj_str); |
---|
2592 | if (!pj) { |
---|
2593 | set_pos(&fp); |
---|
2594 | compile_diagnostic(DIAG_ERR|DIAG_STRING, /*Invalid coordinate system: %s*/443, |
---|
2595 | proj_context_errno_string(PJ_DEFAULT_CTX, |
---|
2596 | proj_context_errno(PJ_DEFAULT_CTX))); |
---|
2597 | skipline(); |
---|
2598 | free(proj_str); |
---|
2599 | return; |
---|
2600 | } |
---|
2601 | int type = proj_get_type(pj); |
---|
2602 | if (type == PJ_TYPE_GEOGRAPHIC_2D_CRS || |
---|
2603 | type == PJ_TYPE_GEOGRAPHIC_3D_CRS) { |
---|
2604 | set_pos(&fp); |
---|
2605 | compile_diagnostic(DIAG_ERR|DIAG_STRING, /*Coordinate system unsuitable for output*/435); |
---|
2606 | skipline(); |
---|
2607 | free(proj_str); |
---|
2608 | return; |
---|
2609 | } |
---|
2610 | } |
---|
2611 | |
---|
2612 | if (proj_str_out) { |
---|
2613 | /* If the output cs is already set, subsequent attempts to set it |
---|
2614 | * are silently ignored (so you can combine two datasets and set |
---|
2615 | * the output cs to use before you include either). |
---|
2616 | */ |
---|
2617 | free(proj_str); |
---|
2618 | } else { |
---|
2619 | proj_str_out = proj_str; |
---|
2620 | } |
---|
2621 | } else { |
---|
2622 | if (proj_str_out && strcmp(proj_str, proj_str_out) == 0) { |
---|
2623 | /* Same as the current output projection, so valid for input. */ |
---|
2624 | } else if (pcs->proj_str && strcmp(proj_str, pcs->proj_str) == 0) { |
---|
2625 | /* Same as the current input projection, so nothing to do! */ |
---|
2626 | return; |
---|
2627 | } else if (ok_for_output == MAYBE) { |
---|
2628 | /* (ok_for_output == MAYBE) also happens to indicate whether we need |
---|
2629 | * to check that the coordinate system is valid for input. |
---|
2630 | */ |
---|
2631 | proj_errno_reset(NULL); |
---|
2632 | PJ* pj = proj_create(PJ_DEFAULT_CTX, proj_str); |
---|
2633 | if (!pj) { |
---|
2634 | set_pos(&fp); |
---|
2635 | compile_diagnostic(DIAG_ERR|DIAG_STRING, /*Invalid coordinate system: %s*/443, |
---|
2636 | proj_context_errno_string(PJ_DEFAULT_CTX, |
---|
2637 | proj_context_errno(PJ_DEFAULT_CTX))); |
---|
2638 | skipline(); |
---|
2639 | return; |
---|
2640 | } |
---|
2641 | proj_destroy(pj); |
---|
2642 | } |
---|
2643 | |
---|
2644 | /* Free current input proj_str if not used by parent. */ |
---|
2645 | settings * p = pcs; |
---|
2646 | if (!p->next || p->proj_str != p->next->proj_str) |
---|
2647 | free(p->proj_str); |
---|
2648 | p->proj_str = proj_str; |
---|
2649 | p->input_convergence = HUGE_REAL; |
---|
2650 | invalidate_pj_cached(); |
---|
2651 | } |
---|
2652 | } |
---|
2653 | |
---|
2654 | static const sztok infer_tab[] = { |
---|
2655 | { "EQUATES", INFER_EQUATES }, |
---|
2656 | { "EXPORTS", INFER_EXPORTS }, |
---|
2657 | { "PLUMBS", INFER_PLUMBS }, |
---|
2658 | #if 0 /* FIXME */ |
---|
2659 | { "SUBSURVEYS", INFER_SUBSURVEYS }, |
---|
2660 | #endif |
---|
2661 | { NULL, INFER_NULL } |
---|
2662 | }; |
---|
2663 | |
---|
2664 | static const sztok onoff_tab[] = { |
---|
2665 | { "OFF", 0 }, |
---|
2666 | { "ON", 1 }, |
---|
2667 | { NULL, -1 } |
---|
2668 | }; |
---|
2669 | |
---|
2670 | static void |
---|
2671 | cmd_infer(void) |
---|
2672 | { |
---|
2673 | infer_what setting; |
---|
2674 | int on; |
---|
2675 | get_token(); |
---|
2676 | setting = match_tok(infer_tab, TABSIZE(infer_tab)); |
---|
2677 | if (setting == INFER_NULL) { |
---|
2678 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Found “%s”, expecting “EQUATES”, “EXPORTS”, or “PLUMBS”*/31, s_str(&token)); |
---|
2679 | return; |
---|
2680 | } |
---|
2681 | get_token(); |
---|
2682 | on = match_tok(onoff_tab, TABSIZE(onoff_tab)); |
---|
2683 | if (on == -1) { |
---|
2684 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Found “%s”, expecting “ON” or “OFF”*/32, s_str(&token)); |
---|
2685 | return; |
---|
2686 | } |
---|
2687 | |
---|
2688 | if (on) { |
---|
2689 | pcs->infer |= BIT(setting); |
---|
2690 | if (setting == INFER_EXPORTS) fExportUsed = true; |
---|
2691 | } else { |
---|
2692 | pcs->infer &= ~BIT(setting); |
---|
2693 | } |
---|
2694 | } |
---|
2695 | |
---|
2696 | static void |
---|
2697 | cmd_truncate(void) |
---|
2698 | { |
---|
2699 | unsigned int truncate_at = 0; /* default is no truncation */ |
---|
2700 | filepos fp; |
---|
2701 | |
---|
2702 | get_pos(&fp); |
---|
2703 | |
---|
2704 | get_token(); |
---|
2705 | if (!S_EQ(&uctoken, "OFF")) { |
---|
2706 | if (!s_empty(&uctoken)) set_pos(&fp); |
---|
2707 | truncate_at = read_uint(); |
---|
2708 | } |
---|
2709 | /* for backward compatibility, "*truncate 0" means "*truncate off" */ |
---|
2710 | pcs->Truncate = (truncate_at == 0) ? INT_MAX : truncate_at; |
---|
2711 | } |
---|
2712 | |
---|
2713 | static void |
---|
2714 | cmd_ref(void) |
---|
2715 | { |
---|
2716 | /* Just syntax check for now. */ |
---|
2717 | string ref = S_INIT; |
---|
2718 | read_string(&ref); |
---|
2719 | s_free(&ref); |
---|
2720 | } |
---|
2721 | |
---|
2722 | static void |
---|
2723 | cmd_require(void) |
---|
2724 | { |
---|
2725 | // Add extra 0 so `*require 1.4.10.1` fails with cavern version 1.4.10. |
---|
2726 | const unsigned version[] = {COMMAVERSION, 0}; |
---|
2727 | |
---|
2728 | filepos fp; |
---|
2729 | get_pos(&fp); |
---|
2730 | |
---|
2731 | // Parse the required version number, storing its components in |
---|
2732 | // required_version. We only store at most one more component than |
---|
2733 | // COMMAVERSION has since more than that can't affect the comparison. |
---|
2734 | size_t i = 0; |
---|
2735 | int diff = 0; |
---|
2736 | while (1) { |
---|
2737 | unsigned component = read_uint(); |
---|
2738 | if (diff == 0 && i < sizeof(version) / sizeof(version[0])) { |
---|
2739 | if (diff == 0) { |
---|
2740 | diff = (int)version[i++] - (int)component; |
---|
2741 | } |
---|
2742 | } |
---|
2743 | if (ch != '.' || isBlank(nextch()) || isComm(ch) || isEol(ch)) |
---|
2744 | break; |
---|
2745 | } |
---|
2746 | |
---|
2747 | if (diff < 0) { |
---|
2748 | // Requirement not satisfied |
---|
2749 | size_t len = (size_t)(ftell(file.fh) - fp.offset); |
---|
2750 | char *v = osmalloc(len + 1); |
---|
2751 | set_pos(&fp); |
---|
2752 | for (size_t j = 0; j < len; j++) { |
---|
2753 | v[j] = ch; |
---|
2754 | nextch(); |
---|
2755 | } |
---|
2756 | v[len] = '\0'; |
---|
2757 | /* TRANSLATORS: Feel free to translate as "or newer" instead of "or |
---|
2758 | * greater" if that gives a more natural translation. It's |
---|
2759 | * technically not quite right when there are parallel active release |
---|
2760 | * series (e.g. Survex 1.0.40 was released *after* 1.2.0), but this |
---|
2761 | * seems unlikely to confuse users. "Survex" is the name of the |
---|
2762 | * software, so should not be translated. |
---|
2763 | * |
---|
2764 | * Here "survey" is a "cave map" rather than list of questions - it should be |
---|
2765 | * translated to the terminology that cavers using the language would use. |
---|
2766 | */ |
---|
2767 | compile_diagnostic(DIAG_FATAL|DIAG_FROM(fp), /*Survex version %s or greater required to process this survey data.*/38, v); |
---|
2768 | // Does not return so no point freeing v here. |
---|
2769 | } |
---|
2770 | } |
---|
2771 | |
---|
2772 | /* allocate new meta_data if need be */ |
---|
2773 | void |
---|
2774 | copy_on_write_meta(settings *s) |
---|
2775 | { |
---|
2776 | if (!s->meta || s->meta->ref_count != 0) { |
---|
2777 | meta_data * meta_new = osnew(meta_data); |
---|
2778 | if (!s->meta) { |
---|
2779 | meta_new->days1 = meta_new->days2 = -1; |
---|
2780 | } else { |
---|
2781 | *meta_new = *(s->meta); |
---|
2782 | } |
---|
2783 | meta_new->ref_count = 0; |
---|
2784 | s->meta = meta_new; |
---|
2785 | } |
---|
2786 | } |
---|
2787 | |
---|
2788 | static int |
---|
2789 | read_year(filepos *fp_date_ptr) |
---|
2790 | { |
---|
2791 | int y = read_uint_raw(DIAG_ERR, /*Expecting date, found “%s”*/198, fp_date_ptr); |
---|
2792 | if (y < 100) { |
---|
2793 | /* Two digit year is 19xx. */ |
---|
2794 | y += 1900; |
---|
2795 | filepos fp_save; |
---|
2796 | get_pos(&fp_save); |
---|
2797 | set_pos(fp_date_ptr); |
---|
2798 | /* TRANSLATORS: %d will be replaced by the assumed year, e.g. 1918 */ |
---|
2799 | compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Assuming 2 digit year is %d*/76, y); |
---|
2800 | set_pos(&fp_save); |
---|
2801 | } else if (y < 1900 || y > 2078) { |
---|
2802 | set_pos(fp_date_ptr); |
---|
2803 | compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid year (< 1900 or > 2078)*/58); |
---|
2804 | longjmp(jbSkipLine, 1); |
---|
2805 | } |
---|
2806 | return y; |
---|
2807 | } |
---|
2808 | |
---|
2809 | static void |
---|
2810 | cmd_date(void) |
---|
2811 | { |
---|
2812 | enum { DATE_SURVEYED = 1, DATE_EXPLORED = 2 }; |
---|
2813 | unsigned date_flags = 0; |
---|
2814 | while (get_token(), !s_empty(&uctoken)) { |
---|
2815 | unsigned new_flag = 0; |
---|
2816 | if (S_EQ(&uctoken, "SURVEYED")) { |
---|
2817 | new_flag = DATE_SURVEYED; |
---|
2818 | } else if (S_EQ(&uctoken, "EXPLORED")) { |
---|
2819 | new_flag = DATE_EXPLORED; |
---|
2820 | } else { |
---|
2821 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN, |
---|
2822 | /*Expecting “%s” or “%s”*/103, "SURVEYED", "EXPLORED"); |
---|
2823 | continue; |
---|
2824 | } |
---|
2825 | |
---|
2826 | if ((date_flags & new_flag)) { |
---|
2827 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN, |
---|
2828 | /*Duplicate date type “%s”*/416, s_str(&token)); |
---|
2829 | } |
---|
2830 | date_flags |= new_flag; |
---|
2831 | } |
---|
2832 | |
---|
2833 | int date_sep = '-'; |
---|
2834 | if (date_flags == 0) { |
---|
2835 | // `*date` without qualification sets the surveyed date. |
---|
2836 | date_flags = DATE_SURVEYED; |
---|
2837 | // Also allow '.' for compatibility. |
---|
2838 | date_sep = 0; |
---|
2839 | } |
---|
2840 | |
---|
2841 | filepos fp_date1, fp_date2, fp; |
---|
2842 | get_pos(&fp_date1); |
---|
2843 | int year = read_year(&fp_date1); |
---|
2844 | int month = 0, day = 0, year2 = 0, month2 = 0, day2 = 0; |
---|
2845 | |
---|
2846 | if (ch == '-') { |
---|
2847 | // Could be ISO-format date (e.g. 2024-10 or 2024-10-21 or 1912-11), or |
---|
2848 | // a range of old-format dates (e.g. 1973-1975 or 1911-12.02.03) or an |
---|
2849 | // ambiguous case like 1911-12 which we need to warn about in a `*date` |
---|
2850 | // command without `surveyed` or `explored` qualifiers. |
---|
2851 | nextch(); |
---|
2852 | get_pos(&fp_date2); |
---|
2853 | int v = read_uint_raw(DIAG_ERR, /*Expecting date, found “%s”*/198, &fp_date1); |
---|
2854 | if (date_sep == '-') { |
---|
2855 | // We're only accepting ISO dates. |
---|
2856 | } else if (ch == '-') { |
---|
2857 | // Two `-` so must be an ISO date, e.g. `2024-10-21`. |
---|
2858 | } else if (v >= 1 && v <= 12) { |
---|
2859 | // Valid month number so assume it's an ISO date. |
---|
2860 | if (year < 1900 + v) { |
---|
2861 | // Warn about ambiguous cases. |
---|
2862 | compile_diagnostic(DIAG_WARN|DIAG_FROM(fp_date1), |
---|
2863 | /*Interpreting as an ISO-format date - use “*date surveyed %d-%02d” to suppress this warning, or “*date %d %d” if you wanted a date range*/158, |
---|
2864 | year, v, year, 1900 + v); |
---|
2865 | } |
---|
2866 | } else { |
---|
2867 | date_sep = '.'; |
---|
2868 | year2 = v; |
---|
2869 | if (year2 < 100) { |
---|
2870 | /* Two digit year is 19xx. */ |
---|
2871 | year2 += 1900; |
---|
2872 | /* TRANSLATORS: %d will be replaced by the assumed year, e.g. 1918 */ |
---|
2873 | compile_diagnostic(DIAG_WARN|DIAG_FROM(fp_date2), |
---|
2874 | /*Assuming 2 digit year is %d*/76, year2); |
---|
2875 | } else if (year2 < 1900 || year2 > 2078) { |
---|
2876 | compile_diagnostic(DIAG_WARN|DIAG_FROM(fp_date2), |
---|
2877 | /*Invalid year (< 1900 or > 2078)*/58); |
---|
2878 | longjmp(jbSkipLine, 1); |
---|
2879 | } |
---|
2880 | goto process_dates; |
---|
2881 | } |
---|
2882 | |
---|
2883 | date_sep = '-'; |
---|
2884 | month = v; |
---|
2885 | fp = fp_date2; |
---|
2886 | } else if (ch == '.') { |
---|
2887 | if (date_sep == '-') { |
---|
2888 | char date_sep_string[2] = { date_sep, '\0' }; |
---|
2889 | compile_diagnostic(DIAG_ERR|DIAG_COL|DIAG_SKIP, |
---|
2890 | /*Expecting “%s”*/497, date_sep_string); |
---|
2891 | return; |
---|
2892 | } |
---|
2893 | date_sep = ch; |
---|
2894 | nextch(); |
---|
2895 | get_pos(&fp); |
---|
2896 | month = read_uint_raw(DIAG_ERR, /*Expecting date, found “%s”*/198, &fp_date1); |
---|
2897 | } else { |
---|
2898 | // Just a year - might be a ISO date range though. |
---|
2899 | date_sep = '-'; |
---|
2900 | goto try_date2; |
---|
2901 | } |
---|
2902 | |
---|
2903 | if (month < 1 || month > 12) { |
---|
2904 | set_pos(&fp); |
---|
2905 | compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid month*/86); |
---|
2906 | longjmp(jbSkipLine, 1); |
---|
2907 | } |
---|
2908 | |
---|
2909 | if (ch == date_sep) { |
---|
2910 | nextch(); |
---|
2911 | get_pos(&fp); |
---|
2912 | day = read_uint_raw(DIAG_ERR, /*Expecting date, found “%s”*/198, &fp_date1); |
---|
2913 | if (day < 1 || day > last_day(year, month)) { |
---|
2914 | set_pos(&fp); |
---|
2915 | /* TRANSLATORS: e.g. 31st of April, or 32nd of any month */ |
---|
2916 | compile_diagnostic(DIAG_WARN|DIAG_UINT, |
---|
2917 | /*Invalid day of the month*/87); |
---|
2918 | longjmp(jbSkipLine, 1); |
---|
2919 | } |
---|
2920 | } |
---|
2921 | |
---|
2922 | try_date2: |
---|
2923 | if (date_sep == '-') { |
---|
2924 | skipblanks(); |
---|
2925 | if (!isdigit(ch)) goto process_dates; |
---|
2926 | } else if (ch == '-') { |
---|
2927 | nextch(); |
---|
2928 | } else { |
---|
2929 | goto process_dates; |
---|
2930 | } |
---|
2931 | { |
---|
2932 | get_pos(&fp_date2); |
---|
2933 | year2 = read_year(&fp_date2); |
---|
2934 | if (ch == date_sep) { |
---|
2935 | nextch(); |
---|
2936 | get_pos(&fp); |
---|
2937 | month2 = read_uint_raw(DIAG_ERR, /*Expecting date, found “%s”*/198, |
---|
2938 | &fp_date2); |
---|
2939 | if (month2 < 1 || month2 > 12) { |
---|
2940 | set_pos(&fp); |
---|
2941 | compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid month*/86); |
---|
2942 | longjmp(jbSkipLine, 1); |
---|
2943 | } |
---|
2944 | |
---|
2945 | if (ch == date_sep) { |
---|
2946 | nextch(); |
---|
2947 | get_pos(&fp); |
---|
2948 | day2 = read_uint_raw(DIAG_ERR, /*Expecting date, found “%s”*/198, |
---|
2949 | &fp_date2); |
---|
2950 | if (day2 < 1 || day2 > last_day(year2, month2)) { |
---|
2951 | set_pos(&fp); |
---|
2952 | /* TRANSLATORS: e.g. 31st of April, or 32nd of any month */ |
---|
2953 | compile_diagnostic(DIAG_WARN|DIAG_UINT, |
---|
2954 | /*Invalid day of the month*/87); |
---|
2955 | longjmp(jbSkipLine, 1); |
---|
2956 | } |
---|
2957 | } |
---|
2958 | } |
---|
2959 | } |
---|
2960 | |
---|
2961 | process_dates:; |
---|
2962 | bool date_range = (year2 != 0); |
---|
2963 | if (!date_range) { |
---|
2964 | year2 = year; |
---|
2965 | month2 = month; |
---|
2966 | day2 = day; |
---|
2967 | } |
---|
2968 | |
---|
2969 | int days1 = days_since_1900(year, month ? month : 1, day ? day : 1); |
---|
2970 | |
---|
2971 | if (days1 > current_days_since_1900) { |
---|
2972 | filepos fp_save; |
---|
2973 | get_pos(&fp_save); |
---|
2974 | set_pos(&fp_date1); |
---|
2975 | compile_diagnostic(DIAG_WARN|DIAG_DATE, /*Date is in the future!*/80); |
---|
2976 | set_pos(&fp_save); |
---|
2977 | } |
---|
2978 | |
---|
2979 | if (month2 == 0) { |
---|
2980 | month2 = 12; |
---|
2981 | day2 = 31; |
---|
2982 | } else if (day2 == 0) { |
---|
2983 | day2 = last_day(year2, month2); |
---|
2984 | } |
---|
2985 | int days2 = days_since_1900(year2, month2, day2); |
---|
2986 | if (date_range && days2 > current_days_since_1900) { |
---|
2987 | // If !date_range, either we already emitted this warning when |
---|
2988 | // processing the start date, or the date is partial and for the |
---|
2989 | // current year or current month, in which case it's not helpful |
---|
2990 | // to warn that the end is on the future, but it makes sense to |
---|
2991 | // process for the end date so the result doesn't change as time |
---|
2992 | // passes. |
---|
2993 | filepos fp_save; |
---|
2994 | get_pos(&fp_save); |
---|
2995 | set_pos(&fp_date2); |
---|
2996 | compile_diagnostic(DIAG_WARN|DIAG_DATE, /*Date is in the future!*/80); |
---|
2997 | set_pos(&fp_save); |
---|
2998 | } |
---|
2999 | |
---|
3000 | if (days2 < days1) { |
---|
3001 | filepos fp_save; |
---|
3002 | get_pos(&fp_save); |
---|
3003 | set_pos(&fp_date1); |
---|
3004 | compile_diagnostic(DIAG_ERR|DIAG_WORD, /*End of date range is before the start*/81); |
---|
3005 | set_pos(&fp_save); |
---|
3006 | // Swap range ends to put us in a consistent state. |
---|
3007 | int tmp = days1; |
---|
3008 | days1 = days2; |
---|
3009 | days2 = tmp; |
---|
3010 | } |
---|
3011 | |
---|
3012 | if ((date_flags & DATE_SURVEYED)) { |
---|
3013 | if (!pcs->meta || pcs->meta->days1 != days1 || pcs->meta->days2 != days2) { |
---|
3014 | copy_on_write_meta(pcs); |
---|
3015 | pcs->meta->days1 = days1; |
---|
3016 | pcs->meta->days2 = days2; |
---|
3017 | /* Invalidate cached declination. */ |
---|
3018 | pcs->declination = HUGE_REAL; |
---|
3019 | } |
---|
3020 | } |
---|
3021 | |
---|
3022 | if ((date_flags & DATE_EXPLORED)) { |
---|
3023 | // FIXME: Need to revise 3d format to allow storing EXPLORED date too. |
---|
3024 | } |
---|
3025 | } |
---|
3026 | |
---|
3027 | typedef void (*cmd_fn)(void); |
---|
3028 | |
---|
3029 | static const cmd_fn cmd_funcs[] = { |
---|
3030 | cmd_alias, |
---|
3031 | cmd_begin, |
---|
3032 | cmd_calibrate, |
---|
3033 | cmd_cartesian, |
---|
3034 | cmd_case, |
---|
3035 | cmd_copyright, |
---|
3036 | cmd_cs, |
---|
3037 | cmd_data, |
---|
3038 | cmd_date, |
---|
3039 | cmd_declination, |
---|
3040 | #ifndef NO_DEPRECATED |
---|
3041 | cmd_default, |
---|
3042 | #endif |
---|
3043 | cmd_end, |
---|
3044 | cmd_entrance, |
---|
3045 | cmd_equate, |
---|
3046 | cmd_export, |
---|
3047 | cmd_fix, |
---|
3048 | cmd_flags, |
---|
3049 | cmd_include, |
---|
3050 | cmd_infer, |
---|
3051 | skipline, /*cmd_instrument,*/ |
---|
3052 | #ifndef NO_DEPRECATED |
---|
3053 | cmd_prefix, |
---|
3054 | #endif |
---|
3055 | cmd_ref, |
---|
3056 | cmd_require, |
---|
3057 | cmd_sd, |
---|
3058 | cmd_set, |
---|
3059 | solve_network, |
---|
3060 | cmd_team, |
---|
3061 | cmd_title, |
---|
3062 | cmd_truncate, |
---|
3063 | cmd_units |
---|
3064 | }; |
---|
3065 | |
---|
3066 | extern void |
---|
3067 | handle_command(void) |
---|
3068 | { |
---|
3069 | filepos fp; |
---|
3070 | get_pos(&fp); |
---|
3071 | get_token_legacy(); |
---|
3072 | int cmdtok = match_tok(cmd_tab, TABSIZE(cmd_tab)); |
---|
3073 | if (cmdtok < 0 || cmdtok >= (int)(sizeof(cmd_funcs) / sizeof(cmd_fn))) { |
---|
3074 | set_pos(&fp); |
---|
3075 | get_token(); |
---|
3076 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Unknown command “%s”*/12, s_str(&token)); |
---|
3077 | return; |
---|
3078 | } |
---|
3079 | |
---|
3080 | do_legacy_token_warning(); |
---|
3081 | |
---|
3082 | switch (cmdtok) { |
---|
3083 | case CMD_EXPORT: |
---|
3084 | if (!f_export_ok) |
---|
3085 | /* TRANSLATORS: The *EXPORT command is only valid just after *BEGIN |
---|
3086 | * <SURVEY>, so this would generate this error: |
---|
3087 | * |
---|
3088 | * *begin fred |
---|
3089 | * 1 2 1.23 045 -6 |
---|
3090 | * *export 2 |
---|
3091 | * *end fred */ |
---|
3092 | compile_diagnostic(DIAG_ERR, /**EXPORT must immediately follow “*BEGIN <SURVEY>”*/57); |
---|
3093 | break; |
---|
3094 | case CMD_ALIAS: |
---|
3095 | case CMD_CALIBRATE: |
---|
3096 | case CMD_CARTESIAN: |
---|
3097 | case CMD_CASE: |
---|
3098 | case CMD_COPYRIGHT: |
---|
3099 | case CMD_CS: |
---|
3100 | case CMD_DATA: |
---|
3101 | case CMD_DATE: |
---|
3102 | case CMD_DECLINATION: |
---|
3103 | case CMD_FLAGS: |
---|
3104 | case CMD_INFER: |
---|
3105 | case CMD_INSTRUMENT: |
---|
3106 | case CMD_REF: |
---|
3107 | case CMD_REQUIRE: |
---|
3108 | case CMD_SD: |
---|
3109 | case CMD_SET: |
---|
3110 | case CMD_TEAM: |
---|
3111 | case CMD_TITLE: |
---|
3112 | case CMD_TRUNCATE: |
---|
3113 | case CMD_UNITS: |
---|
3114 | /* These can all occur between *begin and *export */ |
---|
3115 | break; |
---|
3116 | case CMD_DEFAULT: { |
---|
3117 | // Issue warning here before we skipblanks(). |
---|
3118 | static int default_depr_count = 0; |
---|
3119 | if (default_depr_count < 5) { |
---|
3120 | /* TRANSLATORS: If you're unsure what "deprecated" means, see: |
---|
3121 | * https://en.wikipedia.org/wiki/Deprecation */ |
---|
3122 | compile_diagnostic(DIAG_WARN|DIAG_TOKEN, /**DEFAULT is deprecated - use *CALIBRATE/DATA/SD/UNITS with argument DEFAULT instead*/20); |
---|
3123 | if (++default_depr_count == 5) |
---|
3124 | compile_diagnostic(DIAG_INFO, /*Further uses of this deprecated feature will not be reported*/95); |
---|
3125 | } |
---|
3126 | /* Can occur between *begin and *export */ |
---|
3127 | break; |
---|
3128 | } |
---|
3129 | case CMD_PREFIX: { |
---|
3130 | // Issue warning here before we skipblanks(). |
---|
3131 | static int prefix_depr_count = 0; |
---|
3132 | if (prefix_depr_count < 5) { |
---|
3133 | /* TRANSLATORS: If you're unsure what "deprecated" means, see: |
---|
3134 | * https://en.wikipedia.org/wiki/Deprecation */ |
---|
3135 | compile_diagnostic(DIAG_WARN|DIAG_TOKEN, /**prefix is deprecated - use *begin and *end instead*/109); |
---|
3136 | if (++prefix_depr_count == 5) |
---|
3137 | compile_diagnostic(DIAG_INFO, /*Further uses of this deprecated feature will not be reported*/95); |
---|
3138 | } |
---|
3139 | f_export_ok = false; |
---|
3140 | break; |
---|
3141 | } |
---|
3142 | default: |
---|
3143 | /* NB: additional handling for "*begin <survey>" in cmd_begin */ |
---|
3144 | f_export_ok = false; |
---|
3145 | break; |
---|
3146 | } |
---|
3147 | |
---|
3148 | skipblanks(); |
---|
3149 | |
---|
3150 | cmd_funcs[cmdtok](); |
---|
3151 | } |
---|