Add caret for reentering existing survey warning
[survex.git] / src / commands.c
blobb65f58be5488b78ed5ecb87720a45f30b9f41681
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 /*ᵍ*/76,
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 * http://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 * http://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 * http://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 * http://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 * http://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 * http://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 reading *order = p->ordering;
706 if (order != default_order && (!p->next || order != p->next->ordering))
707 osfree(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_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, /*Same station fixed twice with no coordinates*/61);
835 return;
838 /* TRANSLATORS: " *fix a " gives this message: */
839 compile_diagnostic(DIAG_WARN, /*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,
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;
1264 /* after a bad *data command ignore survey data until the next
1265 * *data command to avoid an avalanche of errors */
1266 pcs->style = STYLE_IGNORE;
1268 kMac = 6; /* minimum for NORMAL style */
1269 new_order = osmalloc(kMac * sizeof(reading));
1271 get_token();
1272 style = match_tok(styletab, TABSIZE(styletab));
1274 if (style == STYLE_DEFAULT) {
1275 default_style(pcs);
1276 return;
1279 if (style == STYLE_UNKNOWN) {
1280 /* TRANSLATORS: e.g. trying to refer to an invalid FNORD data style */
1281 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*Data style “%s” unknown*/65, buffer);
1282 return;
1285 skipblanks();
1286 #ifndef NO_DEPRECATED
1287 /* Olde syntax had optional field for survey grade, so allow an omit
1288 * but issue a warning about it */
1289 if (isOmit(ch)) {
1290 static int data_depr_count = 0;
1291 if (data_depr_count < 5) {
1292 compile_diagnostic(DIAG_WARN|DIAG_BUF, /*“*data %s %c …” is deprecated - use “*data %s …” instead*/104,
1293 buffer, ch, buffer);
1294 if (++data_depr_count == 5)
1295 compile_diagnostic(DIAG_WARN, /*Further uses of this deprecated feature will not be reported*/95);
1297 nextch();
1299 #endif
1301 style_name = osstrdup(buffer);
1302 do {
1303 filepos fp;
1304 get_pos(&fp);
1305 get_token();
1306 d = match_tok(dtab, TABSIZE(dtab));
1307 /* only token allowed after IGNOREALL is NEWLINE */
1308 if (k && new_order[k - 1] == IgnoreAll && d != Newline) {
1309 set_pos(&fp);
1310 break;
1312 /* Note: an unknown token is reported as trailing garbage */
1313 if (!TSTBIT(mask_all[style], d)) {
1314 /* TRANSLATORS: a data "style" is something like NORMAL, DIVING, etc.
1315 * a "reading" is one of FROM, TO, TAPE, COMPASS, CLINO for NORMAL
1316 * neither style nor reading is a keyword in the program This error
1317 * complains about a depth gauge reading in normal style, for example
1319 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP,
1320 /*Reading “%s” not allowed in data style “%s”*/63,
1321 buffer, style_name);
1322 osfree(style_name);
1323 osfree(new_order);
1324 return;
1326 if (TSTBIT(mUsed, Newline) && TSTBIT(m_multi, d)) {
1327 /* TRANSLATORS: caused by e.g.
1329 * *data diving station newline depth tape compass
1331 * ("depth" needs to occur before "newline"). */
1332 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP,
1333 /*Reading “%s” must occur before NEWLINE*/225, buffer);
1334 osfree(style_name);
1335 osfree(new_order);
1336 return;
1338 /* Check for duplicates unless it's a special reading:
1339 * IGNOREALL,IGNORE (duplicates allowed) ; END (not possible)
1341 if (!((BIT(Ignore) | BIT(End) | BIT(IgnoreAll)) & BIT(d))) {
1342 if (TSTBIT(mUsed, d)) {
1343 /* TRANSLATORS: complains about a situation like trying to define
1344 * two from stations per leg */
1345 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*Duplicate reading “%s”*/67, buffer);
1346 osfree(style_name);
1347 osfree(new_order);
1348 return;
1349 } else {
1350 /* Check for previously listed readings which are incompatible
1351 * with this one - e.g. Count vs FrCount */
1352 bool fBad = fFalse;
1353 switch (d) {
1354 case Station:
1355 if (mUsed & (BIT(Fr) | BIT(To))) fBad = fTrue;
1356 break;
1357 case Fr: case To:
1358 if (TSTBIT(mUsed, Station)) fBad = fTrue;
1359 break;
1360 case Count:
1361 if (mUsed & (BIT(FrCount) | BIT(ToCount) | BIT(Tape)))
1362 fBad = fTrue;
1363 break;
1364 case FrCount: case ToCount:
1365 if (mUsed & (BIT(Count) | BIT(Tape)))
1366 fBad = fTrue;
1367 break;
1368 case Depth:
1369 if (mUsed & (BIT(FrDepth) | BIT(ToDepth) | BIT(DepthChange)))
1370 fBad = fTrue;
1371 break;
1372 case FrDepth: case ToDepth:
1373 if (mUsed & (BIT(Depth) | BIT(DepthChange))) fBad = fTrue;
1374 break;
1375 case DepthChange:
1376 if (mUsed & (BIT(FrDepth) | BIT(ToDepth) | BIT(Depth)))
1377 fBad = fTrue;
1378 break;
1379 case Newline:
1380 if (mUsed & ~m_multi) {
1381 /* TRANSLATORS: e.g.
1383 * *data normal from to tape newline compass clino */
1384 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*NEWLINE can only be preceded by STATION, DEPTH, and COUNT*/226);
1385 osfree(style_name);
1386 osfree(new_order);
1387 return;
1389 if (k == 0) {
1390 /* TRANSLATORS: error from:
1392 * *data normal newline from to tape compass clino */
1393 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*NEWLINE can’t be the first reading*/222);
1394 osfree(style_name);
1395 osfree(new_order);
1396 return;
1398 break;
1399 default: /* avoid compiler warnings about unhandled enums */
1400 break;
1402 if (fBad) {
1403 /* Not entirely happy with phrasing this... */
1404 /* TRANSLATORS: This is an error from the *DATA command. It
1405 * means that a reading (which will appear where %s is isn't
1406 * valid as the list of readings has already included the same
1407 * reading, or an equivalent one (e.g. you can't have both
1408 * DEPTH and DEPTHCHANGE together). */
1409 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*Reading “%s” duplicates previous reading(s)*/77,
1410 buffer);
1411 osfree(style_name);
1412 osfree(new_order);
1413 return;
1415 mUsed |= BIT(d); /* used to catch duplicates */
1418 if (k && new_order[k - 1] == IgnoreAll) {
1419 SVX_ASSERT(d == Newline);
1420 k--;
1421 d = IgnoreAllAndNewLine;
1423 if (k >= kMac) {
1424 kMac = kMac * 2;
1425 new_order = osrealloc(new_order, kMac * sizeof(reading));
1427 new_order[k++] = d;
1428 } while (d != End);
1430 if (k >= 2 && new_order[k - 2] == Newline) {
1431 /* TRANSLATORS: error from:
1433 * *data normal from to tape compass clino newline */
1434 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*NEWLINE can’t be the last reading*/223);
1435 osfree(style_name);
1436 osfree(new_order);
1437 return;
1440 if (style == STYLE_NOSURVEY) {
1441 if (TSTBIT(mUsed, Station)) {
1442 if (k >= kMac) {
1443 kMac = kMac * 2;
1444 new_order = osrealloc(new_order, kMac * sizeof(reading));
1446 new_order[k - 1] = Newline;
1447 new_order[k++] = End;
1449 } else if (style == STYLE_PASSAGE) {
1450 /* Station doesn't mean "multiline" for STYLE_PASSAGE. */
1451 } else if (!TSTBIT(mUsed, Newline) && (m_multi & mUsed)) {
1452 /* TRANSLATORS: Error given by something like:
1454 * *data normal station tape compass clino
1456 * ("station" signifies interleaved data). */
1457 compile_diagnostic(DIAG_ERR|DIAG_SKIP, /*Interleaved readings, but no NEWLINE*/224);
1458 osfree(style_name);
1459 osfree(new_order);
1460 return;
1463 #if 0
1464 printf("mUsed = 0x%x\n", mUsed);
1465 #endif
1467 /* Check the supplied readings form a sufficient set. */
1468 if (style != STYLE_PASSAGE) {
1469 if ((mUsed & (BIT(Fr) | BIT(To))) == (BIT(Fr) | BIT(To)))
1470 mUsed |= BIT(Station);
1471 else if (TSTBIT(mUsed, Station))
1472 mUsed |= BIT(Fr) | BIT(To);
1475 if (mUsed & (BIT(Comp) | BIT(BackComp)))
1476 mUsed |= BIT(Comp) | BIT(BackComp);
1478 if (mUsed & (BIT(Clino) | BIT(BackClino)))
1479 mUsed |= BIT(Clino) | BIT(BackClino);
1481 if ((mUsed & (BIT(FrDepth) | BIT(ToDepth))) == (BIT(FrDepth) | BIT(ToDepth)))
1482 mUsed |= BIT(Depth) | BIT(DepthChange);
1483 else if (mUsed & (BIT(Depth) | BIT(DepthChange)))
1484 mUsed |= BIT(FrDepth) | BIT(ToDepth) | BIT(Depth) | BIT(DepthChange);
1486 if ((mUsed & (BIT(FrCount) | BIT(ToCount))) == (BIT(FrCount) | BIT(ToCount)))
1487 mUsed |= BIT(Count) | BIT(Tape) | BIT(BackTape);
1488 else if (mUsed & (BIT(Count) | BIT(Tape) | BIT(BackTape)))
1489 mUsed |= BIT(FrCount) | BIT(ToCount) | BIT(Count) | BIT(Tape) | BIT(BackTape);
1491 #if 0
1492 printf("mUsed = 0x%x, opt = 0x%x, mask = 0x%x\n", mUsed,
1493 mask_optional[style], mask[style]);
1494 #endif
1496 if (((mUsed &~ BIT(Newline)) | mask_optional[style]) != mask[style]) {
1497 /* Test should only fail with too few bits set, not too many */
1498 SVX_ASSERT((((mUsed &~ BIT(Newline)) | mask_optional[style])
1499 &~ mask[style]) == 0);
1500 /* TRANSLATORS: i.e. not enough readings for the style. */
1501 compile_diagnostic(DIAG_ERR|DIAG_SKIP, /*Too few readings for data style “%s”*/64, style_name);
1502 osfree(style_name);
1503 osfree(new_order);
1504 return;
1507 /* don't free default ordering or ordering used by parent */
1508 if (pcs->ordering != default_order &&
1509 !(pcs->next && pcs->next->ordering == pcs->ordering))
1510 osfree(pcs->ordering);
1512 pcs->style = style;
1513 pcs->ordering = new_order;
1515 osfree(style_name);
1517 if (style == STYLE_PASSAGE) {
1518 lrudlist * new_psg = osnew(lrudlist);
1519 new_psg->tube = NULL;
1520 new_psg->next = model;
1521 model = new_psg;
1522 next_lrud = &(new_psg->tube);
1526 static void
1527 cmd_units(void)
1529 int units, quantity;
1530 unsigned long qmask;
1531 unsigned long m; /* mask with bit x set to indicate quantity x specified */
1532 real factor;
1533 filepos fp;
1535 qmask = get_qlist(BIT(Q_POS)|BIT(Q_PLUMB)|BIT(Q_LEVEL));
1537 if (!qmask) return;
1538 if (qmask == BIT(Q_DEFAULT)) {
1539 default_units(pcs);
1540 return;
1543 get_pos(&fp);
1544 factor = read_numeric(fTrue);
1545 if (factor == 0.0) {
1546 set_pos(&fp);
1547 /* TRANSLATORS: error message given by "*units tape 0 feet" - it’s
1548 * meaningless to say your tape is marked in "0 feet" (but you might
1549 * measure distance by counting knots on a diving line, and tie them
1550 * every "2 feet"). */
1551 compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /**UNITS factor must be non-zero*/200);
1552 skipline();
1553 return;
1556 units = get_units(qmask, fTrue);
1557 if (units == UNITS_NULL) return;
1558 if (TSTBIT(qmask, Q_GRADIENT))
1559 pcs->f_clino_percent = (units == UNITS_PERCENT);
1560 if (TSTBIT(qmask, Q_BACKGRADIENT))
1561 pcs->f_backclino_percent = (units == UNITS_PERCENT);
1563 if (factor == HUGE_REAL) {
1564 factor = factor_tab[units];
1565 } else {
1566 factor *= factor_tab[units];
1569 for (quantity = 0, m = BIT(quantity); m <= qmask; quantity++, m <<= 1)
1570 if (qmask & m) pcs->units[quantity] = factor;
1573 static void
1574 cmd_calibrate(void)
1576 real sc, z;
1577 unsigned long qmask, m;
1578 int quantity;
1579 filepos fp;
1581 qmask = get_qlist(BIT(Q_POS)|BIT(Q_PLUMB)|BIT(Q_LEVEL));
1582 if (!qmask) return; /* error already reported */
1584 if (qmask == BIT(Q_DEFAULT)) {
1585 default_calib(pcs);
1586 return;
1589 if (((qmask & LEN_QMASK)) && ((qmask & ANG_QMASK))) {
1590 /* TRANSLATORS: e.g.
1592 * *calibrate tape compass 1 1
1594 compile_diagnostic(DIAG_ERR|DIAG_SKIP, /*Can’t calibrate angular and length quantities together*/227);
1595 return;
1598 z = read_numeric(fFalse);
1599 get_pos(&fp);
1600 sc = read_numeric(fTrue);
1601 if (sc == HUGE_REAL) {
1602 if (isalpha(ch)) {
1603 int units = get_units(qmask, fFalse);
1604 if (units == UNITS_NULL) {
1605 return;
1607 z *= factor_tab[units];
1608 sc = read_numeric(fTrue);
1609 if (sc == HUGE_REAL) {
1610 sc = (real)1.0;
1611 } else {
1612 /* Adjustment applied is: (reading - z) * sc
1613 * We want: reading * sc - z
1614 * So divide z by sc so the applied adjustment does what we want.
1616 z /= sc;
1618 } else {
1619 sc = (real)1.0;
1623 if (sc == HUGE_REAL) sc = (real)1.0;
1624 /* check for declination scale */
1625 if (TSTBIT(qmask, Q_DECLINATION) && sc != 1.0) {
1626 set_pos(&fp);
1627 /* TRANSLATORS: DECLINATION is a built-in keyword, so best not to
1628 * translate */
1629 compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Scale factor must be 1.0 for DECLINATION*/40);
1630 skipline();
1631 return;
1633 if (sc == 0.0) {
1634 set_pos(&fp);
1635 /* TRANSLATORS: If the scale factor for an instrument is zero, then any
1636 * reading would be mapped to zero, which doesn't make sense. */
1637 compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Scale factor must be non-zero*/391);
1638 skipline();
1639 return;
1641 for (quantity = 0, m = BIT(quantity); m <= qmask; quantity++, m <<= 1) {
1642 if (qmask & m) {
1643 pcs->z[quantity] = pcs->units[quantity] * z;
1644 pcs->sc[quantity] = sc;
1649 static void
1650 cmd_declination(void)
1652 real v = read_numeric(fTrue);
1653 if (v == HUGE_REAL) {
1654 get_token_no_blanks();
1655 if (strcmp(ucbuffer, "AUTO") != 0) {
1656 compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_COL, /*Expected number or 'AUTO'*/309);
1657 return;
1659 /* *declination auto X Y Z */
1660 real x = read_numeric(fFalse);
1661 real y = read_numeric(fFalse);
1662 real z = read_numeric(fFalse);
1663 if (!pcs->proj) {
1664 compile_diagnostic(DIAG_ERR, /*Input coordinate system must be specified for '*DECLINATION AUTO'*/301);
1665 return;
1667 if (!proj_wgs84) {
1668 proj_wgs84 = pj_init_plus("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs");
1670 /* Convert to WGS84 lat long. */
1671 if (pj_is_latlong(pcs->proj)) {
1672 /* PROJ expects lat and long in radians. */
1673 x = rad(x);
1674 y = rad(y);
1676 int r = pj_transform(pcs->proj, proj_wgs84, 1, 1, &x, &y, &z);
1677 if (r != 0) {
1678 compile_diagnostic(DIAG_ERR, /*Failed to convert coordinates: %s*/436, pj_strerrno(r));
1679 return;
1681 pcs->z[Q_DECLINATION] = HUGE_REAL;
1682 pcs->dec_x = x;
1683 pcs->dec_y = y;
1684 pcs->dec_z = z;
1685 /* Invalidate cached declination. */
1686 pcs->declination = HUGE_REAL;
1688 projLP lp = { x, y };
1689 struct FACTORS factors;
1690 memset(&factors, 0, sizeof(factors));
1691 pj_factors(lp, proj_out, 0.0, &factors);
1692 pcs->convergence = factors.conv;
1694 } else {
1695 /* *declination D UNITS */
1696 int units = get_units(BIT(Q_DECLINATION), fFalse);
1697 if (units == UNITS_NULL) {
1698 return;
1700 pcs->z[Q_DECLINATION] = -v * factor_tab[units];
1701 pcs->convergence = 0;
1705 #ifndef NO_DEPRECATED
1706 static void
1707 cmd_default(void)
1709 static const sztok defaulttab[] = {
1710 { "CALIBRATE", CMD_CALIBRATE },
1711 { "DATA", CMD_DATA },
1712 { "UNITS", CMD_UNITS },
1713 { NULL, CMD_NULL }
1715 static int default_depr_count = 0;
1717 if (default_depr_count < 5) {
1718 /* TRANSLATORS: If you're unsure what "deprecated" means, see:
1719 * http://en.wikipedia.org/wiki/Deprecation */
1720 compile_diagnostic(DIAG_WARN|DIAG_COL, /**DEFAULT is deprecated - use *CALIBRATE/DATA/SD/UNITS with argument DEFAULT instead*/20);
1721 if (++default_depr_count == 5)
1722 compile_diagnostic(DIAG_WARN, /*Further uses of this deprecated feature will not be reported*/95);
1725 get_token();
1726 switch (match_tok(defaulttab, TABSIZE(defaulttab))) {
1727 case CMD_CALIBRATE:
1728 default_calib(pcs);
1729 break;
1730 case CMD_DATA:
1731 default_style(pcs);
1732 default_grade(pcs);
1733 break;
1734 case CMD_UNITS:
1735 default_units(pcs);
1736 break;
1737 default:
1738 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*Unknown setting “%s”*/41, buffer);
1741 #endif
1743 static void
1744 cmd_include(void)
1746 char *pth, *fnm = NULL;
1747 int fnm_len;
1748 #ifndef NO_DEPRECATED
1749 prefix *root_store;
1750 #endif
1751 int ch_store;
1753 pth = path_from_fnm(file.filename);
1755 read_string(&fnm, &fnm_len);
1757 #ifndef NO_DEPRECATED
1758 /* Since *begin / *end nesting cannot cross file boundaries we only
1759 * need to preserve the prefix if the deprecated *prefix command
1760 * can be used */
1761 root_store = root;
1762 root = pcs->Prefix; /* Root for include file is current prefix */
1763 #endif
1764 ch_store = ch;
1766 data_file(pth, fnm);
1768 #ifndef NO_DEPRECATED
1769 root = root_store; /* and restore root */
1770 #endif
1771 ch = ch_store;
1773 s_free(&fnm);
1774 osfree(pth);
1777 static void
1778 cmd_sd(void)
1780 real sd, variance;
1781 int units;
1782 unsigned long qmask, m;
1783 int quantity;
1784 qmask = get_qlist(BIT(Q_DECLINATION));
1785 if (!qmask) return; /* no quantities found - error already reported */
1787 if (qmask == BIT(Q_DEFAULT)) {
1788 default_grade(pcs);
1789 return;
1791 sd = read_numeric(fFalse);
1792 if (sd <= (real)0.0) {
1793 compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_COL, /*Standard deviation must be positive*/48);
1794 return;
1796 units = get_units(qmask, fFalse);
1797 if (units == UNITS_NULL) return;
1799 sd *= factor_tab[units];
1800 variance = sqrd(sd);
1802 for (quantity = 0, m = BIT(quantity); m <= qmask; quantity++, m <<= 1)
1803 if (qmask & m) pcs->Var[quantity] = variance;
1806 static void
1807 cmd_title(void)
1809 if (!fExplicitTitle && pcs->Prefix == root) {
1810 /* If we don't have an explicit title yet, and we're currently in the
1811 * root prefix, use this title explicitly. */
1812 fExplicitTitle = fTrue;
1813 read_string(&survey_title, &survey_title_len);
1814 } else {
1815 /* parse and throw away this title (but still check rest of line) */
1816 char *s = NULL;
1817 int len;
1818 read_string(&s, &len);
1819 s_free(&s);
1823 static const sztok case_tab[] = {
1824 {"PRESERVE", OFF},
1825 {"TOLOWER", LOWER},
1826 {"TOUPPER", UPPER},
1827 {NULL, -1}
1830 static void
1831 cmd_case(void)
1833 int setting;
1834 get_token();
1835 setting = match_tok(case_tab, TABSIZE(case_tab));
1836 if (setting != -1) {
1837 pcs->Case = setting;
1838 } else {
1839 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*Found “%s”, expecting “PRESERVE”, “TOUPPER”, or “TOLOWER”*/10, buffer);
1843 typedef enum {
1844 CS_NONE = -1,
1845 CS_CUSTOM,
1846 CS_EPSG,
1847 CS_ESRI,
1848 CS_EUR,
1849 CS_IJTSK,
1850 CS_JTSK,
1851 CS_LAT,
1852 CS_LOCAL,
1853 CS_LONG,
1854 CS_OSGB,
1855 CS_S_MERC,
1856 CS_UTM
1857 } cs_class;
1859 static const sztok cs_tab[] = {
1860 {"CUSTOM", CS_CUSTOM},
1861 {"EPSG", CS_EPSG}, /* EPSG:<number> */
1862 {"ESRI", CS_ESRI}, /* ESRI:<number> */
1863 {"EUR", CS_EUR}, /* EUR79Z30 */
1864 {"IJTSK", CS_IJTSK}, /* IJTSK or IJTSK03 */
1865 {"JTSK", CS_JTSK}, /* JTSK or JTSK03 */
1866 {"LAT", CS_LAT}, /* LAT-LONG */
1867 {"LOCAL", CS_LOCAL},
1868 {"LONG", CS_LONG}, /* LONG-LAT */
1869 {"OSGB", CS_OSGB}, /* OSGB:<H, N, O, S or T><A-Z except I> */
1870 {"S", CS_S_MERC}, /* S-MERC */
1871 {"UTM", CS_UTM}, /* UTM<zone><N or S or nothing> */
1872 {NULL, CS_NONE}
1875 static void
1876 cmd_cs(void)
1878 char * proj_str = NULL;
1879 int proj_str_len;
1880 cs_class cs;
1881 int cs_sub = INT_MIN;
1882 filepos fp;
1883 bool output = fFalse;
1884 enum { YES, NO, MAYBE } ok_for_output = YES;
1885 static bool had_cs = fFalse;
1887 if (!had_cs) {
1888 had_cs = fTrue;
1889 if (first_fix_name) {
1890 compile_diagnostic_at(DIAG_ERR,
1891 first_fix_filename, first_fix_line,
1892 /*Station “%s” fixed before CS command first used*/442,
1893 sprint_prefix(first_fix_name));
1897 get_pos(&fp);
1898 /* Note get_token() only accepts letters - it'll stop at digits so "UTM12"
1899 * will give token "UTM". */
1900 get_token();
1901 if (strcmp(ucbuffer, "OUT") == 0) {
1902 output = fTrue;
1903 get_pos(&fp);
1904 get_token();
1906 cs = match_tok(cs_tab, TABSIZE(cs_tab));
1907 switch (cs) {
1908 case CS_NONE:
1909 break;
1910 case CS_CUSTOM:
1911 ok_for_output = MAYBE;
1912 get_pos(&fp);
1913 read_string(&proj_str, &proj_str_len);
1914 cs_sub = 0;
1915 break;
1916 case CS_EPSG: case CS_ESRI:
1917 ok_for_output = MAYBE;
1918 if (ch == ':' && isdigit(nextch())) {
1919 unsigned n = read_uint();
1920 if (n < 1000000) {
1921 cs_sub = (int)n;
1924 break;
1925 case CS_EUR:
1926 if (isdigit(ch) &&
1927 read_uint() == 79 &&
1928 (ch == 'Z' || ch == 'z') &&
1929 isdigit(nextch()) &&
1930 read_uint() == 30) {
1931 cs_sub = 7930;
1933 break;
1934 case CS_JTSK:
1935 ok_for_output = NO;
1936 /* FALLTHRU */
1937 case CS_IJTSK:
1938 if (ch == '0') {
1939 if (nextch() == '3') {
1940 nextch();
1941 cs_sub = 3;
1943 } else {
1944 cs_sub = 0;
1946 break;
1947 case CS_LAT: case CS_LONG:
1948 ok_for_output = NO;
1949 if (ch == '-') {
1950 nextch();
1951 get_token_no_blanks();
1952 cs_class cs2 = match_tok(cs_tab, TABSIZE(cs_tab));
1953 if ((cs ^ cs2) == (CS_LAT ^ CS_LONG)) {
1954 cs_sub = 0;
1957 break;
1958 case CS_LOCAL:
1959 cs_sub = 0;
1960 break;
1961 case CS_OSGB:
1962 if (ch == ':') {
1963 int uch1 = toupper(nextch());
1964 if (strchr("HNOST", uch1)) {
1965 int uch2 = toupper(nextch());
1966 if (uch2 >= 'A' && uch2 <= 'Z' && uch2 != 'I') {
1967 int x, y;
1968 nextch();
1969 if (uch1 > 'I') --uch1;
1970 uch1 -= 'A';
1971 if (uch2 > 'I') --uch2;
1972 uch2 -= 'A';
1973 x = uch1 % 5;
1974 y = uch1 / 5;
1975 x = (x * 5) + uch2 % 5;
1976 y = (y * 5) + uch2 / 5;
1977 cs_sub = y * 25 + x;
1981 break;
1982 case CS_S_MERC:
1983 if (ch == '-') {
1984 nextch();
1985 get_token_no_blanks();
1986 if (strcmp(ucbuffer, "MERC") == 0) {
1987 cs_sub = 0;
1990 break;
1991 case CS_UTM:
1992 if (isdigit(ch)) {
1993 unsigned n = read_uint();
1994 if (n >= 1 && n <= 60) {
1995 int uch = toupper(ch);
1996 cs_sub = (int)n;
1997 if (uch == 'S') {
1998 nextch();
1999 cs_sub = -cs_sub;
2000 } else if (uch == 'N') {
2001 nextch();
2005 break;
2007 if (cs_sub == INT_MIN || isalnum(ch)) {
2008 set_pos(&fp);
2009 compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Unknown coordinate system*/434);
2010 skipline();
2011 return;
2013 /* Actually handle the cs */
2014 switch (cs) {
2015 case CS_NONE:
2016 break;
2017 case CS_CUSTOM:
2018 /* proj_str already set */
2019 break;
2020 case CS_EPSG:
2021 proj_str = osmalloc(32);
2022 sprintf(proj_str, "+init=epsg:%d +no_defs", cs_sub);
2023 break;
2024 case CS_ESRI:
2025 proj_str = osmalloc(32);
2026 sprintf(proj_str, "+init=esri:%d +no_defs", cs_sub);
2027 break;
2028 case CS_EUR:
2029 proj_str = osstrdup("+proj=utm +zone=30 +ellps=intl +towgs84=-86,-98,-119,0,0,0,0 +no_defs");
2030 break;
2031 case CS_IJTSK:
2032 if (cs_sub == 0)
2033 proj_str = osstrdup("+proj=krovak +ellps=bessel +towgs84=570.8285,85.6769,462.842,4.9984,1.5867,5.2611,3.5623 +no_defs");
2034 else
2035 proj_str = osstrdup("+proj=krovak +ellps=bessel +towgs84=485.021,169.465,483.839,7.786342,4.397554,4.102655,0 +no_defs");
2036 break;
2037 case CS_JTSK:
2038 if (cs_sub == 0)
2039 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");
2040 else
2041 proj_str = osstrdup("+proj=krovak +czech +ellps=bessel +towgs84=485.021,169.465,483.839,7.786342,4.397554,4.102655,0 +no_defs");
2042 break;
2043 case CS_LAT:
2044 /* FIXME: Requires PROJ >= 4.8.0 for +axis, and the SDs will be
2045 * misapplied, so we may want to swap ourselves. Also, while
2046 * therion supports lat-long, I'm not totally convinced that it is
2047 * sensible to do so - people often say "lat-long", but probably
2048 * don't think that that's actually "Northing, Easting". This
2049 * seems like it'll result in people accidentally getting X and Y
2050 * swapped in their fixed points...
2052 #if 0
2053 proj_str = osstrdup("+proj=longlat +ellps=WGS84 +datum=WGS84 +axis=neu +no_defs");
2054 #endif
2055 break;
2056 case CS_LOCAL:
2057 /* FIXME: Is it useful to be able to explicitly specify this? */
2058 break;
2059 case CS_LONG:
2060 proj_str = osstrdup("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs");
2061 break;
2062 case CS_OSGB: {
2063 int x = 14 - (cs_sub % 25);
2064 int y = (cs_sub / 25) - 20;
2065 proj_str = osmalloc(160);
2066 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);
2067 break;
2069 case CS_S_MERC:
2070 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");
2071 break;
2072 case CS_UTM:
2073 proj_str = osmalloc(74);
2074 if (cs_sub > 0) {
2075 sprintf(proj_str, "+proj=utm +ellps=WGS84 +datum=WGS84 +units=m +zone=%d +no_defs", cs_sub);
2076 } else {
2077 sprintf(proj_str, "+proj=utm +ellps=WGS84 +datum=WGS84 +units=m +zone=%d +south +no_defs", -cs_sub);
2079 break;
2082 if (!proj_str) {
2083 /* printf("CS %d:%d\n", (int)cs, cs_sub); */
2084 set_pos(&fp);
2085 compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Unknown coordinate system*/434);
2086 skipline();
2087 return;
2090 if (output) {
2091 if (ok_for_output == NO) {
2092 set_pos(&fp);
2093 compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Coordinate system unsuitable for output*/435);
2094 skipline();
2095 return;
2098 /* If the output projection is already set, we still need to create the
2099 * projection object for a custom projection, so we can report errors.
2100 * But if the string is identical, we know it's valid.
2102 if (!proj_out ||
2103 (ok_for_output == MAYBE && strcmp(proj_str, proj_str_out) != 0)) {
2104 projPJ pj = pj_init_plus(proj_str);
2105 if (!pj) {
2106 set_pos(&fp);
2107 compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Invalid coordinate system: %s*/443,
2108 pj_strerrno(pj_errno));
2109 skipline();
2110 return;
2112 if (ok_for_output == MAYBE && pj_is_latlong(pj)) {
2113 set_pos(&fp);
2114 compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Coordinate system unsuitable for output*/435);
2115 skipline();
2116 return;
2118 if (proj_out) {
2119 pj_free(pj);
2120 osfree(proj_str);
2121 } else {
2122 proj_out = pj;
2123 proj_str_out = proj_str;
2126 } else {
2127 projPJ pj;
2128 if (proj_str_out && strcmp(proj_str, proj_str_out) == 0) {
2129 /* Same as the current output projection. */
2130 pj = proj_out;
2131 } else {
2132 pj = pj_init_plus(proj_str);
2133 if (!pj) {
2134 set_pos(&fp);
2135 compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Invalid coordinate system: %s*/443,
2136 pj_strerrno(pj_errno));
2137 skipline();
2138 return;
2142 /* Free proj if not used by parent, or as the output projection. */
2143 settings * p = pcs;
2144 if (p->proj && (!p->next || p->proj != p->next->proj))
2145 if (p->proj != proj_out)
2146 pj_free(p->proj);
2147 p->proj = pj;
2151 static const sztok infer_tab[] = {
2152 { "EQUATES", INFER_EQUATES },
2153 { "EXPORTS", INFER_EXPORTS },
2154 { "PLUMBS", INFER_PLUMBS },
2155 #if 0 /* FIXME */
2156 { "SUBSURVEYS", INFER_SUBSURVEYS },
2157 #endif
2158 { NULL, INFER_NULL }
2161 static const sztok onoff_tab[] = {
2162 { "OFF", 0 },
2163 { "ON", 1 },
2164 { NULL, -1 }
2167 static void
2168 cmd_infer(void)
2170 infer_what setting;
2171 int on;
2172 get_token();
2173 setting = match_tok(infer_tab, TABSIZE(infer_tab));
2174 if (setting == INFER_NULL) {
2175 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*Found “%s”, expecting “EQUATES”, “EXPORTS”, or “PLUMBS”*/31, buffer);
2176 return;
2178 get_token();
2179 on = match_tok(onoff_tab, TABSIZE(onoff_tab));
2180 if (on == -1) {
2181 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*Found “%s”, expecting “ON” or “OFF”*/32, buffer);
2182 return;
2185 if (on) {
2186 pcs->infer |= BIT(setting);
2187 if (setting == INFER_EXPORTS) fExportUsed = fTrue;
2188 } else {
2189 pcs->infer &= ~BIT(setting);
2193 static void
2194 cmd_truncate(void)
2196 unsigned int truncate_at = 0; /* default is no truncation */
2197 filepos fp;
2199 get_pos(&fp);
2201 get_token();
2202 if (strcmp(ucbuffer, "OFF") != 0) {
2203 if (*ucbuffer) set_pos(&fp);
2204 truncate_at = read_uint();
2206 /* for backward compatibility, "*truncate 0" means "*truncate off" */
2207 pcs->Truncate = (truncate_at == 0) ? INT_MAX : truncate_at;
2210 static void
2211 cmd_ref(void)
2213 /* Just syntax check for now. */
2214 char *ref = NULL;
2215 int ref_len;
2216 read_string(&ref, &ref_len);
2217 s_free(&ref);
2220 static void
2221 cmd_require(void)
2223 const unsigned int version[] = {COMMAVERSION};
2224 const unsigned int *ver = version;
2225 filepos fp;
2227 skipblanks();
2228 get_pos(&fp);
2229 while (1) {
2230 int diff = *ver++ - read_uint();
2231 if (diff > 0) break;
2232 if (diff < 0) {
2233 size_t i, len;
2234 char *v;
2235 filepos fp_tmp;
2237 /* find end of version number */
2238 while (isdigit(ch) || ch == '.') nextch();
2239 get_pos(&fp_tmp);
2240 len = (size_t)(fp_tmp.offset - fp.offset);
2241 v = osmalloc(len + 1);
2242 set_pos(&fp);
2243 for (i = 0; i < len; i++) {
2244 v[i] = ch;
2245 nextch();
2247 v[i] = '\0';
2248 /* TRANSLATORS: Feel free to translate as "or newer" instead of "or
2249 * greater" if that gives a more natural translation. It's
2250 * technically not quite right when there are parallel active release
2251 * series (e.g. Survex 1.0.40 was released *after* 1.2.0), but this
2252 * seems unlikely to confuse users. "Survex" is the name of the
2253 * software, so should not be translated.
2255 * Here "survey" is a "cave map" rather than list of questions - it should be
2256 * translated to the terminology that cavers using the language would use.
2258 fatalerror_in_file(file.filename, file.line, /*Survex version %s or greater required to process this survey data.*/2, v);
2260 if (ch != '.') break;
2261 nextch();
2262 if (!isdigit(ch) || ver == version + sizeof(version) / sizeof(*version))
2263 break;
2265 /* skip rest of version number */
2266 while (isdigit(ch) || ch == '.') nextch();
2269 /* allocate new meta_data if need be */
2270 void
2271 copy_on_write_meta(settings *s)
2273 if (!s->meta || s->meta->ref_count != 0) {
2274 meta_data * meta_new = osnew(meta_data);
2275 if (!s->meta) {
2276 meta_new->days1 = meta_new->days2 = -1;
2277 } else {
2278 *meta_new = *(s->meta);
2280 meta_new->ref_count = 0;
2281 s->meta = meta_new;
2285 static void
2286 cmd_date(void)
2288 int year, month, day;
2289 int days1, days2;
2290 bool implicit_range = fFalse;
2291 filepos fp, fp2;
2293 get_pos(&fp);
2294 read_date(&year, &month, &day);
2295 days1 = days_since_1900(year, month ? month : 1, day ? day : 1);
2297 if (days1 > current_days_since_1900) {
2298 set_pos(&fp);
2299 compile_diagnostic(DIAG_WARN|DIAG_DATE, /*Date is in the future!*/80);
2302 skipblanks();
2303 if (ch == '-') {
2304 nextch();
2305 get_pos(&fp2);
2306 read_date(&year, &month, &day);
2307 } else {
2308 if (month && day) {
2309 days2 = days1;
2310 goto read;
2312 implicit_range = fTrue;
2315 if (month == 0) month = 12;
2316 if (day == 0) day = last_day(year, month);
2317 days2 = days_since_1900(year, month, day);
2319 if (!implicit_range && days2 > current_days_since_1900) {
2320 set_pos(&fp2);
2321 compile_diagnostic(DIAG_WARN|DIAG_DATE, /*Date is in the future!*/80);
2324 if (days2 < days1) {
2325 set_pos(&fp);
2326 compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*End of date range is before the start*/81);
2327 int tmp = days1;
2328 days1 = days2;
2329 days2 = tmp;
2332 read:
2333 if (!pcs->meta || pcs->meta->days1 != days1 || pcs->meta->days2 != days2) {
2334 copy_on_write_meta(pcs);
2335 pcs->meta->days1 = days1;
2336 pcs->meta->days2 = days2;
2337 /* Invalidate cached declination. */
2338 pcs->declination = HUGE_REAL;
2342 typedef void (*cmd_fn)(void);
2344 static const cmd_fn cmd_funcs[] = {
2345 cmd_alias,
2346 cmd_begin,
2347 cmd_calibrate,
2348 cmd_case,
2349 skipline, /*cmd_copyright,*/
2350 cmd_cs,
2351 cmd_data,
2352 cmd_date,
2353 cmd_declination,
2354 #ifndef NO_DEPRECATED
2355 cmd_default,
2356 #endif
2357 cmd_end,
2358 cmd_entrance,
2359 cmd_equate,
2360 cmd_export,
2361 cmd_fix,
2362 cmd_flags,
2363 cmd_include,
2364 cmd_infer,
2365 skipline, /*cmd_instrument,*/
2366 #ifndef NO_DEPRECATED
2367 cmd_prefix,
2368 #endif
2369 cmd_ref,
2370 cmd_require,
2371 cmd_sd,
2372 cmd_set,
2373 solve_network,
2374 skipline, /*cmd_team,*/
2375 cmd_title,
2376 cmd_truncate,
2377 cmd_units
2380 extern void
2381 handle_command(void)
2383 int cmdtok;
2384 get_token();
2385 cmdtok = match_tok(cmd_tab, TABSIZE(cmd_tab));
2387 if (cmdtok < 0 || cmdtok >= (int)(sizeof(cmd_funcs) / sizeof(cmd_fn))) {
2388 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*Unknown command “%s”*/12, buffer);
2389 return;
2392 switch (cmdtok) {
2393 case CMD_EXPORT:
2394 if (!f_export_ok)
2395 /* TRANSLATORS: The *EXPORT command is only valid just after *BEGIN
2396 * <SURVEY>, so this would generate this error:
2398 * *begin fred
2399 * 1 2 1.23 045 -6
2400 * *export 2
2401 * *end fred */
2402 compile_diagnostic(DIAG_ERR, /**EXPORT must immediately follow “*BEGIN <SURVEY>”*/57);
2403 break;
2404 case CMD_ALIAS:
2405 case CMD_CALIBRATE:
2406 case CMD_CASE:
2407 case CMD_COPYRIGHT:
2408 case CMD_CS:
2409 case CMD_DATA:
2410 case CMD_DATE:
2411 case CMD_DECLINATION:
2412 case CMD_DEFAULT:
2413 case CMD_FLAGS:
2414 case CMD_INFER:
2415 case CMD_INSTRUMENT:
2416 case CMD_REF:
2417 case CMD_REQUIRE:
2418 case CMD_SD:
2419 case CMD_SET:
2420 case CMD_TEAM:
2421 case CMD_TITLE:
2422 case CMD_TRUNCATE:
2423 case CMD_UNITS:
2424 /* These can occur between *begin and *export */
2425 break;
2426 default:
2427 /* NB: additional handling for "*begin <survey>" in cmd_begin */
2428 f_export_ok = fFalse;
2429 break;
2432 cmd_funcs[cmdtok]();