Export valid KML geometry for passages and walls
[survex.git] / src / commands.c
blob67d12866efab163c420a5907f645166e49393998
1 /* commands.c
2 * Code for directives
3 * Copyright (C) 1991-2003,2004,2005,2006,2010,2011,2012,2013,2014,2015,2016 Olly Betts
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.
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.
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
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
24 #include <assert.h>
25 #include <limits.h>
26 #include <stddef.h> /* for offsetof */
27 #include <string.h>
29 #include <proj_api.h>
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 "out.h"
41 #include "readval.h"
42 #include "str.h"
44 /*** Extracted from proj.4 projects (yuck, but grass also does this): */
45 struct DERIVS {
46 double x_l, x_p; /* derivatives of x for lambda-phi */
47 double y_l, y_p; /* derivatives of y for lambda-phi */
50 struct FACTORS {
51 struct DERIVS der;
52 double h, k; /* meridinal, parallel scales */
53 double omega, thetap; /* angular distortion, theta prime */
54 double conv; /* convergence */
55 double s; /* areal scale factor */
56 double a, b; /* max-min scale error */
57 int code; /* info as to analytics, see following */
60 int pj_factors(projLP, projPJ *, double, struct FACTORS *);
61 /***/
63 static projPJ proj_wgs84;
65 static void
66 default_grade(settings *s)
68 /* Values correspond to those in bcra5.svx */
69 s->Var[Q_POS] = (real)sqrd(0.05);
70 s->Var[Q_LENGTH] = (real)sqrd(0.05);
71 s->Var[Q_BACKLENGTH] = (real)sqrd(0.05);
72 s->Var[Q_COUNT] = (real)sqrd(0.05);
73 s->Var[Q_DX] = s->Var[Q_DY] = s->Var[Q_DZ] = (real)sqrd(0.05);
74 s->Var[Q_BEARING] = (real)sqrd(rad(0.5));
75 s->Var[Q_GRADIENT] = (real)sqrd(rad(0.5));
76 s->Var[Q_BACKBEARING] = (real)sqrd(rad(0.5));
77 s->Var[Q_BACKGRADIENT] = (real)sqrd(rad(0.5));
78 /* SD of plumbed legs (0.25 degrees?) */
79 s->Var[Q_PLUMB] = (real)sqrd(rad(0.25));
80 /* SD of level legs (0.25 degrees?) */
81 s->Var[Q_LEVEL] = (real)sqrd(rad(0.25));
82 s->Var[Q_DEPTH] = (real)sqrd(0.05);
85 static void
86 default_truncate(settings *s)
88 s->Truncate = INT_MAX;
91 static void
92 default_case(settings *s)
94 s->Case = LOWER;
97 static reading default_order[] = { Fr, To, Tape, Comp, Clino, End };
99 static void
100 default_style(settings *s)
102 s->style = STYLE_NORMAL;
103 s->ordering = default_order;
104 s->dash_for_anon_wall_station = fFalse;
107 static void
108 default_prefix(settings *s)
110 s->Prefix = root;
113 static void
114 default_translate(settings *s)
116 int i;
117 short *t;
118 if (s->next && s->next->Translate == s->Translate) {
119 t = ((short*)osmalloc(ossizeof(short) * 257)) + 1;
120 memcpy(t - 1, s->Translate - 1, sizeof(short) * 257);
121 s->Translate = t;
123 /* SVX_ASSERT(EOF==-1);*/ /* important, since we rely on this */
124 t = s->Translate;
125 memset(t - 1, 0, sizeof(short) * 257);
126 for (i = '0'; i <= '9'; i++) t[i] = SPECIAL_NAMES;
127 for (i = 'A'; i <= 'Z'; i++) t[i] = SPECIAL_NAMES;
128 for (i = 'a'; i <= 'z'; i++) t[i] = SPECIAL_NAMES;
130 t['\t'] |= SPECIAL_BLANK;
131 t[' '] |= SPECIAL_BLANK;
132 t[','] |= SPECIAL_BLANK;
133 t[';'] |= SPECIAL_COMMENT;
134 t['\032'] |= SPECIAL_EOL; /* Ctrl-Z, so olde DOS text files are handled ok */
135 t[EOF] |= SPECIAL_EOL;
136 t['\n'] |= SPECIAL_EOL;
137 t['\r'] |= SPECIAL_EOL;
138 t['*'] |= SPECIAL_KEYWORD;
139 t['-'] |= SPECIAL_OMIT;
140 t['\\'] |= SPECIAL_ROOT;
141 t['.'] |= SPECIAL_SEPARATOR;
142 t['_'] |= SPECIAL_NAMES;
143 t['-'] |= SPECIAL_NAMES; /* Added in 0.97 prerelease 4 */
144 t['.'] |= SPECIAL_DECIMAL;
145 t['-'] |= SPECIAL_MINUS;
146 t['+'] |= SPECIAL_PLUS;
147 #if 0 /* FIXME */
148 t['{'] |= SPECIAL_OPEN;
149 t['}'] |= SPECIAL_CLOSE;
150 #endif
153 void
154 default_units(settings *s)
156 int quantity;
157 for (quantity = 0; quantity < Q_MAC; quantity++) {
158 if (TSTBIT(ANG_QMASK, quantity))
159 s->units[quantity] = (real)(M_PI / 180.0); /* degrees */
160 else
161 s->units[quantity] = (real)1.0; /* metres */
163 s->f_clino_percent = s->f_backclino_percent = fFalse;
166 void
167 default_calib(settings *s)
169 int quantity;
170 for (quantity = 0; quantity < Q_MAC; quantity++) {
171 s->z[quantity] = (real)0.0;
172 s->sc[quantity] = (real)1.0;
176 static void
177 default_flags(settings *s)
179 s->flags = 0;
182 extern void
183 default_all(settings *s)
185 default_truncate(s);
186 s->infer = 0;
187 default_case(s);
188 default_style(s);
189 default_prefix(s);
190 default_translate(s);
191 default_grade(s);
192 default_units(s);
193 default_calib(s);
194 default_flags(s);
197 char *buffer = NULL;
198 static int buf_len;
200 static char *ucbuffer = NULL;
202 /* read token */
203 extern void
204 get_token(void)
206 skipblanks();
207 get_token_no_blanks();
210 extern void
211 get_token_no_blanks(void)
213 int i = -1;
215 s_zero(&buffer);
216 osfree(ucbuffer);
217 while (isalpha(ch)) {
218 s_catchar(&buffer, &buf_len, (char)ch);
219 nextch();
222 if (!buffer) s_catchar(&buffer, &buf_len, '\0');
224 ucbuffer = osmalloc(buf_len);
225 do {
226 i++;
227 ucbuffer[i] = toupper(buffer[i]);
228 } while (buffer[i]);
229 #if 0
230 printf("get_token_no_blanks() got “%s”\n", buffer);
231 #endif
234 /* read word */
235 static void
236 get_word(void)
238 s_zero(&buffer);
239 skipblanks();
240 while (!isBlank(ch) && !isEol(ch)) {
241 s_catchar(&buffer, &buf_len, (char)ch);
242 nextch();
245 if (!buffer) s_catchar(&buffer, &buf_len, '\0');
246 #if 0
247 printf("get_word() got “%s”\n", buffer);
248 #endif
251 /* match_tok() now uses binary chop
252 * tab argument should be alphabetically sorted (ascending)
254 extern int
255 match_tok(const sztok *tab, int tab_size)
257 int a = 0, b = tab_size - 1, c;
258 int r;
259 assert(tab_size > 0); /* catch empty table */
260 /* printf("[%d,%d]",a,b); */
261 while (a <= b) {
262 c = (unsigned)(a + b) / 2;
263 /* printf(" %d",c); */
264 r = strcmp(tab[c].sz, ucbuffer);
265 if (r == 0) return tab[c].tok; /* match */
266 if (r < 0)
267 a = c + 1;
268 else
269 b = c - 1;
271 return tab[tab_size].tok; /* no match */
274 typedef enum {
275 CMD_NULL = -1, CMD_ALIAS, CMD_BEGIN, CMD_CALIBRATE, CMD_CASE, CMD_COPYRIGHT,
276 CMD_CS, CMD_DATA, CMD_DATE, CMD_DECLINATION, CMD_DEFAULT, CMD_END,
277 CMD_ENTRANCE, CMD_EQUATE, CMD_EXPORT, CMD_FIX, CMD_FLAGS, CMD_INCLUDE,
278 CMD_INFER, CMD_INSTRUMENT, CMD_PREFIX, CMD_REF, CMD_REQUIRE, CMD_SD,
279 CMD_SET, CMD_SOLVE, CMD_TEAM, CMD_TITLE, CMD_TRUNCATE, CMD_UNITS
280 } cmds;
282 static const sztok cmd_tab[] = {
283 {"ALIAS", CMD_ALIAS},
284 {"BEGIN", CMD_BEGIN},
285 {"CALIBRATE", CMD_CALIBRATE},
286 {"CASE", CMD_CASE},
287 {"COPYRIGHT", CMD_COPYRIGHT},
288 {"CS", CMD_CS},
289 {"DATA", CMD_DATA},
290 {"DATE", CMD_DATE},
291 {"DECLINATION", CMD_DECLINATION},
292 #ifndef NO_DEPRECATED
293 {"DEFAULT", CMD_DEFAULT},
294 #endif
295 {"END", CMD_END},
296 {"ENTRANCE", CMD_ENTRANCE},
297 {"EQUATE", CMD_EQUATE},
298 {"EXPORT", CMD_EXPORT},
299 {"FIX", CMD_FIX},
300 {"FLAGS", CMD_FLAGS},
301 {"INCLUDE", CMD_INCLUDE},
302 {"INFER", CMD_INFER},
303 {"INSTRUMENT",CMD_INSTRUMENT},
304 #ifndef NO_DEPRECATED
305 {"PREFIX", CMD_PREFIX},
306 #endif
307 {"REF", CMD_REF},
308 {"REQUIRE", CMD_REQUIRE},
309 {"SD", CMD_SD},
310 {"SET", CMD_SET},
311 {"SOLVE", CMD_SOLVE},
312 {"TEAM", CMD_TEAM},
313 {"TITLE", CMD_TITLE},
314 {"TRUNCATE", CMD_TRUNCATE},
315 {"UNITS", CMD_UNITS},
316 {NULL, CMD_NULL}
319 /* masks for units which are length and angles respectively */
320 #define LEN_UMASK (BIT(UNITS_METRES) | BIT(UNITS_FEET) | BIT(UNITS_YARDS))
321 #define ANG_UMASK (BIT(UNITS_DEGS) | BIT(UNITS_GRADS) | BIT(UNITS_MINUTES))
323 /* ordering must be the same as the units enum */
324 const real factor_tab[] = {
325 1.0, METRES_PER_FOOT, (METRES_PER_FOOT*3.0),
326 (M_PI/180.0), (M_PI/200.0), 0.01, (M_PI/180.0/60.0)
329 const int units_to_msgno[] = {
330 /*m*/424,
331 /*ft*/428,
332 -1, /* yards */
333 /*°*/344,
334 /*ᵍ*/345,
335 /*%*/96,
336 -1 /* minutes */
339 int get_length_units(int quantity) {
340 double factor = pcs->units[quantity];
341 if (fabs(factor - METRES_PER_FOOT) <= REAL_EPSILON ||
342 fabs(factor - METRES_PER_FOOT * 3.0) <= REAL_EPSILON) {
343 return UNITS_FEET;
345 return UNITS_METRES;
348 int get_angle_units(int quantity) {
349 double factor = pcs->units[quantity];
350 if (fabs(factor - M_PI / 200.0) <= REAL_EPSILON) {
351 return UNITS_GRADS;
353 return UNITS_DEGS;
356 static int
357 get_units(unsigned long qmask, bool percent_ok)
359 static const sztok utab[] = {
360 {"DEGREES", UNITS_DEGS },
361 {"DEGS", UNITS_DEGS },
362 {"FEET", UNITS_FEET },
363 {"GRADS", UNITS_GRADS },
364 {"METERS", UNITS_METRES },
365 {"METRES", UNITS_METRES },
366 {"METRIC", UNITS_METRES },
367 {"MILS", UNITS_GRADS },
368 {"MINUTES", UNITS_MINUTES },
369 {"PERCENT", UNITS_PERCENT },
370 {"PERCENTAGE", UNITS_PERCENT },
371 {"YARDS", UNITS_YARDS },
372 {NULL, UNITS_NULL }
374 int units;
375 get_token();
376 units = match_tok(utab, TABSIZE(utab));
377 if (units == UNITS_NULL) {
378 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*Unknown units “%s”*/35, buffer);
379 return UNITS_NULL;
381 if (units == UNITS_PERCENT && percent_ok &&
382 !(qmask & ~(BIT(Q_GRADIENT)|BIT(Q_BACKGRADIENT)))) {
383 return units;
385 if (((qmask & LEN_QMASK) && !TSTBIT(LEN_UMASK, units)) ||
386 ((qmask & ANG_QMASK) && !TSTBIT(ANG_UMASK, units))) {
387 /* TRANSLATORS: Note: In English you talk about the *units* of a single
388 * measurement, but the correct term in other languages may be singular.
390 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*Invalid units “%s” for quantity*/37, buffer);
391 return UNITS_NULL;
393 return units;
396 /* returns mask with bit x set to indicate quantity x specified */
397 static unsigned long
398 get_qlist(unsigned long mask_bad)
400 static const sztok qtab[] = {
401 {"ALTITUDE", Q_DZ },
402 {"BACKBEARING", Q_BACKBEARING },
403 {"BACKCLINO", Q_BACKGRADIENT }, /* alternative name */
404 {"BACKCOMPASS", Q_BACKBEARING }, /* alternative name */
405 {"BACKGRADIENT", Q_BACKGRADIENT },
406 {"BACKLENGTH", Q_BACKLENGTH },
407 {"BACKTAPE", Q_BACKLENGTH }, /* alternative name */
408 {"BEARING", Q_BEARING },
409 {"CEILING", Q_UP }, /* alternative name */
410 {"CLINO", Q_GRADIENT }, /* alternative name */
411 {"COMPASS", Q_BEARING }, /* alternative name */
412 {"COUNT", Q_COUNT },
413 {"COUNTER", Q_COUNT }, /* alternative name */
414 {"DECLINATION", Q_DECLINATION },
415 {"DEFAULT", Q_DEFAULT }, /* not a real quantity... */
416 {"DEPTH", Q_DEPTH },
417 {"DOWN", Q_DOWN },
418 {"DX", Q_DX }, /* alternative name */
419 {"DY", Q_DY }, /* alternative name */
420 {"DZ", Q_DZ }, /* alternative name */
421 {"EASTING", Q_DX },
422 {"FLOOR", Q_DOWN }, /* alternative name */
423 {"GRADIENT", Q_GRADIENT },
424 {"LEFT", Q_LEFT },
425 {"LENGTH", Q_LENGTH },
426 {"LEVEL", Q_LEVEL},
427 {"NORTHING", Q_DY },
428 {"PLUMB", Q_PLUMB},
429 {"POSITION", Q_POS },
430 {"RIGHT", Q_RIGHT },
431 {"TAPE", Q_LENGTH }, /* alternative name */
432 {"UP", Q_UP },
433 {NULL, Q_NULL }
435 unsigned long qmask = 0;
436 int tok;
437 filepos fp;
439 while (1) {
440 get_pos(&fp);
441 get_token();
442 tok = match_tok(qtab, TABSIZE(qtab));
443 if (tok == Q_DEFAULT && !(mask_bad & BIT(Q_DEFAULT))) {
444 /* Only recognise DEFAULT if it is the first quantity, and then don't
445 * look for any more. */
446 if (qmask == 0)
447 return BIT(Q_DEFAULT);
448 break;
450 /* bail out if we reach the table end with no match */
451 if (tok == Q_NULL) break;
452 qmask |= BIT(tok);
453 if (qmask & mask_bad) {
454 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*Unknown instrument “%s”*/39, buffer);
455 return 0;
459 if (qmask == 0) {
460 /* TRANSLATORS: A "quantity" is something measured like "LENGTH",
461 * "BEARING", "ALTITUDE", etc. */
462 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*Unknown quantity “%s”*/34, buffer);
463 } else {
464 set_pos(&fp);
467 return qmask;
470 #define SPECIAL_UNKNOWN 0
471 static void
472 cmd_set(void)
474 static const sztok chartab[] = {
475 {"BLANK", SPECIAL_BLANK },
476 /*FIXME {"CLOSE", SPECIAL_CLOSE }, */
477 {"COMMENT", SPECIAL_COMMENT },
478 {"DECIMAL", SPECIAL_DECIMAL },
479 {"EOL", SPECIAL_EOL }, /* EOL won't work well */
480 {"KEYWORD", SPECIAL_KEYWORD },
481 {"MINUS", SPECIAL_MINUS },
482 {"NAMES", SPECIAL_NAMES },
483 {"OMIT", SPECIAL_OMIT },
484 /*FIXME {"OPEN", SPECIAL_OPEN }, */
485 {"PLUS", SPECIAL_PLUS },
486 #ifndef NO_DEPRECATED
487 {"ROOT", SPECIAL_ROOT },
488 #endif
489 {"SEPARATOR", SPECIAL_SEPARATOR },
490 {NULL, SPECIAL_UNKNOWN }
492 int mask;
493 int i;
495 get_token();
496 mask = match_tok(chartab, TABSIZE(chartab));
498 if (mask == SPECIAL_UNKNOWN) {
499 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*Unknown character class “%s”*/42, buffer);
500 return;
503 #ifndef NO_DEPRECATED
504 if (mask == SPECIAL_ROOT) {
505 if (root_depr_count < 5) {
506 /* TRANSLATORS: Use of the ROOT character (which is "\" by default) is
507 * deprecated, so this error would be generated by:
509 * *equate \foo.7 1
511 * If you're unsure what "deprecated" means, see:
512 * https://en.wikipedia.org/wiki/Deprecation */
513 compile_diagnostic(DIAG_WARN|DIAG_BUF, /*ROOT is deprecated*/25);
514 if (++root_depr_count == 5)
515 /* TRANSLATORS: If you're unsure what "deprecated" means, see:
516 * https://en.wikipedia.org/wiki/Deprecation */
517 compile_diagnostic(DIAG_WARN, /*Further uses of this deprecated feature will not be reported*/95);
520 #endif
522 /* if we're currently using an inherited translation table, allocate a new
523 * table, and copy old one into it */
524 if (pcs->next && pcs->next->Translate == pcs->Translate) {
525 short *p;
526 p = ((short*)osmalloc(ossizeof(short) * 257)) + 1;
527 memcpy(p - 1, pcs->Translate - 1, sizeof(short) * 257);
528 pcs->Translate = p;
531 skipblanks();
533 /* clear this flag for all non-alphanums */
534 for (i = 0; i < 256; i++)
535 if (!isalnum(i)) pcs->Translate[i] &= ~mask;
537 /* now set this flag for all specified chars */
538 while (!isEol(ch)) {
539 if (!isalnum(ch)) {
540 pcs->Translate[ch] |= mask;
541 } else if (tolower(ch) == 'x') {
542 int hex;
543 filepos fp;
544 get_pos(&fp);
545 nextch();
546 if (!isxdigit(ch)) {
547 set_pos(&fp);
548 break;
550 hex = isdigit(ch) ? ch - '0' : tolower(ch) - 'a';
551 nextch();
552 if (!isxdigit(ch)) {
553 set_pos(&fp);
554 break;
556 hex = hex << 4 | (isdigit(ch) ? ch - '0' : tolower(ch) - 'a');
557 pcs->Translate[hex] |= mask;
558 } else {
559 break;
561 nextch();
565 static void
566 check_reentry(prefix *survey, const filepos* fpos_ptr)
568 /* Don't try to check "*prefix \" or "*begin \" */
569 if (!survey->up) return;
570 if (TSTBIT(survey->sflags, SFLAGS_PREFIX_ENTERED)) {
571 static int reenter_depr_count = 0;
572 filepos fp_tmp;
574 if (reenter_depr_count >= 5)
575 return;
577 get_pos(&fp_tmp);
578 set_pos(fpos_ptr);
579 /* TRANSLATORS: The first of two warnings given when a survey which has
580 * already been completed is reentered. This example file crawl.svx:
582 * *begin crawl ; <- second warning here
583 * 1 2 9.45 234 -01
584 * *end crawl
585 * *begin crawl ; <- first warning here
586 * 2 3 7.67 223 -03
587 * *end crawl
589 * Would lead to:
591 * crawl.svx:4: Reentering an existing survey is deprecated
592 * crawl.svx:1: Originally entered here
594 * If you're unsure what "deprecated" means, see:
595 * https://en.wikipedia.org/wiki/Deprecation */
596 compile_diagnostic(DIAG_WARN|DIAG_TOKEN, /*Reentering an existing survey is deprecated*/29);
597 set_pos(&fp_tmp);
598 /* TRANSLATORS: The second of two warnings given when a survey which has
599 * already been completed is reentered. This example file crawl.svx:
601 * *begin crawl
602 * 1 2 9.45 234 -01 # <- second warning here
603 * *end crawl
604 * *begin crawl # <- first warning here
605 * 2 3 7.67 223 -03
606 * *end crawl
608 * Would lead to:
610 * crawl.svx:3: Reentering an existing survey is deprecated
611 * crawl.svx:1: Originally entered here
613 * If you're unsure what "deprecated" means, see:
614 * https://en.wikipedia.org/wiki/Deprecation */
615 compile_diagnostic_pfx(DIAG_WARN, survey, /*Originally entered here*/30);
616 if (++reenter_depr_count == 5) {
617 /* After we've warned about 5 uses of the same deprecated feature, we
618 * give up for the rest of the current processing run.
620 * If you're unsure what "deprecated" means, see:
621 * https://en.wikipedia.org/wiki/Deprecation */
622 compile_diagnostic(DIAG_WARN, /*Further uses of this deprecated feature will not be reported*/95);
624 } else {
625 survey->sflags |= BIT(SFLAGS_PREFIX_ENTERED);
626 survey->filename = file.filename;
627 survey->line = file.line;
631 #ifndef NO_DEPRECATED
632 static void
633 cmd_prefix(void)
635 static int prefix_depr_count = 0;
636 prefix *survey;
637 filepos fp;
638 /* Issue warning first, so "*prefix \" warns first that *prefix is
639 * deprecated and then that ROOT is...
641 if (prefix_depr_count < 5) {
642 /* TRANSLATORS: If you're unsure what "deprecated" means, see:
643 * https://en.wikipedia.org/wiki/Deprecation */
644 compile_diagnostic(DIAG_WARN|DIAG_BUF, /**prefix is deprecated - use *begin and *end instead*/6);
645 if (++prefix_depr_count == 5)
646 compile_diagnostic(DIAG_WARN, /*Further uses of this deprecated feature will not be reported*/95);
648 get_pos(&fp);
649 survey = read_prefix(PFX_SURVEY|PFX_ALLOW_ROOT);
650 pcs->Prefix = survey;
651 check_reentry(survey, &fp);
653 #endif
655 static void
656 cmd_alias(void)
658 /* Currently only two forms are supported:
659 * *alias station - ..
660 * *alias station -
662 get_token();
663 if (strcmp(ucbuffer, "STATION") != 0)
664 goto bad;
665 get_word();
666 if (strcmp(buffer, "-") != 0)
667 goto bad;
668 get_word();
669 if (*buffer && strcmp(buffer, "..") != 0)
670 goto bad;
671 pcs->dash_for_anon_wall_station = (*buffer != '\0');
672 return;
673 bad:
674 compile_diagnostic(DIAG_ERR|DIAG_SKIP, /*Bad *alias command*/397);
677 static void
678 cmd_begin(void)
680 settings *pcsNew;
682 pcsNew = osnew(settings);
683 *pcsNew = *pcs; /* copy contents */
684 pcsNew->begin_lineno = file.line;
685 pcsNew->next = pcs;
686 pcs = pcsNew;
688 skipblanks();
689 pcs->begin_survey = NULL;
690 if (!isEol(ch) && !isComm(ch)) {
691 filepos fp;
692 prefix *survey;
693 get_pos(&fp);
694 survey = read_prefix(PFX_SURVEY|PFX_ALLOW_ROOT|PFX_WARN_SEPARATOR);
695 pcs->begin_survey = survey;
696 pcs->Prefix = survey;
697 check_reentry(survey, &fp);
698 f_export_ok = fTrue;
702 extern void
703 free_settings(settings *p) {
704 /* don't free default ordering or ordering used by parent */
705 const reading *order = p->ordering;
706 if (order != default_order && (!p->next || order != p->next->ordering))
707 osfree((reading*)order);
709 /* free Translate if not used by parent */
710 if (!p->next || p->Translate != p->next->Translate)
711 osfree(p->Translate - 1);
713 /* free meta if not used by parent, or in this block */
714 if (p->meta && (!p->next || p->meta != p->next->meta) && p->meta->ref_count == 0)
715 osfree(p->meta);
717 /* free proj if not used by parent, or as the output projection */
718 if (p->proj && (!p->next || p->proj != p->next->proj) && p->proj != proj_out)
719 pj_free(p->proj);
721 osfree(p);
724 static void
725 cmd_end(void)
727 settings *pcsParent;
728 prefix *survey, *begin_survey;
729 filepos fp;
731 pcsParent = pcs->next;
733 if (pcs->begin_lineno == 0) {
734 if (pcsParent == NULL) {
735 /* more ENDs than BEGINs */
736 compile_diagnostic(DIAG_ERR|DIAG_SKIP, /*No matching BEGIN*/192);
737 } else {
738 compile_diagnostic(DIAG_ERR|DIAG_SKIP, /*END with no matching BEGIN in this file*/22);
740 return;
743 begin_survey = pcs->begin_survey;
745 SVX_ASSERT(pcsParent);
746 free_settings(pcs);
747 pcs = pcsParent;
749 /* note need to read using root *before* BEGIN */
750 skipblanks();
751 if (isEol(ch) || isComm(ch)) {
752 survey = NULL;
753 } else {
754 get_pos(&fp);
755 survey = read_prefix(PFX_SURVEY|PFX_ALLOW_ROOT);
758 if (survey != begin_survey) {
759 if (survey) {
760 set_pos(&fp);
761 if (!begin_survey) {
762 /* TRANSLATORS: Used when a BEGIN command has no survey, but the
763 * END command does, e.g.:
765 * *begin
766 * 1 2 10.00 178 -01
767 * *end entrance <--[Message given here] */
768 compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Matching BEGIN command has no survey name*/36);
769 } else {
770 /* TRANSLATORS: *BEGIN <survey> and *END <survey> should have the
771 * same <survey> if it’s given at all */
772 compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Survey name doesn’t match BEGIN*/193);
774 skipline();
775 } else {
776 /* TRANSLATORS: Used when a BEGIN command has a survey name, but the
777 * END command omits it, e.g.:
779 * *begin entrance
780 * 1 2 10.00 178 -01
781 * *end <--[Message given here] */
782 compile_diagnostic(DIAG_WARN|DIAG_COL, /*Survey name omitted from END*/194);
787 static void
788 cmd_entrance(void)
790 prefix *pfx = read_prefix(PFX_STATION);
791 pfx->sflags |= BIT(SFLAGS_ENTRANCE);
794 static const prefix * first_fix_name = NULL;
795 static const char * first_fix_filename;
796 static unsigned first_fix_line;
798 static void
799 cmd_fix(void)
801 prefix *fix_name;
802 node *stn = NULL;
803 static prefix *name_omit_already = NULL;
804 static const char * name_omit_already_filename = NULL;
805 static unsigned int name_omit_already_line;
806 real x, y, z;
807 filepos fp;
809 fix_name = read_prefix(PFX_STATION|PFX_ALLOW_ROOT);
810 fix_name->sflags |= BIT(SFLAGS_FIXED);
812 get_pos(&fp);
813 get_token();
814 if (strcmp(ucbuffer, "REFERENCE") == 0) {
815 /* suppress "unused fixed point" warnings for this station */
816 fix_name->sflags |= BIT(SFLAGS_USED);
817 } else {
818 if (*ucbuffer) set_pos(&fp);
821 x = read_numeric(fTrue);
822 if (x == HUGE_REAL) {
823 /* If the end of the line isn't blank, read a number after all to
824 * get a more helpful error message */
825 if (!isEol(ch) && !isComm(ch)) x = read_numeric(fFalse);
827 if (x == HUGE_REAL) {
828 if (pcs->proj || proj_out) {
829 compile_diagnostic(DIAG_ERR|DIAG_COL|DIAG_SKIP, /*Coordinates can't be omitted when coordinate system has been specified*/439);
830 return;
833 if (fix_name == name_omit_already) {
834 compile_diagnostic(DIAG_WARN|DIAG_COL, /*Same station fixed twice with no coordinates*/61);
835 return;
838 /* TRANSLATORS: " *fix a " gives this message: */
839 compile_diagnostic(DIAG_WARN|DIAG_COL, /*FIX command with no coordinates - fixing at (0,0,0)*/54);
841 if (name_omit_already) {
842 /* TRANSLATORS: Emitted after second and subsequent "FIX command with
843 * no coordinates - fixing at (0,0,0)" warnings.
845 compile_diagnostic_at(DIAG_ERR|DIAG_COL,
846 name_omit_already_filename,
847 name_omit_already_line,
848 /*Already had FIX command with no coordinates for station “%s”*/441,
849 sprint_prefix(name_omit_already));
850 } else {
851 name_omit_already = fix_name;
852 name_omit_already_filename = file.filename;
853 name_omit_already_line = file.line;
856 x = y = z = (real)0.0;
857 } else {
858 real sdx;
859 y = read_numeric(fFalse);
860 z = read_numeric(fFalse);
862 if (pcs->proj && proj_out) {
863 if (pj_is_latlong(pcs->proj)) {
864 /* PROJ expects lat and long in radians. */
865 x = rad(x);
866 y = rad(y);
868 int r = pj_transform(pcs->proj, proj_out, 1, 1, &x, &y, &z);
869 if (r != 0) {
870 compile_diagnostic(DIAG_ERR, /*Failed to convert coordinates: %s*/436, pj_strerrno(r));
872 } else if (pcs->proj) {
873 compile_diagnostic(DIAG_ERR, /*The input projection is set but the output projection isn't*/437);
874 } else if (proj_out) {
875 compile_diagnostic(DIAG_ERR, /*The output projection is set but the input projection isn't*/438);
878 get_pos(&fp);
879 sdx = read_numeric(fTrue);
880 if (sdx <= 0) {
881 set_pos(&fp);
882 compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_NUM, /*Standard deviation must be positive*/48);
883 return;
885 if (sdx != HUGE_REAL) {
886 real sdy, sdz;
887 real cxy = 0, cyz = 0, czx = 0;
888 get_pos(&fp);
889 sdy = read_numeric(fTrue);
890 if (sdy == HUGE_REAL) {
891 /* only one variance given */
892 sdy = sdz = sdx;
893 } else {
894 if (sdy <= 0) {
895 set_pos(&fp);
896 compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_NUM, /*Standard deviation must be positive*/48);
897 return;
899 get_pos(&fp);
900 sdz = read_numeric(fTrue);
901 if (sdz == HUGE_REAL) {
902 /* two variances given - horizontal & vertical */
903 sdz = sdy;
904 sdy = sdx;
905 } else {
906 if (sdz <= 0) {
907 set_pos(&fp);
908 compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_NUM, /*Standard deviation must be positive*/48);
909 return;
911 cxy = read_numeric(fTrue);
912 if (cxy != HUGE_REAL) {
913 /* covariances given */
914 cyz = read_numeric(fFalse);
915 czx = read_numeric(fFalse);
916 } else {
917 cxy = 0;
921 stn = StnFromPfx(fix_name);
922 if (!fixed(stn)) {
923 node *fixpt = osnew(node);
924 prefix *name;
925 name = osnew(prefix);
926 name->pos = osnew(pos);
927 name->ident = NULL;
928 name->shape = 0;
929 fixpt->name = name;
930 name->stn = fixpt;
931 name->up = NULL;
932 if (TSTBIT(pcs->infer, INFER_EXPORTS)) {
933 name->min_export = USHRT_MAX;
934 } else {
935 name->min_export = 0;
937 name->max_export = 0;
938 name->sflags = 0;
939 add_stn_to_list(&stnlist, fixpt);
940 POS(fixpt, 0) = x;
941 POS(fixpt, 1) = y;
942 POS(fixpt, 2) = z;
943 fix(fixpt);
944 fixpt->leg[0] = fixpt->leg[1] = fixpt->leg[2] = NULL;
945 addfakeleg(fixpt, stn, 0, 0, 0,
946 sdx * sdx, sdy * sdy, sdz * sdz
947 #ifndef NO_COVARIANCES
948 , cxy, cyz, czx
949 #endif
953 if (!first_fix_name) {
954 /* We track if we've fixed a station yet, and if so what the name
955 * of the first fix was, so that we can issue an error if the
956 * output coordinate system is set after fixing a station. */
957 first_fix_name = fix_name;
958 first_fix_filename = file.filename;
959 first_fix_line = file.line;
962 return;
966 if (!first_fix_name) {
967 /* We track if we've fixed a station yet, and if so what the name of the
968 * first fix was, so that we can issue an error if the output coordinate
969 * system is set after fixing a station. */
970 first_fix_name = fix_name;
971 first_fix_filename = file.filename;
972 first_fix_line = file.line;
975 stn = StnFromPfx(fix_name);
976 if (!fixed(stn)) {
977 POS(stn, 0) = x;
978 POS(stn, 1) = y;
979 POS(stn, 2) = z;
980 fix(stn);
981 return;
984 if (x != POS(stn, 0) || y != POS(stn, 1) || z != POS(stn, 2)) {
985 compile_diagnostic(DIAG_ERR, /*Station already fixed or equated to a fixed point*/46);
986 return;
988 /* TRANSLATORS: *fix a 1 2 3 / *fix a 1 2 3 */
989 compile_diagnostic(DIAG_WARN, /*Station already fixed at the same coordinates*/55);
992 static void
993 cmd_flags(void)
995 static const sztok flagtab[] = {
996 {"DUPLICATE", FLAGS_DUPLICATE },
997 {"NOT", FLAGS_NOT },
998 {"SPLAY", FLAGS_SPLAY },
999 {"SURFACE", FLAGS_SURFACE },
1000 {NULL, FLAGS_UNKNOWN }
1002 bool fNot = fFalse;
1003 bool fEmpty = fTrue;
1004 while (1) {
1005 int flag;
1006 get_token();
1007 /* If buffer is empty, it could mean end of line, or maybe
1008 * some non-letter junk which is better reported later */
1009 if (!buffer[0]) break;
1011 fEmpty = fFalse;
1012 flag = match_tok(flagtab, TABSIZE(flagtab));
1013 /* treat the second NOT in "NOT NOT" as an unknown flag */
1014 if (flag == FLAGS_UNKNOWN || (fNot && flag == FLAGS_NOT)) {
1015 compile_diagnostic(DIAG_ERR|DIAG_BUF, /*FLAG “%s” unknown*/68, buffer);
1016 /* Recover from “*FLAGS NOT BOGUS SURFACE” by ignoring "NOT BOGUS" */
1017 fNot = fFalse;
1018 } else if (flag == FLAGS_NOT) {
1019 fNot = fTrue;
1020 } else if (fNot) {
1021 pcs->flags &= ~BIT(flag);
1022 fNot = fFalse;
1023 } else {
1024 pcs->flags |= BIT(flag);
1028 if (fNot) {
1029 compile_diagnostic(DIAG_ERR|DIAG_BUF, /*Expecting “DUPLICATE”, “SPLAY”, or “SURFACE”*/188);
1030 } else if (fEmpty) {
1031 compile_diagnostic(DIAG_ERR|DIAG_BUF, /*Expecting “NOT”, “DUPLICATE”, “SPLAY”, or “SURFACE”*/189);
1035 static void
1036 cmd_equate(void)
1038 prefix *name1, *name2;
1039 bool fOnlyOneStn = fTrue; /* to trap eg *equate entrance.6 */
1040 filepos fp;
1042 get_pos(&fp);
1043 name1 = read_prefix(PFX_STATION|PFX_ALLOW_ROOT|PFX_SUSPECT_TYPO);
1044 while (fTrue) {
1045 name2 = name1;
1046 skipblanks();
1047 if (isEol(ch) || isComm(ch)) {
1048 if (fOnlyOneStn) {
1049 set_pos(&fp);
1050 /* TRANSLATORS: EQUATE is a command name, so shouldn’t be
1051 * translated.
1053 * Here "station" is a survey station, not a train station.
1055 compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_TOKEN, /*Only one station in EQUATE command*/33);
1057 return;
1060 name1 = read_prefix(PFX_STATION|PFX_ALLOW_ROOT|PFX_SUSPECT_TYPO);
1061 process_equate(name1, name2);
1062 fOnlyOneStn = fFalse;
1066 static void
1067 report_missing_export(prefix *pfx, int depth)
1069 char *s;
1070 const char *p;
1071 prefix *survey = pfx;
1072 int i;
1073 for (i = depth + 1; i; i--) {
1074 survey = survey->up;
1075 SVX_ASSERT(survey);
1077 s = osstrdup(sprint_prefix(survey));
1078 p = sprint_prefix(pfx);
1079 if (survey->filename) {
1080 /* TRANSLATORS: A station must be exported out of each level it is in, so
1081 * this would give "Station “\outer.inner.1” not exported from survey
1082 * “\outer”)":
1084 * *equate entrance outer.inner.1
1085 * *begin outer
1086 * *begin inner
1087 * *export 1
1088 * 1 2 1.23 045 -6
1089 * *end inner
1090 * *end outer
1092 * Here "survey" is a "cave map" rather than list of questions - it should be
1093 * translated to the terminology that cavers using the language would use.
1095 compile_diagnostic_pfx(DIAG_ERR, survey,
1096 /*Station “%s” not exported from survey “%s”*/26, p, s);
1097 } else {
1098 compile_diagnostic(DIAG_ERR, /*Station “%s” not exported from survey “%s”*/26, p, s);
1100 osfree(s);
1103 static void
1104 cmd_export(void)
1106 prefix *pfx;
1108 fExportUsed = fTrue;
1109 do {
1110 int depth = 0;
1111 pfx = read_prefix(PFX_STATION|PFX_NEW);
1112 if (pfx == NULL) {
1113 /* The argument was an existing station. */
1114 /* FIXME */
1115 } else {
1116 prefix *p = pfx;
1117 while (p != NULL && p != pcs->Prefix) {
1118 depth++;
1119 p = p->up;
1121 /* Something like: *export \foo, but we've excluded use of root */
1122 SVX_ASSERT(p);
1124 /* *export \ or similar bogus stuff */
1125 SVX_ASSERT(depth);
1126 #if 0
1127 printf("C min %d max %d depth %d pfx %s\n",
1128 pfx->min_export, pfx->max_export, depth, sprint_prefix(pfx));
1129 #endif
1130 if (pfx->min_export == 0) {
1131 /* not encountered *export for this name before */
1132 if (pfx->max_export > depth) report_missing_export(pfx, depth);
1133 pfx->min_export = pfx->max_export = depth;
1134 } else if (pfx->min_export != USHRT_MAX) {
1135 /* FIXME: what to do if a station is marked for inferred exports
1136 * but is then explicitly exported? Currently we just ignore the
1137 * explicit export... */
1138 if (pfx->min_export - 1 > depth) {
1139 report_missing_export(pfx, depth);
1140 } else if (pfx->min_export - 1 < depth) {
1141 /* TRANSLATORS: Here "station" is a survey station, not a train station.
1143 * Exporting a station twice gives this error:
1145 * *begin example
1146 * *export 1
1147 * *export 1
1148 * 1 2 1.24 045 -6
1149 * *end example */
1150 compile_diagnostic(DIAG_ERR, /*Station “%s” already exported*/66,
1151 sprint_prefix(pfx));
1153 pfx->min_export = depth;
1155 skipblanks();
1156 } while (!isEol(ch) && !isComm(ch));
1159 static void
1160 cmd_data(void)
1162 static const sztok dtab[] = {
1163 {"ALTITUDE", Dz },
1164 {"BACKBEARING", BackComp },
1165 {"BACKCLINO", BackClino }, /* alternative name */
1166 {"BACKCOMPASS", BackComp }, /* alternative name */
1167 {"BACKGRADIENT", BackClino },
1168 {"BACKLENGTH", BackTape },
1169 {"BACKTAPE", BackTape }, /* alternative name */
1170 {"BEARING", Comp },
1171 {"CEILING", Up }, /* alternative name */
1172 {"CLINO", Clino }, /* alternative name */
1173 {"COMPASS", Comp }, /* alternative name */
1174 {"COUNT", Count }, /* FrCount&ToCount in multiline */
1175 {"DEPTH", Depth }, /* FrDepth&ToDepth in multiline */
1176 {"DEPTHCHANGE", DepthChange },
1177 {"DIRECTION", Dir },
1178 {"DOWN", Down },
1179 {"DX", Dx },
1180 {"DY", Dy },
1181 {"DZ", Dz },
1182 {"EASTING", Dx },
1183 {"FLOOR", Down }, /* alternative name */
1184 {"FROM", Fr },
1185 {"FROMCOUNT", FrCount },
1186 {"FROMDEPTH", FrDepth },
1187 {"GRADIENT", Clino },
1188 {"IGNORE", Ignore },
1189 {"IGNOREALL", IgnoreAll },
1190 {"LEFT", Left },
1191 {"LENGTH", Tape },
1192 {"NEWLINE", Newline },
1193 {"NORTHING", Dy },
1194 {"RIGHT", Right },
1195 {"STATION", Station }, /* Fr&To in multiline */
1196 {"TAPE", Tape }, /* alternative name */
1197 {"TO", To },
1198 {"TOCOUNT", ToCount },
1199 {"TODEPTH", ToDepth },
1200 {"UP", Up },
1201 {NULL, End }
1204 #define MASK_stns BIT(Fr) | BIT(To) | BIT(Station)
1205 #define MASK_tape BIT(Tape) | BIT(BackTape) | BIT(FrCount) | BIT(ToCount) | BIT(Count)
1206 #define MASK_dpth BIT(FrDepth) | BIT(ToDepth) | BIT(Depth) | BIT(DepthChange)
1207 #define MASK_comp BIT(Comp) | BIT(BackComp)
1208 #define MASK_clin BIT(Clino) | BIT(BackClino)
1210 #define MASK_NORMAL MASK_stns | BIT(Dir) | MASK_tape | MASK_comp | MASK_clin
1211 #define MASK_DIVING MASK_NORMAL | MASK_dpth
1212 #define MASK_CARTESIAN MASK_stns | BIT(Dx) | BIT(Dy) | BIT(Dz)
1213 #define MASK_CYLPOLAR MASK_stns | BIT(Dir) | MASK_tape | MASK_comp | MASK_dpth
1214 #define MASK_NOSURVEY MASK_stns
1215 #define MASK_PASSAGE BIT(Station) | BIT(Left) | BIT(Right) | BIT(Up) | BIT(Down)
1217 /* readings which may be given for each style */
1218 static const unsigned long mask[] = {
1219 MASK_NORMAL, MASK_DIVING, MASK_CARTESIAN, MASK_CYLPOLAR, MASK_NOSURVEY,
1220 MASK_PASSAGE
1223 /* readings which may be omitted for each style */
1224 static const unsigned long mask_optional[] = {
1225 BIT(Dir) | BIT(Clino) | BIT(BackClino),
1226 BIT(Dir) | BIT(Clino) | BIT(BackClino),
1228 BIT(Dir),
1230 0 /* BIT(Left) | BIT(Right) | BIT(Up) | BIT(Down), */
1233 /* all valid readings */
1234 static const unsigned long mask_all[] = {
1235 MASK_NORMAL | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End),
1236 MASK_DIVING | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End),
1237 MASK_CARTESIAN | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End),
1238 MASK_CYLPOLAR | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End),
1239 MASK_NOSURVEY | BIT(Ignore) | BIT(IgnoreAll) | BIT(End),
1240 MASK_PASSAGE | BIT(Ignore) | BIT(IgnoreAll) | BIT(End)
1242 #define STYLE_DEFAULT -2
1243 #define STYLE_UNKNOWN -1
1245 static const sztok styletab[] = {
1246 {"CARTESIAN", STYLE_CARTESIAN },
1247 {"CYLPOLAR", STYLE_CYLPOLAR },
1248 {"DEFAULT", STYLE_DEFAULT },
1249 {"DIVING", STYLE_DIVING },
1250 {"NORMAL", STYLE_NORMAL },
1251 {"NOSURVEY", STYLE_NOSURVEY },
1252 {"PASSAGE", STYLE_PASSAGE },
1253 {"TOPOFIL", STYLE_NORMAL },
1254 {NULL, STYLE_UNKNOWN }
1257 #define m_multi (BIT(Station) | BIT(Count) | BIT(Depth))
1259 int style, k = 0, kMac;
1260 reading *new_order, d;
1261 unsigned long mUsed = 0;
1262 char *style_name;
1263 int old_style = pcs->style;
1265 /* after a bad *data command ignore survey data until the next
1266 * *data command to avoid an avalanche of errors */
1267 pcs->style = STYLE_IGNORE;
1269 kMac = 6; /* minimum for NORMAL style */
1270 new_order = osmalloc(kMac * sizeof(reading));
1272 get_token();
1273 style = match_tok(styletab, TABSIZE(styletab));
1275 if (style == STYLE_DEFAULT) {
1276 default_style(pcs);
1277 return;
1280 if (style == STYLE_UNKNOWN) {
1281 if (!buffer[0]) {
1282 /* "*data" reinitialises the current style - for *data passage that
1283 * breaks the passage.
1285 style = old_style;
1286 goto reinit_style;
1288 /* TRANSLATORS: e.g. trying to refer to an invalid FNORD data style */
1289 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*Data style “%s” unknown*/65, buffer);
1290 return;
1293 skipblanks();
1294 #ifndef NO_DEPRECATED
1295 /* Olde syntax had optional field for survey grade, so allow an omit
1296 * but issue a warning about it */
1297 if (isOmit(ch)) {
1298 static int data_depr_count = 0;
1299 if (data_depr_count < 5) {
1300 compile_diagnostic(DIAG_WARN|DIAG_BUF, /*“*data %s %c …” is deprecated - use “*data %s …” instead*/104,
1301 buffer, ch, buffer);
1302 if (++data_depr_count == 5)
1303 compile_diagnostic(DIAG_WARN, /*Further uses of this deprecated feature will not be reported*/95);
1305 nextch();
1307 #endif
1309 style_name = osstrdup(buffer);
1310 do {
1311 filepos fp;
1312 get_pos(&fp);
1313 get_token();
1314 d = match_tok(dtab, TABSIZE(dtab));
1315 /* only token allowed after IGNOREALL is NEWLINE */
1316 if (k && new_order[k - 1] == IgnoreAll && d != Newline) {
1317 set_pos(&fp);
1318 break;
1320 /* Note: an unknown token is reported as trailing garbage */
1321 if (!TSTBIT(mask_all[style], d)) {
1322 /* TRANSLATORS: a data "style" is something like NORMAL, DIVING, etc.
1323 * a "reading" is one of FROM, TO, TAPE, COMPASS, CLINO for NORMAL
1324 * neither style nor reading is a keyword in the program This error
1325 * complains about a depth gauge reading in normal style, for example
1327 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP,
1328 /*Reading “%s” not allowed in data style “%s”*/63,
1329 buffer, style_name);
1330 osfree(style_name);
1331 osfree(new_order);
1332 return;
1334 if (TSTBIT(mUsed, Newline) && TSTBIT(m_multi, d)) {
1335 /* TRANSLATORS: caused by e.g.
1337 * *data diving station newline depth tape compass
1339 * ("depth" needs to occur before "newline"). */
1340 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP,
1341 /*Reading “%s” must occur before NEWLINE*/225, buffer);
1342 osfree(style_name);
1343 osfree(new_order);
1344 return;
1346 /* Check for duplicates unless it's a special reading:
1347 * IGNOREALL,IGNORE (duplicates allowed) ; END (not possible)
1349 if (!((BIT(Ignore) | BIT(End) | BIT(IgnoreAll)) & BIT(d))) {
1350 if (TSTBIT(mUsed, d)) {
1351 /* TRANSLATORS: complains about a situation like trying to define
1352 * two from stations per leg */
1353 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*Duplicate reading “%s”*/67, buffer);
1354 osfree(style_name);
1355 osfree(new_order);
1356 return;
1357 } else {
1358 /* Check for previously listed readings which are incompatible
1359 * with this one - e.g. Count vs FrCount */
1360 bool fBad = fFalse;
1361 switch (d) {
1362 case Station:
1363 if (mUsed & (BIT(Fr) | BIT(To))) fBad = fTrue;
1364 break;
1365 case Fr: case To:
1366 if (TSTBIT(mUsed, Station)) fBad = fTrue;
1367 break;
1368 case Count:
1369 if (mUsed & (BIT(FrCount) | BIT(ToCount) | BIT(Tape)))
1370 fBad = fTrue;
1371 break;
1372 case FrCount: case ToCount:
1373 if (mUsed & (BIT(Count) | BIT(Tape)))
1374 fBad = fTrue;
1375 break;
1376 case Depth:
1377 if (mUsed & (BIT(FrDepth) | BIT(ToDepth) | BIT(DepthChange)))
1378 fBad = fTrue;
1379 break;
1380 case FrDepth: case ToDepth:
1381 if (mUsed & (BIT(Depth) | BIT(DepthChange))) fBad = fTrue;
1382 break;
1383 case DepthChange:
1384 if (mUsed & (BIT(FrDepth) | BIT(ToDepth) | BIT(Depth)))
1385 fBad = fTrue;
1386 break;
1387 case Newline:
1388 if (mUsed & ~m_multi) {
1389 /* TRANSLATORS: e.g.
1391 * *data normal from to tape newline compass clino */
1392 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*NEWLINE can only be preceded by STATION, DEPTH, and COUNT*/226);
1393 osfree(style_name);
1394 osfree(new_order);
1395 return;
1397 if (k == 0) {
1398 /* TRANSLATORS: error from:
1400 * *data normal newline from to tape compass clino */
1401 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*NEWLINE can’t be the first reading*/222);
1402 osfree(style_name);
1403 osfree(new_order);
1404 return;
1406 break;
1407 default: /* avoid compiler warnings about unhandled enums */
1408 break;
1410 if (fBad) {
1411 /* Not entirely happy with phrasing this... */
1412 /* TRANSLATORS: This is an error from the *DATA command. It
1413 * means that a reading (which will appear where %s is isn't
1414 * valid as the list of readings has already included the same
1415 * reading, or an equivalent one (e.g. you can't have both
1416 * DEPTH and DEPTHCHANGE together). */
1417 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*Reading “%s” duplicates previous reading(s)*/77,
1418 buffer);
1419 osfree(style_name);
1420 osfree(new_order);
1421 return;
1423 mUsed |= BIT(d); /* used to catch duplicates */
1426 if (k && new_order[k - 1] == IgnoreAll) {
1427 SVX_ASSERT(d == Newline);
1428 k--;
1429 d = IgnoreAllAndNewLine;
1431 if (k >= kMac) {
1432 kMac = kMac * 2;
1433 new_order = osrealloc(new_order, kMac * sizeof(reading));
1435 new_order[k++] = d;
1436 } while (d != End);
1438 if (k >= 2 && new_order[k - 2] == Newline) {
1439 /* TRANSLATORS: error from:
1441 * *data normal from to tape compass clino newline */
1442 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*NEWLINE can’t be the last reading*/223);
1443 osfree(style_name);
1444 osfree(new_order);
1445 return;
1448 if (style == STYLE_NOSURVEY) {
1449 if (TSTBIT(mUsed, Station)) {
1450 if (k >= kMac) {
1451 kMac = kMac * 2;
1452 new_order = osrealloc(new_order, kMac * sizeof(reading));
1454 new_order[k - 1] = Newline;
1455 new_order[k++] = End;
1457 } else if (style == STYLE_PASSAGE) {
1458 /* Station doesn't mean "multiline" for STYLE_PASSAGE. */
1459 } else if (!TSTBIT(mUsed, Newline) && (m_multi & mUsed)) {
1460 /* TRANSLATORS: Error given by something like:
1462 * *data normal station tape compass clino
1464 * ("station" signifies interleaved data). */
1465 compile_diagnostic(DIAG_ERR|DIAG_SKIP, /*Interleaved readings, but no NEWLINE*/224);
1466 osfree(style_name);
1467 osfree(new_order);
1468 return;
1471 #if 0
1472 printf("mUsed = 0x%x\n", mUsed);
1473 #endif
1475 /* Check the supplied readings form a sufficient set. */
1476 if (style != STYLE_PASSAGE) {
1477 if ((mUsed & (BIT(Fr) | BIT(To))) == (BIT(Fr) | BIT(To)))
1478 mUsed |= BIT(Station);
1479 else if (TSTBIT(mUsed, Station))
1480 mUsed |= BIT(Fr) | BIT(To);
1483 if (mUsed & (BIT(Comp) | BIT(BackComp)))
1484 mUsed |= BIT(Comp) | BIT(BackComp);
1486 if (mUsed & (BIT(Clino) | BIT(BackClino)))
1487 mUsed |= BIT(Clino) | BIT(BackClino);
1489 if ((mUsed & (BIT(FrDepth) | BIT(ToDepth))) == (BIT(FrDepth) | BIT(ToDepth)))
1490 mUsed |= BIT(Depth) | BIT(DepthChange);
1491 else if (mUsed & (BIT(Depth) | BIT(DepthChange)))
1492 mUsed |= BIT(FrDepth) | BIT(ToDepth) | BIT(Depth) | BIT(DepthChange);
1494 if ((mUsed & (BIT(FrCount) | BIT(ToCount))) == (BIT(FrCount) | BIT(ToCount)))
1495 mUsed |= BIT(Count) | BIT(Tape) | BIT(BackTape);
1496 else if (mUsed & (BIT(Count) | BIT(Tape) | BIT(BackTape)))
1497 mUsed |= BIT(FrCount) | BIT(ToCount) | BIT(Count) | BIT(Tape) | BIT(BackTape);
1499 #if 0
1500 printf("mUsed = 0x%x, opt = 0x%x, mask = 0x%x\n", mUsed,
1501 mask_optional[style], mask[style]);
1502 #endif
1504 if (((mUsed &~ BIT(Newline)) | mask_optional[style]) != mask[style]) {
1505 /* Test should only fail with too few bits set, not too many */
1506 SVX_ASSERT((((mUsed &~ BIT(Newline)) | mask_optional[style])
1507 &~ mask[style]) == 0);
1508 /* TRANSLATORS: i.e. not enough readings for the style. */
1509 compile_diagnostic(DIAG_ERR|DIAG_SKIP, /*Too few readings for data style “%s”*/64, style_name);
1510 osfree(style_name);
1511 osfree(new_order);
1512 return;
1515 /* don't free default ordering or ordering used by parent */
1516 if (pcs->ordering != default_order &&
1517 !(pcs->next && pcs->next->ordering == pcs->ordering))
1518 osfree((reading*)pcs->ordering);
1520 pcs->style = style;
1521 pcs->ordering = new_order;
1523 osfree(style_name);
1525 reinit_style:
1526 if (style == STYLE_PASSAGE) {
1527 lrudlist * new_psg = osnew(lrudlist);
1528 new_psg->tube = NULL;
1529 new_psg->next = model;
1530 model = new_psg;
1531 next_lrud = &(new_psg->tube);
1535 static void
1536 cmd_units(void)
1538 int units, quantity;
1539 unsigned long qmask;
1540 unsigned long m; /* mask with bit x set to indicate quantity x specified */
1541 real factor;
1542 filepos fp;
1544 qmask = get_qlist(BIT(Q_POS)|BIT(Q_PLUMB)|BIT(Q_LEVEL));
1546 if (!qmask) return;
1547 if (qmask == BIT(Q_DEFAULT)) {
1548 default_units(pcs);
1549 return;
1552 get_pos(&fp);
1553 factor = read_numeric(fTrue);
1554 if (factor == 0.0) {
1555 set_pos(&fp);
1556 /* TRANSLATORS: error message given by "*units tape 0 feet" - it’s
1557 * meaningless to say your tape is marked in "0 feet" (but you might
1558 * measure distance by counting knots on a diving line, and tie them
1559 * every "2 feet"). */
1560 compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /**UNITS factor must be non-zero*/200);
1561 skipline();
1562 return;
1565 units = get_units(qmask, fTrue);
1566 if (units == UNITS_NULL) return;
1567 if (TSTBIT(qmask, Q_GRADIENT))
1568 pcs->f_clino_percent = (units == UNITS_PERCENT);
1569 if (TSTBIT(qmask, Q_BACKGRADIENT))
1570 pcs->f_backclino_percent = (units == UNITS_PERCENT);
1572 if (factor == HUGE_REAL) {
1573 factor = factor_tab[units];
1574 } else {
1575 factor *= factor_tab[units];
1578 for (quantity = 0, m = BIT(quantity); m <= qmask; quantity++, m <<= 1)
1579 if (qmask & m) pcs->units[quantity] = factor;
1582 static void
1583 cmd_calibrate(void)
1585 real sc, z;
1586 unsigned long qmask, m;
1587 int quantity;
1588 filepos fp;
1590 qmask = get_qlist(BIT(Q_POS)|BIT(Q_PLUMB)|BIT(Q_LEVEL));
1591 if (!qmask) return; /* error already reported */
1593 if (qmask == BIT(Q_DEFAULT)) {
1594 default_calib(pcs);
1595 return;
1598 if (((qmask & LEN_QMASK)) && ((qmask & ANG_QMASK))) {
1599 /* TRANSLATORS: e.g.
1601 * *calibrate tape compass 1 1
1603 compile_diagnostic(DIAG_ERR|DIAG_SKIP, /*Can’t calibrate angular and length quantities together*/227);
1604 return;
1607 z = read_numeric(fFalse);
1608 get_pos(&fp);
1609 sc = read_numeric(fTrue);
1610 if (sc == HUGE_REAL) {
1611 if (isalpha(ch)) {
1612 int units = get_units(qmask, fFalse);
1613 if (units == UNITS_NULL) {
1614 return;
1616 z *= factor_tab[units];
1617 sc = read_numeric(fTrue);
1618 if (sc == HUGE_REAL) {
1619 sc = (real)1.0;
1620 } else {
1621 /* Adjustment applied is: (reading - z) * sc
1622 * We want: reading * sc - z
1623 * So divide z by sc so the applied adjustment does what we want.
1625 z /= sc;
1627 } else {
1628 sc = (real)1.0;
1632 if (sc == HUGE_REAL) sc = (real)1.0;
1633 /* check for declination scale */
1634 if (TSTBIT(qmask, Q_DECLINATION) && sc != 1.0) {
1635 set_pos(&fp);
1636 /* TRANSLATORS: DECLINATION is a built-in keyword, so best not to
1637 * translate */
1638 compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Scale factor must be 1.0 for DECLINATION*/40);
1639 skipline();
1640 return;
1642 if (sc == 0.0) {
1643 set_pos(&fp);
1644 /* TRANSLATORS: If the scale factor for an instrument is zero, then any
1645 * reading would be mapped to zero, which doesn't make sense. */
1646 compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Scale factor must be non-zero*/391);
1647 skipline();
1648 return;
1650 for (quantity = 0, m = BIT(quantity); m <= qmask; quantity++, m <<= 1) {
1651 if (qmask & m) {
1652 pcs->z[quantity] = pcs->units[quantity] * z;
1653 pcs->sc[quantity] = sc;
1658 static void
1659 cmd_declination(void)
1661 real v = read_numeric(fTrue);
1662 if (v == HUGE_REAL) {
1663 get_token_no_blanks();
1664 if (strcmp(ucbuffer, "AUTO") != 0) {
1665 compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_COL, /*Expected number or “AUTO”*/309);
1666 return;
1668 /* *declination auto X Y Z */
1669 real x = read_numeric(fFalse);
1670 real y = read_numeric(fFalse);
1671 real z = read_numeric(fFalse);
1672 if (!pcs->proj) {
1673 compile_diagnostic(DIAG_ERR, /*Input coordinate system must be specified for “*DECLINATION AUTO”*/301);
1674 return;
1676 if (!proj_wgs84) {
1677 proj_wgs84 = pj_init_plus("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs");
1679 /* Convert to WGS84 lat long. */
1680 if (pj_is_latlong(pcs->proj)) {
1681 /* PROJ expects lat and long in radians. */
1682 x = rad(x);
1683 y = rad(y);
1685 int r = pj_transform(pcs->proj, proj_wgs84, 1, 1, &x, &y, &z);
1686 if (r != 0) {
1687 compile_diagnostic(DIAG_ERR, /*Failed to convert coordinates: %s*/436, pj_strerrno(r));
1688 return;
1690 pcs->z[Q_DECLINATION] = HUGE_REAL;
1691 pcs->dec_x = x;
1692 pcs->dec_y = y;
1693 pcs->dec_z = z;
1694 /* Invalidate cached declination. */
1695 pcs->declination = HUGE_REAL;
1697 projLP lp = { x, y };
1698 struct FACTORS factors;
1699 memset(&factors, 0, sizeof(factors));
1700 pj_factors(lp, proj_out, 0.0, &factors);
1701 pcs->convergence = factors.conv;
1703 } else {
1704 /* *declination D UNITS */
1705 int units = get_units(BIT(Q_DECLINATION), fFalse);
1706 if (units == UNITS_NULL) {
1707 return;
1709 pcs->z[Q_DECLINATION] = -v * factor_tab[units];
1710 pcs->convergence = 0;
1714 #ifndef NO_DEPRECATED
1715 static void
1716 cmd_default(void)
1718 static const sztok defaulttab[] = {
1719 { "CALIBRATE", CMD_CALIBRATE },
1720 { "DATA", CMD_DATA },
1721 { "UNITS", CMD_UNITS },
1722 { NULL, CMD_NULL }
1724 static int default_depr_count = 0;
1726 if (default_depr_count < 5) {
1727 /* TRANSLATORS: If you're unsure what "deprecated" means, see:
1728 * https://en.wikipedia.org/wiki/Deprecation */
1729 compile_diagnostic(DIAG_WARN|DIAG_COL, /**DEFAULT is deprecated - use *CALIBRATE/DATA/SD/UNITS with argument DEFAULT instead*/20);
1730 if (++default_depr_count == 5)
1731 compile_diagnostic(DIAG_WARN, /*Further uses of this deprecated feature will not be reported*/95);
1734 get_token();
1735 switch (match_tok(defaulttab, TABSIZE(defaulttab))) {
1736 case CMD_CALIBRATE:
1737 default_calib(pcs);
1738 break;
1739 case CMD_DATA:
1740 default_style(pcs);
1741 default_grade(pcs);
1742 break;
1743 case CMD_UNITS:
1744 default_units(pcs);
1745 break;
1746 default:
1747 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*Unknown setting “%s”*/41, buffer);
1750 #endif
1752 static void
1753 cmd_include(void)
1755 char *pth, *fnm = NULL;
1756 int fnm_len;
1757 #ifndef NO_DEPRECATED
1758 prefix *root_store;
1759 #endif
1760 int ch_store;
1762 pth = path_from_fnm(file.filename);
1764 read_string(&fnm, &fnm_len);
1766 #ifndef NO_DEPRECATED
1767 /* Since *begin / *end nesting cannot cross file boundaries we only
1768 * need to preserve the prefix if the deprecated *prefix command
1769 * can be used */
1770 root_store = root;
1771 root = pcs->Prefix; /* Root for include file is current prefix */
1772 #endif
1773 ch_store = ch;
1775 data_file(pth, fnm);
1777 #ifndef NO_DEPRECATED
1778 root = root_store; /* and restore root */
1779 #endif
1780 ch = ch_store;
1782 s_free(&fnm);
1783 osfree(pth);
1786 static void
1787 cmd_sd(void)
1789 real sd, variance;
1790 int units;
1791 unsigned long qmask, m;
1792 int quantity;
1793 qmask = get_qlist(BIT(Q_DECLINATION));
1794 if (!qmask) return; /* no quantities found - error already reported */
1796 if (qmask == BIT(Q_DEFAULT)) {
1797 default_grade(pcs);
1798 return;
1800 sd = read_numeric(fFalse);
1801 if (sd <= (real)0.0) {
1802 compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_COL, /*Standard deviation must be positive*/48);
1803 return;
1805 units = get_units(qmask, fFalse);
1806 if (units == UNITS_NULL) return;
1808 sd *= factor_tab[units];
1809 variance = sqrd(sd);
1811 for (quantity = 0, m = BIT(quantity); m <= qmask; quantity++, m <<= 1)
1812 if (qmask & m) pcs->Var[quantity] = variance;
1815 static void
1816 cmd_title(void)
1818 if (!fExplicitTitle && pcs->Prefix == root) {
1819 /* If we don't have an explicit title yet, and we're currently in the
1820 * root prefix, use this title explicitly. */
1821 fExplicitTitle = fTrue;
1822 read_string(&survey_title, &survey_title_len);
1823 } else {
1824 /* parse and throw away this title (but still check rest of line) */
1825 char *s = NULL;
1826 int len;
1827 read_string(&s, &len);
1828 s_free(&s);
1832 static const sztok case_tab[] = {
1833 {"PRESERVE", OFF},
1834 {"TOLOWER", LOWER},
1835 {"TOUPPER", UPPER},
1836 {NULL, -1}
1839 static void
1840 cmd_case(void)
1842 int setting;
1843 get_token();
1844 setting = match_tok(case_tab, TABSIZE(case_tab));
1845 if (setting != -1) {
1846 pcs->Case = setting;
1847 } else {
1848 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*Found “%s”, expecting “PRESERVE”, “TOUPPER”, or “TOLOWER”*/10, buffer);
1852 typedef enum {
1853 CS_NONE = -1,
1854 CS_CUSTOM,
1855 CS_EPSG,
1856 CS_ESRI,
1857 CS_EUR,
1858 CS_IJTSK,
1859 CS_JTSK,
1860 CS_LAT,
1861 CS_LOCAL,
1862 CS_LONG,
1863 CS_OSGB,
1864 CS_S_MERC,
1865 CS_UTM
1866 } cs_class;
1868 static const sztok cs_tab[] = {
1869 {"CUSTOM", CS_CUSTOM},
1870 {"EPSG", CS_EPSG}, /* EPSG:<number> */
1871 {"ESRI", CS_ESRI}, /* ESRI:<number> */
1872 {"EUR", CS_EUR}, /* EUR79Z30 */
1873 {"IJTSK", CS_IJTSK}, /* IJTSK or IJTSK03 */
1874 {"JTSK", CS_JTSK}, /* JTSK or JTSK03 */
1875 {"LAT", CS_LAT}, /* LAT-LONG */
1876 {"LOCAL", CS_LOCAL},
1877 {"LONG", CS_LONG}, /* LONG-LAT */
1878 {"OSGB", CS_OSGB}, /* OSGB:<H, N, O, S or T><A-Z except I> */
1879 {"S", CS_S_MERC}, /* S-MERC */
1880 {"UTM", CS_UTM}, /* UTM<zone><N or S or nothing> */
1881 {NULL, CS_NONE}
1884 static void
1885 cmd_cs(void)
1887 char * proj_str = NULL;
1888 int proj_str_len;
1889 cs_class cs;
1890 int cs_sub = INT_MIN;
1891 filepos fp;
1892 bool output = fFalse;
1893 enum { YES, NO, MAYBE } ok_for_output = YES;
1894 static bool had_cs = fFalse;
1896 if (!had_cs) {
1897 had_cs = fTrue;
1898 if (first_fix_name) {
1899 compile_diagnostic_at(DIAG_ERR,
1900 first_fix_filename, first_fix_line,
1901 /*Station “%s” fixed before CS command first used*/442,
1902 sprint_prefix(first_fix_name));
1906 get_pos(&fp);
1907 /* Note get_token() only accepts letters - it'll stop at digits so "UTM12"
1908 * will give token "UTM". */
1909 get_token();
1910 if (strcmp(ucbuffer, "OUT") == 0) {
1911 output = fTrue;
1912 get_pos(&fp);
1913 get_token();
1915 cs = match_tok(cs_tab, TABSIZE(cs_tab));
1916 switch (cs) {
1917 case CS_NONE:
1918 break;
1919 case CS_CUSTOM:
1920 ok_for_output = MAYBE;
1921 get_pos(&fp);
1922 read_string(&proj_str, &proj_str_len);
1923 cs_sub = 0;
1924 break;
1925 case CS_EPSG: case CS_ESRI:
1926 ok_for_output = MAYBE;
1927 if (ch == ':' && isdigit(nextch())) {
1928 unsigned n = read_uint();
1929 if (n < 1000000) {
1930 cs_sub = (int)n;
1933 break;
1934 case CS_EUR:
1935 if (isdigit(ch) &&
1936 read_uint() == 79 &&
1937 (ch == 'Z' || ch == 'z') &&
1938 isdigit(nextch()) &&
1939 read_uint() == 30) {
1940 cs_sub = 7930;
1942 break;
1943 case CS_JTSK:
1944 ok_for_output = NO;
1945 /* FALLTHRU */
1946 case CS_IJTSK:
1947 if (ch == '0') {
1948 if (nextch() == '3') {
1949 nextch();
1950 cs_sub = 3;
1952 } else {
1953 cs_sub = 0;
1955 break;
1956 case CS_LAT: case CS_LONG:
1957 ok_for_output = NO;
1958 if (ch == '-') {
1959 nextch();
1960 get_token_no_blanks();
1961 cs_class cs2 = match_tok(cs_tab, TABSIZE(cs_tab));
1962 if ((cs ^ cs2) == (CS_LAT ^ CS_LONG)) {
1963 cs_sub = 0;
1966 break;
1967 case CS_LOCAL:
1968 cs_sub = 0;
1969 break;
1970 case CS_OSGB:
1971 if (ch == ':') {
1972 int uch1 = toupper(nextch());
1973 if (strchr("HNOST", uch1)) {
1974 int uch2 = toupper(nextch());
1975 if (uch2 >= 'A' && uch2 <= 'Z' && uch2 != 'I') {
1976 int x, y;
1977 nextch();
1978 if (uch1 > 'I') --uch1;
1979 uch1 -= 'A';
1980 if (uch2 > 'I') --uch2;
1981 uch2 -= 'A';
1982 x = uch1 % 5;
1983 y = uch1 / 5;
1984 x = (x * 5) + uch2 % 5;
1985 y = (y * 5) + uch2 / 5;
1986 cs_sub = y * 25 + x;
1990 break;
1991 case CS_S_MERC:
1992 if (ch == '-') {
1993 nextch();
1994 get_token_no_blanks();
1995 if (strcmp(ucbuffer, "MERC") == 0) {
1996 cs_sub = 0;
1999 break;
2000 case CS_UTM:
2001 if (isdigit(ch)) {
2002 unsigned n = read_uint();
2003 if (n >= 1 && n <= 60) {
2004 int uch = toupper(ch);
2005 cs_sub = (int)n;
2006 if (uch == 'S') {
2007 nextch();
2008 cs_sub = -cs_sub;
2009 } else if (uch == 'N') {
2010 nextch();
2014 break;
2016 if (cs_sub == INT_MIN || isalnum(ch)) {
2017 set_pos(&fp);
2018 compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Unknown coordinate system*/434);
2019 skipline();
2020 return;
2022 /* Actually handle the cs */
2023 switch (cs) {
2024 case CS_NONE:
2025 break;
2026 case CS_CUSTOM:
2027 /* proj_str already set */
2028 break;
2029 case CS_EPSG:
2030 proj_str = osmalloc(32);
2031 sprintf(proj_str, "+init=epsg:%d +no_defs", cs_sub);
2032 break;
2033 case CS_ESRI:
2034 proj_str = osmalloc(32);
2035 sprintf(proj_str, "+init=esri:%d +no_defs", cs_sub);
2036 break;
2037 case CS_EUR:
2038 proj_str = osstrdup("+proj=utm +zone=30 +ellps=intl +towgs84=-86,-98,-119,0,0,0,0 +no_defs");
2039 break;
2040 case CS_IJTSK:
2041 if (cs_sub == 0)
2042 proj_str = osstrdup("+proj=krovak +ellps=bessel +towgs84=570.8285,85.6769,462.842,4.9984,1.5867,5.2611,3.5623 +no_defs");
2043 else
2044 proj_str = osstrdup("+proj=krovak +ellps=bessel +towgs84=485.021,169.465,483.839,7.786342,4.397554,4.102655,0 +no_defs");
2045 break;
2046 case CS_JTSK:
2047 if (cs_sub == 0)
2048 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");
2049 else
2050 proj_str = osstrdup("+proj=krovak +czech +ellps=bessel +towgs84=485.021,169.465,483.839,7.786342,4.397554,4.102655,0 +no_defs");
2051 break;
2052 case CS_LAT:
2053 /* FIXME: Requires PROJ >= 4.8.0 for +axis, and the SDs will be
2054 * misapplied, so we may want to swap ourselves. Also, while
2055 * therion supports lat-long, I'm not totally convinced that it is
2056 * sensible to do so - people often say "lat-long", but probably
2057 * don't think that that's actually "Northing, Easting". This
2058 * seems like it'll result in people accidentally getting X and Y
2059 * swapped in their fixed points...
2061 #if 0
2062 proj_str = osstrdup("+proj=longlat +ellps=WGS84 +datum=WGS84 +axis=neu +no_defs");
2063 #endif
2064 break;
2065 case CS_LOCAL:
2066 /* FIXME: Is it useful to be able to explicitly specify this? */
2067 break;
2068 case CS_LONG:
2069 proj_str = osstrdup("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs");
2070 break;
2071 case CS_OSGB: {
2072 int x = 14 - (cs_sub % 25);
2073 int y = (cs_sub / 25) - 20;
2074 proj_str = osmalloc(160);
2075 sprintf(proj_str, "+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=%d +y_0=%d +ellps=airy +datum=OSGB36 +units=m +no_defs", x * 100000, y * 100000);
2076 break;
2078 case CS_S_MERC:
2079 proj_str = osstrdup("+proj=merc +lat_ts=0 +lon_0=0 +k=1 +x_0=0 +y_0=0 +a=6378137 +b=6378137 +units=m +nadgrids=@null +no_defs");
2080 break;
2081 case CS_UTM:
2082 proj_str = osmalloc(74);
2083 if (cs_sub > 0) {
2084 sprintf(proj_str, "+proj=utm +ellps=WGS84 +datum=WGS84 +units=m +zone=%d +no_defs", cs_sub);
2085 } else {
2086 sprintf(proj_str, "+proj=utm +ellps=WGS84 +datum=WGS84 +units=m +zone=%d +south +no_defs", -cs_sub);
2088 break;
2091 if (!proj_str) {
2092 /* printf("CS %d:%d\n", (int)cs, cs_sub); */
2093 set_pos(&fp);
2094 compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Unknown coordinate system*/434);
2095 skipline();
2096 return;
2099 if (output) {
2100 if (ok_for_output == NO) {
2101 set_pos(&fp);
2102 compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Coordinate system unsuitable for output*/435);
2103 skipline();
2104 return;
2107 /* If the output projection is already set, we still need to create the
2108 * projection object for a custom projection, so we can report errors.
2109 * But if the string is identical, we know it's valid.
2111 if (!proj_out ||
2112 (ok_for_output == MAYBE && strcmp(proj_str, proj_str_out) != 0)) {
2113 projPJ pj = pj_init_plus(proj_str);
2114 if (!pj) {
2115 set_pos(&fp);
2116 compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Invalid coordinate system: %s*/443,
2117 pj_strerrno(pj_errno));
2118 skipline();
2119 return;
2121 if (ok_for_output == MAYBE && pj_is_latlong(pj)) {
2122 set_pos(&fp);
2123 compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Coordinate system unsuitable for output*/435);
2124 skipline();
2125 return;
2127 if (proj_out) {
2128 pj_free(pj);
2129 osfree(proj_str);
2130 } else {
2131 proj_out = pj;
2132 proj_str_out = proj_str;
2135 } else {
2136 projPJ pj;
2137 if (proj_str_out && strcmp(proj_str, proj_str_out) == 0) {
2138 /* Same as the current output projection. */
2139 pj = proj_out;
2140 } else {
2141 pj = pj_init_plus(proj_str);
2142 if (!pj) {
2143 set_pos(&fp);
2144 compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Invalid coordinate system: %s*/443,
2145 pj_strerrno(pj_errno));
2146 skipline();
2147 return;
2151 /* Free proj if not used by parent, or as the output projection. */
2152 settings * p = pcs;
2153 if (p->proj && (!p->next || p->proj != p->next->proj))
2154 if (p->proj != proj_out)
2155 pj_free(p->proj);
2156 p->proj = pj;
2160 static const sztok infer_tab[] = {
2161 { "EQUATES", INFER_EQUATES },
2162 { "EXPORTS", INFER_EXPORTS },
2163 { "PLUMBS", INFER_PLUMBS },
2164 #if 0 /* FIXME */
2165 { "SUBSURVEYS", INFER_SUBSURVEYS },
2166 #endif
2167 { NULL, INFER_NULL }
2170 static const sztok onoff_tab[] = {
2171 { "OFF", 0 },
2172 { "ON", 1 },
2173 { NULL, -1 }
2176 static void
2177 cmd_infer(void)
2179 infer_what setting;
2180 int on;
2181 get_token();
2182 setting = match_tok(infer_tab, TABSIZE(infer_tab));
2183 if (setting == INFER_NULL) {
2184 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*Found “%s”, expecting “EQUATES”, “EXPORTS”, or “PLUMBS”*/31, buffer);
2185 return;
2187 get_token();
2188 on = match_tok(onoff_tab, TABSIZE(onoff_tab));
2189 if (on == -1) {
2190 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*Found “%s”, expecting “ON” or “OFF”*/32, buffer);
2191 return;
2194 if (on) {
2195 pcs->infer |= BIT(setting);
2196 if (setting == INFER_EXPORTS) fExportUsed = fTrue;
2197 } else {
2198 pcs->infer &= ~BIT(setting);
2202 static void
2203 cmd_truncate(void)
2205 unsigned int truncate_at = 0; /* default is no truncation */
2206 filepos fp;
2208 get_pos(&fp);
2210 get_token();
2211 if (strcmp(ucbuffer, "OFF") != 0) {
2212 if (*ucbuffer) set_pos(&fp);
2213 truncate_at = read_uint();
2215 /* for backward compatibility, "*truncate 0" means "*truncate off" */
2216 pcs->Truncate = (truncate_at == 0) ? INT_MAX : truncate_at;
2219 static void
2220 cmd_ref(void)
2222 /* Just syntax check for now. */
2223 char *ref = NULL;
2224 int ref_len;
2225 read_string(&ref, &ref_len);
2226 s_free(&ref);
2229 static void
2230 cmd_require(void)
2232 const unsigned int version[] = {COMMAVERSION};
2233 const unsigned int *ver = version;
2234 filepos fp;
2236 skipblanks();
2237 get_pos(&fp);
2238 while (1) {
2239 int diff = *ver++ - read_uint();
2240 if (diff > 0) break;
2241 if (diff < 0) {
2242 size_t i, len;
2243 char *v;
2244 filepos fp_tmp;
2246 /* find end of version number */
2247 while (isdigit(ch) || ch == '.') nextch();
2248 get_pos(&fp_tmp);
2249 len = (size_t)(fp_tmp.offset - fp.offset);
2250 v = osmalloc(len + 1);
2251 set_pos(&fp);
2252 for (i = 0; i < len; i++) {
2253 v[i] = ch;
2254 nextch();
2256 v[i] = '\0';
2257 /* TRANSLATORS: Feel free to translate as "or newer" instead of "or
2258 * greater" if that gives a more natural translation. It's
2259 * technically not quite right when there are parallel active release
2260 * series (e.g. Survex 1.0.40 was released *after* 1.2.0), but this
2261 * seems unlikely to confuse users. "Survex" is the name of the
2262 * software, so should not be translated.
2264 * Here "survey" is a "cave map" rather than list of questions - it should be
2265 * translated to the terminology that cavers using the language would use.
2267 fatalerror_in_file(file.filename, file.line, /*Survex version %s or greater required to process this survey data.*/2, v);
2269 if (ch != '.') break;
2270 nextch();
2271 if (!isdigit(ch) || ver == version + sizeof(version) / sizeof(*version))
2272 break;
2274 /* skip rest of version number */
2275 while (isdigit(ch) || ch == '.') nextch();
2278 /* allocate new meta_data if need be */
2279 void
2280 copy_on_write_meta(settings *s)
2282 if (!s->meta || s->meta->ref_count != 0) {
2283 meta_data * meta_new = osnew(meta_data);
2284 if (!s->meta) {
2285 meta_new->days1 = meta_new->days2 = -1;
2286 } else {
2287 *meta_new = *(s->meta);
2289 meta_new->ref_count = 0;
2290 s->meta = meta_new;
2294 static void
2295 cmd_date(void)
2297 int year, month, day;
2298 int days1, days2;
2299 bool implicit_range = fFalse;
2300 filepos fp, fp2;
2302 get_pos(&fp);
2303 read_date(&year, &month, &day);
2304 days1 = days_since_1900(year, month ? month : 1, day ? day : 1);
2306 if (days1 > current_days_since_1900) {
2307 set_pos(&fp);
2308 compile_diagnostic(DIAG_WARN|DIAG_DATE, /*Date is in the future!*/80);
2311 skipblanks();
2312 if (ch == '-') {
2313 nextch();
2314 get_pos(&fp2);
2315 read_date(&year, &month, &day);
2316 } else {
2317 if (month && day) {
2318 days2 = days1;
2319 goto read;
2321 implicit_range = fTrue;
2324 if (month == 0) month = 12;
2325 if (day == 0) day = last_day(year, month);
2326 days2 = days_since_1900(year, month, day);
2328 if (!implicit_range && days2 > current_days_since_1900) {
2329 set_pos(&fp2);
2330 compile_diagnostic(DIAG_WARN|DIAG_DATE, /*Date is in the future!*/80);
2333 if (days2 < days1) {
2334 set_pos(&fp);
2335 compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*End of date range is before the start*/81);
2336 int tmp = days1;
2337 days1 = days2;
2338 days2 = tmp;
2341 read:
2342 if (!pcs->meta || pcs->meta->days1 != days1 || pcs->meta->days2 != days2) {
2343 copy_on_write_meta(pcs);
2344 pcs->meta->days1 = days1;
2345 pcs->meta->days2 = days2;
2346 /* Invalidate cached declination. */
2347 pcs->declination = HUGE_REAL;
2351 typedef void (*cmd_fn)(void);
2353 static const cmd_fn cmd_funcs[] = {
2354 cmd_alias,
2355 cmd_begin,
2356 cmd_calibrate,
2357 cmd_case,
2358 skipline, /*cmd_copyright,*/
2359 cmd_cs,
2360 cmd_data,
2361 cmd_date,
2362 cmd_declination,
2363 #ifndef NO_DEPRECATED
2364 cmd_default,
2365 #endif
2366 cmd_end,
2367 cmd_entrance,
2368 cmd_equate,
2369 cmd_export,
2370 cmd_fix,
2371 cmd_flags,
2372 cmd_include,
2373 cmd_infer,
2374 skipline, /*cmd_instrument,*/
2375 #ifndef NO_DEPRECATED
2376 cmd_prefix,
2377 #endif
2378 cmd_ref,
2379 cmd_require,
2380 cmd_sd,
2381 cmd_set,
2382 solve_network,
2383 skipline, /*cmd_team,*/
2384 cmd_title,
2385 cmd_truncate,
2386 cmd_units
2389 extern void
2390 handle_command(void)
2392 int cmdtok;
2393 get_token();
2394 cmdtok = match_tok(cmd_tab, TABSIZE(cmd_tab));
2396 if (cmdtok < 0 || cmdtok >= (int)(sizeof(cmd_funcs) / sizeof(cmd_fn))) {
2397 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*Unknown command “%s”*/12, buffer);
2398 return;
2401 switch (cmdtok) {
2402 case CMD_EXPORT:
2403 if (!f_export_ok)
2404 /* TRANSLATORS: The *EXPORT command is only valid just after *BEGIN
2405 * <SURVEY>, so this would generate this error:
2407 * *begin fred
2408 * 1 2 1.23 045 -6
2409 * *export 2
2410 * *end fred */
2411 compile_diagnostic(DIAG_ERR, /**EXPORT must immediately follow “*BEGIN <SURVEY>”*/57);
2412 break;
2413 case CMD_ALIAS:
2414 case CMD_CALIBRATE:
2415 case CMD_CASE:
2416 case CMD_COPYRIGHT:
2417 case CMD_CS:
2418 case CMD_DATA:
2419 case CMD_DATE:
2420 case CMD_DECLINATION:
2421 case CMD_DEFAULT:
2422 case CMD_FLAGS:
2423 case CMD_INFER:
2424 case CMD_INSTRUMENT:
2425 case CMD_REF:
2426 case CMD_REQUIRE:
2427 case CMD_SD:
2428 case CMD_SET:
2429 case CMD_TEAM:
2430 case CMD_TITLE:
2431 case CMD_TRUNCATE:
2432 case CMD_UNITS:
2433 /* These can occur between *begin and *export */
2434 break;
2435 default:
2436 /* NB: additional handling for "*begin <survey>" in cmd_begin */
2437 f_export_ok = fFalse;
2438 break;
2441 cmd_funcs[cmdtok]();