source: git/src/commands.c@ 36efb03

RELEASE/1.2 debug-ci debug-ci-sanitisers faster-cavernlog log-select main stereo stereo-2025 walls-data walls-data-hanging-as-warning warn-only-for-hanging-survey
Last change on this file since 36efb03 was 36efb03, checked in by Olly Betts <olly@…>, 11 years ago

src/: Add more TRANSLATORS comments.

  • Property mode set to 100644
File size: 59.3 KB
RevLine 
[ff6cfe1]1/* commands.c
[d1b1380]2 * Code for directives
[82919e07]3 * Copyright (C) 1991-2003,2004,2005,2006,2010,2011,2012,2013,2014,2015 Olly Betts
[846746e]4 *
[89231c4]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.
[846746e]9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
[89231c4]12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
[846746e]14 *
[89231c4]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
[d333899]17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
[d1b1380]18 */
19
[a420b49]20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
[d1b1380]23
24#include <assert.h>
[a420b49]25#include <limits.h>
[be97baf]26#include <stddef.h> /* for offsetof */
[a420b49]27
[c092d72]28#include <proj_api.h>
29
[a420b49]30#include "cavern.h"
[d1b1380]31#include "commands.h"
32#include "datain.h"
[1ee204e]33#include "date.h"
[d1b1380]34#include "debug.h"
[5853657]35#include "filename.h"
36#include "message.h"
37#include "netbits.h"
38#include "netskel.h"
[5f1e194]39#include "out.h"
[5853657]40#include "readval.h"
[69c920d]41#include "str.h"
[a420b49]42
43static void
44default_grade(settings *s)
45{
[770157e]46 /* Values correspond to those in bcra5.svx */
47 s->Var[Q_POS] = (real)sqrd(0.05);
48 s->Var[Q_LENGTH] = (real)sqrd(0.05);
49 s->Var[Q_COUNT] = (real)sqrd(0.05);
50 s->Var[Q_DX] = s->Var[Q_DY] = s->Var[Q_DZ] = (real)sqrd(0.05);
51 s->Var[Q_BEARING] = (real)sqrd(rad(0.5));
52 s->Var[Q_GRADIENT] = (real)sqrd(rad(0.5));
53 s->Var[Q_BACKBEARING] = (real)sqrd(rad(0.5));
54 s->Var[Q_BACKGRADIENT] = (real)sqrd(rad(0.5));
[a420b49]55 /* SD of plumbed legs (0.25 degrees?) */
56 s->Var[Q_PLUMB] = (real)sqrd(rad(0.25));
[6b7079f]57 /* SD of level legs (0.25 degrees?) */
58 s->Var[Q_LEVEL] = (real)sqrd(rad(0.25));
[770157e]59 s->Var[Q_DEPTH] = (real)sqrd(0.05);
[a420b49]60}
61
62static void
63default_truncate(settings *s)
64{
65 s->Truncate = INT_MAX;
66}
67
68static void
69default_case(settings *s)
70{
71 s->Case = LOWER;
72}
73
[27b8b59]74static reading default_order[] = { Fr, To, Tape, Comp, Clino, End };
[a420b49]75
76static void
77default_style(settings *s)
78{
[107b8bd]79 s->style = STYLE_NORMAL;
[a420b49]80 s->ordering = default_order;
[dcbcae0]81 s->dash_for_anon_wall_station = fFalse;
[a420b49]82}
83
84static void
85default_prefix(settings *s)
86{
87 s->Prefix = root;
88}
89
90static void
91default_translate(settings *s)
92{
93 int i;
94 short *t;
95 if (s->next && s->next->Translate == s->Translate) {
96 t = ((short*)osmalloc(ossizeof(short) * 257)) + 1;
97 memcpy(t - 1, s->Translate - 1, sizeof(short) * 257);
98 s->Translate = t;
99 }
[4c07c51]100/* SVX_ASSERT(EOF==-1);*/ /* important, since we rely on this */
[a420b49]101 t = s->Translate;
[be97baf]102 memset(t - 1, 0, sizeof(short) * 257);
103 for (i = '0'; i <= '9'; i++) t[i] = SPECIAL_NAMES;
104 for (i = 'A'; i <= 'Z'; i++) t[i] = SPECIAL_NAMES;
105 for (i = 'a'; i <= 'z'; i++) t[i] = SPECIAL_NAMES;
[a420b49]106
107 t['\t'] |= SPECIAL_BLANK;
108 t[' '] |= SPECIAL_BLANK;
109 t[','] |= SPECIAL_BLANK;
110 t[';'] |= SPECIAL_COMMENT;
111 t['\032'] |= SPECIAL_EOL; /* Ctrl-Z, so olde DOS text files are handled ok */
112 t[EOF] |= SPECIAL_EOL;
113 t['\n'] |= SPECIAL_EOL;
114 t['\r'] |= SPECIAL_EOL;
115 t['*'] |= SPECIAL_KEYWORD;
116 t['-'] |= SPECIAL_OMIT;
117 t['\\'] |= SPECIAL_ROOT;
118 t['.'] |= SPECIAL_SEPARATOR;
119 t['_'] |= SPECIAL_NAMES;
[f3ac7d4]120 t['-'] |= SPECIAL_NAMES; /* Added in 0.97 prerelease 4 */
[a420b49]121 t['.'] |= SPECIAL_DECIMAL;
122 t['-'] |= SPECIAL_MINUS;
123 t['+'] |= SPECIAL_PLUS;
[3593388]124#if 0 /* FIXME */
[7d40549]125 t['{'] |= SPECIAL_OPEN;
126 t['}'] |= SPECIAL_CLOSE;
[3593388]127#endif
[a420b49]128}
129
[be97baf]130void
[a420b49]131default_units(settings *s)
132{
133 int quantity;
134 for (quantity = 0; quantity < Q_MAC; quantity++) {
[7f08c83]135 if (TSTBIT(ANG_QMASK, quantity))
[bca0071]136 s->units[quantity] = (real)(M_PI / 180.0); /* degrees */
[a420b49]137 else
138 s->units[quantity] = (real)1.0; /* metres */
139 }
[fa42426]140 s->f_clino_percent = s->f_backclino_percent = fFalse;
[a420b49]141}
142
[be97baf]143void
[a420b49]144default_calib(settings *s)
145{
146 int quantity;
147 for (quantity = 0; quantity < Q_MAC; quantity++) {
148 s->z[quantity] = (real)0.0;
149 s->sc[quantity] = (real)1.0;
150 }
151}
152
[5c3c61a]153static void
154default_flags(settings *s)
155{
156 s->flags = 0;
157}
158
[a420b49]159extern void
160default_all(settings *s)
161{
162 default_truncate(s);
[27b8b59]163 s->infer = 0;
[a420b49]164 default_case(s);
165 default_style(s);
166 default_prefix(s);
167 default_translate(s);
168 default_grade(s);
169 default_units(s);
170 default_calib(s);
[5c3c61a]171 default_flags(s);
[a420b49]172}
173
[ee1ec59]174char *buffer = NULL;
[ee7bafa]175static int buf_len;
[d1b1380]176
[cfe093e]177static char *ucbuffer = NULL;
[5f1e194]178
[647407d]179/* read token */
[a420b49]180extern void
181get_token(void)
[f6bdb01]182{
183 skipblanks();
184 get_token_no_blanks();
185}
186
187extern void
188get_token_no_blanks(void)
[a420b49]189{
[be97baf]190 int i = -1;
191
[cfe093e]192 s_zero(&buffer);
193 osfree(ucbuffer);
[5f1e194]194 while (isalpha(ch)) {
[63dc4eb]195 s_catchar(&buffer, &buf_len, (char)ch);
[5f1e194]196 nextch();
197 }
[cfe093e]198
[be97baf]199 if (!buffer) s_catchar(&buffer, &buf_len, '\0');
200
[cfe093e]201 ucbuffer = osmalloc(buf_len);
[be97baf]202 do {
203 i++;
204 ucbuffer[i] = toupper(buffer[i]);
205 } while (buffer[i]);
[cfe093e]206#if 0
[f6bdb01]207 printf("get_token_no_blanks() got “%s”\n", buffer);
[cfe093e]208#endif
[d1b1380]209}
210
[dcbcae0]211/* read word */
212static void
213get_word(void)
214{
215 s_zero(&buffer);
216 skipblanks();
217 while (!isBlank(ch) && !isEol(ch)) {
218 s_catchar(&buffer, &buf_len, (char)ch);
219 nextch();
220 }
221
222 if (!buffer) s_catchar(&buffer, &buf_len, '\0');
223#if 0
224 printf("get_word() got “%s”\n", buffer);
225#endif
226}
227
[d1b1380]228/* match_tok() now uses binary chop
229 * tab argument should be alphabetically sorted (ascending)
230 */
[a420b49]231extern int
232match_tok(const sztok *tab, int tab_size)
233{
[5f1e194]234 int a = 0, b = tab_size - 1, c;
235 int r;
236 assert(tab_size > 0); /* catch empty table */
[d1b1380]237/* printf("[%d,%d]",a,b); */
[5f1e194]238 while (a <= b) {
239 c = (unsigned)(a + b) / 2;
[d1b1380]240/* printf(" %d",c); */
[647407d]241 r = strcmp(tab[c].sz, ucbuffer);
[5f1e194]242 if (r == 0) return tab[c].tok; /* match */
243 if (r < 0)
244 a = c + 1;
245 else
246 b = c - 1;
247 }
248 return tab[tab_size].tok; /* no match */
[d1b1380]249}
250
[fb2e93c]251typedef enum {
[dcbcae0]252 CMD_NULL = -1, CMD_ALIAS, CMD_BEGIN, CMD_CALIBRATE, CMD_CASE, CMD_COPYRIGHT,
[abd0310]253 CMD_CS, CMD_DATA, CMD_DATE, CMD_DEFAULT, CMD_END, CMD_ENTRANCE, CMD_EQUATE,
[dfb4240]254 CMD_EXPORT, CMD_FIX, CMD_FLAGS, CMD_INCLUDE, CMD_INFER, CMD_INSTRUMENT,
[82be646]255 CMD_PREFIX, CMD_REQUIRE, CMD_SD, CMD_SET, CMD_SOLVE,
[13a48f6]256 CMD_TEAM, CMD_TITLE, CMD_TRUNCATE, CMD_UNITS
[5f1e194]257} cmds;
[cb3d1e2]258
[82919e07]259static const sztok cmd_tab[] = {
[dcbcae0]260 {"ALIAS", CMD_ALIAS},
[5f1e194]261 {"BEGIN", CMD_BEGIN},
262 {"CALIBRATE", CMD_CALIBRATE},
[a420b49]263 {"CASE", CMD_CASE},
[13a48f6]264 {"COPYRIGHT", CMD_COPYRIGHT},
[abd0310]265 {"CS", CMD_CS},
[5f1e194]266 {"DATA", CMD_DATA},
[13a48f6]267 {"DATE", CMD_DATE},
[a7d5f1c]268#ifndef NO_DEPRECATED
[5f1e194]269 {"DEFAULT", CMD_DEFAULT},
[a7d5f1c]270#endif
[5f1e194]271 {"END", CMD_END},
[dfb4240]272 {"ENTRANCE", CMD_ENTRANCE},
[5f1e194]273 {"EQUATE", CMD_EQUATE},
[fb2e93c]274 {"EXPORT", CMD_EXPORT},
[5f1e194]275 {"FIX", CMD_FIX},
[5c3c61a]276 {"FLAGS", CMD_FLAGS},
[5f1e194]277 {"INCLUDE", CMD_INCLUDE},
[a420b49]278 {"INFER", CMD_INFER},
[ec6a4b3]279 {"INSTRUMENT",CMD_INSTRUMENT},
[a7d5f1c]280#ifndef NO_DEPRECATED
[5f1e194]281 {"PREFIX", CMD_PREFIX},
[a7d5f1c]282#endif
[647407d]283 {"REQUIRE", CMD_REQUIRE},
[a4ae909]284 {"SD", CMD_SD},
[5f1e194]285 {"SET", CMD_SET},
286 {"SOLVE", CMD_SOLVE},
[13a48f6]287 {"TEAM", CMD_TEAM},
[a420b49]288 {"TITLE", CMD_TITLE},
289 {"TRUNCATE", CMD_TRUNCATE},
[5f1e194]290 {"UNITS", CMD_UNITS},
[a4ae909]291 {NULL, CMD_NULL}
[5f1e194]292};
293
[fa42426]294/* masks for units which are length and angles respectively */
295#define LEN_UMASK (BIT(UNITS_METRES) | BIT(UNITS_FEET) | BIT(UNITS_YARDS))
296#define ANG_UMASK (BIT(UNITS_DEGS) | BIT(UNITS_GRADS) | BIT(UNITS_MINUTES))
297
298/* ordering must be the same as the units enum */
[85c0078]299const real factor_tab[] = {
[fa42426]300 1.0, METRES_PER_FOOT, (METRES_PER_FOOT*3.0),
301 (M_PI/180.0), (M_PI/200.0), 0.01, (M_PI/180.0/60.0)
302};
303
[85c0078]304const int units_to_msgno[] = {
305 /*m*/424,
306 /*ft*/428,
307 -1, /* yards */
308 /*°*/344,
309 /*ᵍ*/76,
310 /*%*/96,
311 -1 /* minutes */
312};
313
314int get_length_units(int quantity) {
315 double factor = pcs->units[quantity];
316 if (fabs(factor - METRES_PER_FOOT) <= REAL_EPSILON ||
317 fabs(factor - METRES_PER_FOOT * 3.0) <= REAL_EPSILON) {
318 return UNITS_FEET;
319 }
320 return UNITS_METRES;
321}
322
323int get_angle_units(int quantity) {
324 double factor = pcs->units[quantity];
325 if (fabs(factor - M_PI / 200.0) <= REAL_EPSILON) {
326 return UNITS_GRADS;
327 }
328 return UNITS_DEGS;
329}
330
[a420b49]331static int
[fa42426]332get_units(unsigned long qmask, bool percent_ok)
[a420b49]333{
[82919e07]334 static const sztok utab[] = {
[5f1e194]335 {"DEGREES", UNITS_DEGS },
[a4ae909]336 {"DEGS", UNITS_DEGS },
337 {"FEET", UNITS_FEET },
338 {"GRADS", UNITS_GRADS },
339 {"METERS", UNITS_METRES },
340 {"METRES", UNITS_METRES },
341 {"METRIC", UNITS_METRES },
342 {"MILS", UNITS_GRADS },
343 {"MINUTES", UNITS_MINUTES },
344 {"PERCENT", UNITS_PERCENT },
[5f1e194]345 {"PERCENTAGE", UNITS_PERCENT },
[a4ae909]346 {"YARDS", UNITS_YARDS },
347 {NULL, UNITS_NULL }
[5f1e194]348 };
[3a33d12]349 int units;
350 get_token();
351 units = match_tok(utab, TABSIZE(utab));
[647407d]352 if (units == UNITS_NULL) {
[da96015]353 file.lpos += strlen(buffer);
354 compile_error_skip(-/*Unknown units “%s”*/35, buffer);
[fa42426]355 return UNITS_NULL;
356 }
357 if (units == UNITS_PERCENT && percent_ok &&
358 !(qmask & ~(BIT(Q_GRADIENT)|BIT(Q_BACKGRADIENT)))) {
359 return units;
360 }
361 if (((qmask & LEN_QMASK) && !TSTBIT(LEN_UMASK, units)) ||
362 ((qmask & ANG_QMASK) && !TSTBIT(ANG_UMASK, units))) {
[da96015]363 file.lpos += strlen(buffer);
[736f7df]364 /* TRANSLATORS: Note: In English you talk about the *units* of a single
365 * measurement, but the correct term in other languages may be singular.
366 */
[da96015]367 compile_error_skip(-/*Invalid units “%s” for quantity*/37, buffer);
[fa42426]368 return UNITS_NULL;
[647407d]369 }
[5f1e194]370 return units;
[d1b1380]371}
372
373/* returns mask with bit x set to indicate quantity x specified */
[67508f0]374static unsigned long
[46cb98f]375get_qlist(unsigned long mask_bad)
[a420b49]376{
[82919e07]377 static const sztok qtab[] = {
[9b5d785]378 {"ALTITUDE", Q_DZ },
[b14f44f]379 {"BACKBEARING", Q_BACKBEARING },
380 {"BACKCLINO", Q_BACKGRADIENT }, /* alternative name */
381 {"BACKCOMPASS", Q_BACKBEARING }, /* alternative name */
382 {"BACKGRADIENT", Q_BACKGRADIENT },
[5f1e194]383 {"BEARING", Q_BEARING },
[ee05463]384 {"CEILING", Q_UP }, /* alternative name */
[a4ae909]385 {"CLINO", Q_GRADIENT }, /* alternative name */
[5f1e194]386 {"COMPASS", Q_BEARING }, /* alternative name */
[a4ae909]387 {"COUNT", Q_COUNT },
[5f1e194]388 {"COUNTER", Q_COUNT }, /* alternative name */
389 {"DECLINATION", Q_DECLINATION },
[a420b49]390 {"DEFAULT", Q_DEFAULT }, /* not a real quantity... */
[a4ae909]391 {"DEPTH", Q_DEPTH },
[ee05463]392 {"DOWN", Q_DOWN },
[a4ae909]393 {"DX", Q_DX }, /* alternative name */
394 {"DY", Q_DY }, /* alternative name */
395 {"DZ", Q_DZ }, /* alternative name */
396 {"EASTING", Q_DX },
[ee05463]397 {"FLOOR", Q_DOWN }, /* alternative name */
[5f1e194]398 {"GRADIENT", Q_GRADIENT },
[ee05463]399 {"LEFT", Q_LEFT },
[5f1e194]400 {"LENGTH", Q_LENGTH },
[a4ae909]401 {"LEVEL", Q_LEVEL},
[9b5d785]402 {"NORTHING", Q_DY },
[a4ae909]403 {"PLUMB", Q_PLUMB},
[5f1e194]404 {"POSITION", Q_POS },
[ee05463]405 {"RIGHT", Q_RIGHT },
[a4ae909]406 {"TAPE", Q_LENGTH }, /* alternative name */
[ee05463]407 {"UP", Q_UP },
[a4ae909]408 {NULL, Q_NULL }
[5f1e194]409 };
[67508f0]410 unsigned long qmask = 0;
[5f1e194]411 int tok;
[3a33d12]412 filepos fp;
413
[a420b49]414 while (1) {
[3a33d12]415 get_pos(&fp);
[a420b49]416 get_token();
[5f1e194]417 tok = match_tok(qtab, TABSIZE(qtab));
[da96015]418 if (tok == Q_DEFAULT && !(mask_bad & BIT(Q_DEFAULT))) {
419 /* Only recognise DEFAULT if it is the first quantity, and then don't
420 * look for any more. */
421 if (qmask == 0)
422 return BIT(Q_DEFAULT);
423 break;
424 }
[5f1e194]425 /* bail out if we reach the table end with no match */
426 if (tok == Q_NULL) break;
427 qmask |= BIT(tok);
[da96015]428 if (qmask & mask_bad) {
429 file.lpos += strlen(buffer);
430 compile_error_skip(-/*Unknown instrument “%s”*/39, buffer);
[46cb98f]431 return 0;
432 }
[5f1e194]433 }
[3a33d12]434
[647407d]435 if (qmask == 0) {
[da96015]436 file.lpos += strlen(buffer);
[36efb03]437 /* TRANSLATORS: A "quantity" is something measured like "LENGTH",
438 * "BEARING", "ALTITUDE", etc. */
[da96015]439 compile_error_skip(-/*Unknown quantity “%s”*/34, buffer);
[3a33d12]440 } else {
441 set_pos(&fp);
[647407d]442 }
[3a33d12]443
[5f1e194]444 return qmask;
[d1b1380]445}
446
447#define SPECIAL_UNKNOWN 0
[a420b49]448static void
[eb18f4d]449cmd_set(void)
[a420b49]450{
[82919e07]451 static const sztok chartab[] = {
[5f1e194]452 {"BLANK", SPECIAL_BLANK },
[3593388]453/*FIXME {"CLOSE", SPECIAL_CLOSE }, */
[5f1e194]454 {"COMMENT", SPECIAL_COMMENT },
455 {"DECIMAL", SPECIAL_DECIMAL },
456 {"EOL", SPECIAL_EOL }, /* EOL won't work well */
457 {"KEYWORD", SPECIAL_KEYWORD },
458 {"MINUS", SPECIAL_MINUS },
459 {"NAMES", SPECIAL_NAMES },
460 {"OMIT", SPECIAL_OMIT },
[3593388]461/*FIXME {"OPEN", SPECIAL_OPEN }, */
[5f1e194]462 {"PLUS", SPECIAL_PLUS },
[7f1ab95]463#ifndef NO_DEPRECATED
[5f1e194]464 {"ROOT", SPECIAL_ROOT },
[7f1ab95]465#endif
[5f1e194]466 {"SEPARATOR", SPECIAL_SEPARATOR },
[a4ae909]467 {NULL, SPECIAL_UNKNOWN }
[5f1e194]468 };
469 int mask;
470 int i;
[b15eeda]471
[a420b49]472 get_token();
[5f1e194]473 mask = match_tok(chartab, TABSIZE(chartab));
[b15eeda]474
[f74d0cb]475 if (mask == SPECIAL_UNKNOWN) {
[da96015]476 file.lpos += strlen(buffer);
477 compile_error_skip(-/*Unknown character class “%s”*/42, buffer);
[5f1e194]478 return;
479 }
[b15eeda]480
[7f1ab95]481#ifndef NO_DEPRECATED
[c86cc71]482 if (mask == SPECIAL_ROOT) {
483 if (root_depr_count < 5) {
[da96015]484 file.lpos += strlen(buffer);
[736f7df]485 /* TRANSLATORS: Use of the ROOT character (which is "\" by default) is
486 * deprecated, so this error would be generated by:
487 *
488 * *equate \foo.7 1
489 *
490 * If you're unsure what "deprecated" means, see:
491 * http://en.wikipedia.org/wiki/Deprecation */
[da96015]492 compile_warning(-/*ROOT is deprecated*/25);
[c86cc71]493 if (++root_depr_count == 5)
[736f7df]494 /* TRANSLATORS: If you're unsure what "deprecated" means, see:
495 * http://en.wikipedia.org/wiki/Deprecation */
[c40038a]496 compile_warning(/*Further uses of this deprecated feature will not be reported*/95);
[c86cc71]497 }
498 }
[7f1ab95]499#endif
[b15eeda]500
[5f1e194]501 /* if we're currently using an inherited translation table, allocate a new
502 * table, and copy old one into it */
503 if (pcs->next && pcs->next->Translate == pcs->Translate) {
504 short *p;
505 p = ((short*)osmalloc(ossizeof(short) * 257)) + 1;
506 memcpy(p - 1, pcs->Translate - 1, sizeof(short) * 257);
507 pcs->Translate = p;
508 }
[11f9067]509
510 skipblanks();
511
[5f1e194]512 /* clear this flag for all non-alphanums */
[a420b49]513 for (i = 0; i < 256; i++)
514 if (!isalnum(i)) pcs->Translate[i] &= ~mask;
[11f9067]515
[5f1e194]516 /* now set this flag for all specified chars */
517 while (!isEol(ch)) {
[11f9067]518 if (!isalnum(ch)) {
519 pcs->Translate[ch] |= mask;
520 } else if (tolower(ch) == 'x') {
521 int hex;
522 filepos fp;
523 get_pos(&fp);
524 nextch();
525 if (!isxdigit(ch)) {
526 set_pos(&fp);
527 break;
528 }
529 hex = isdigit(ch) ? ch - '0' : tolower(ch) - 'a';
530 nextch();
531 if (!isxdigit(ch)) {
532 set_pos(&fp);
533 break;
534 }
535 hex = hex << 4 | (isdigit(ch) ? ch - '0' : tolower(ch) - 'a');
536 pcs->Translate[hex] |= mask;
537 } else {
538 break;
539 }
[5f1e194]540 nextch();
541 }
[d1b1380]542}
543
[4d9eecd]544static void
[613028c]545check_reentry(prefix *survey)
[4d9eecd]546{
[91912b4]547 /* Don't try to check "*prefix \" or "*begin \" */
[613028c]548 if (!survey->up) return;
549 if (TSTBIT(survey->sflags, SFLAGS_PREFIX_ENTERED)) {
[15696f3]550 static int reenter_depr_count = 0;
[c86cc71]551
[e1a66da]552 if (reenter_depr_count >= 5)
553 return;
554
555 /* TRANSLATORS: e.g.
556 *
557 * *begin crawl
558 * 1 2 9.45 234 -01
559 * *end crawl
560 * *begin crawl # <- warning here
561 * 2 3 7.67 223 -03
562 * *end crawl
563 *
564 * If you're unsure what "deprecated" means, see:
565 * http://en.wikipedia.org/wiki/Deprecation */
566 compile_warning(/*Reentering an existing survey is deprecated*/29);
[93e3492]567 compile_warning_pfx(survey, /*Originally entered here*/30);
[e1a66da]568 if (++reenter_depr_count == 5)
569 compile_warning(/*Further uses of this deprecated feature will not be reported*/95);
[4d9eecd]570 } else {
[613028c]571 survey->sflags |= BIT(SFLAGS_PREFIX_ENTERED);
572 survey->filename = file.filename;
573 survey->line = file.line;
[4d9eecd]574 }
575}
576
[7f1ab95]577#ifndef NO_DEPRECATED
[a420b49]578static void
[eb18f4d]579cmd_prefix(void)
[a420b49]580{
[c86cc71]581 static int prefix_depr_count = 0;
[613028c]582 prefix *survey;
[91912b4]583 /* Issue warning first, so "*prefix \" warns first that *prefix is
584 * deprecated and then that ROOT is...
585 */
[c86cc71]586 if (prefix_depr_count < 5) {
[736f7df]587 /* TRANSLATORS: If you're unsure what "deprecated" means, see:
588 * http://en.wikipedia.org/wiki/Deprecation */
[da96015]589 compile_warning(-/**prefix is deprecated - use *begin and *end instead*/6);
[c86cc71]590 if (++prefix_depr_count == 5)
[c40038a]591 compile_warning(/*Further uses of this deprecated feature will not be reported*/95);
[c86cc71]592 }
[613028c]593 survey = read_prefix(PFX_SURVEY|PFX_ALLOW_ROOT);
594 pcs->Prefix = survey;
595 check_reentry(survey);
[d1b1380]596}
[7f1ab95]597#endif
[d1b1380]598
[dcbcae0]599static void
600cmd_alias(void)
601{
[45dcea2]602 /* Currently only two forms are supported:
[dcbcae0]603 * *alias station - ..
[45dcea2]604 * *alias station -
[dcbcae0]605 */
606 get_token();
607 if (strcmp(ucbuffer, "STATION") != 0)
608 goto bad;
609 get_word();
610 if (strcmp(buffer, "-") != 0)
611 goto bad;
612 get_word();
613 if (*buffer && strcmp(buffer, "..") != 0)
614 goto bad;
615 pcs->dash_for_anon_wall_station = (*buffer != '\0');
616 return;
617bad:
618 compile_error_skip(/*Bad *alias command*/397);
619}
620
[a420b49]621static void
[eb18f4d]622cmd_begin(void)
[a420b49]623{
[613028c]624 prefix *survey;
[5f1e194]625 settings *pcsNew;
[4be360f]626
[5f1e194]627 pcsNew = osnew(settings);
628 *pcsNew = *pcs; /* copy contents */
[47c7a94]629 pcsNew->begin_lineno = file.line;
[5f1e194]630 pcsNew->next = pcs;
631 pcs = pcsNew;
[4be360f]632
[613028c]633 survey = read_prefix(PFX_SURVEY|PFX_OPT|PFX_ALLOW_ROOT|PFX_WARN_SEPARATOR);
634 pcs->begin_survey = survey;
635 if (survey) {
636 pcs->Prefix = survey;
637 check_reentry(survey);
[4dcd3af]638 f_export_ok = fTrue;
[d53b0f5]639 }
[647407d]640}
641
642extern void
[eb18f4d]643free_settings(settings *p) {
[647407d]644 /* don't free default ordering or ordering used by parent */
[0395657]645 reading *order = p->ordering;
[0580c6a]646 if (order != default_order && (!p->next || order != p->next->ordering))
647 osfree(order);
[cb3d1e2]648
[647407d]649 /* free Translate if not used by parent */
[0580c6a]650 if (!p->next || p->Translate != p->next->Translate)
651 osfree(p->Translate - 1);
[647407d]652
[aa6536a]653 /* free meta if not used by parent, or in this block */
[ef3d3cc]654 if (p->meta && (!p->next || p->meta != p->next->meta) && p->meta->ref_count == 0)
[b5a3219]655 osfree(p->meta);
656
[c092d72]657 /* free proj if not used by parent, or as the output projection */
658 if (p->proj && (!p->next || p->proj != p->next->proj) && p->proj != proj_out)
659 pj_free(p->proj);
660
[eb18f4d]661 osfree(p);
[d1b1380]662}
663
[4be360f]664static void
[eb18f4d]665cmd_end(void)
[a420b49]666{
[5f1e194]667 settings *pcsParent;
[613028c]668 prefix *survey, *begin_survey;
[4be360f]669
[5f1e194]670 pcsParent = pcs->next;
[4be360f]671
[47c7a94]672 if (pcs->begin_lineno == 0) {
[4be360f]673 if (pcsParent == NULL) {
674 /* more ENDs than BEGINs */
[fa42426]675 compile_error_skip(/*No matching BEGIN*/192);
[4be360f]676 } else {
[fa42426]677 compile_error_skip(/*END with no matching BEGIN in this file*/22);
[4be360f]678 }
679 return;
680 }
681
[613028c]682 begin_survey = pcs->begin_survey;
[647407d]683
[4c07c51]684 SVX_ASSERT(pcsParent);
[647407d]685 free_settings(pcs);
[5f1e194]686 pcs = pcsParent;
[d1b1380]687
[84c60fc]688 /* note need to read using root *before* BEGIN */
[613028c]689 survey = read_prefix(PFX_SURVEY|PFX_OPT|PFX_ALLOW_ROOT);
690 if (survey != begin_survey) {
691 if (survey) {
692 if (!begin_survey) {
693 /* TRANSLATORS: Used when a BEGIN command has no survey, but the
694 * END command does, e.g.:
[736f7df]695 *
696 * *begin
697 * 1 2 10.00 178 -01
698 * *end entrance <--[Message given here] */
[613028c]699 compile_error_skip(-/*Matching BEGIN command has no survey name*/36);
[647407d]700 } else {
[613028c]701 /* TRANSLATORS: *BEGIN <survey> and *END <survey> should have the
702 * same <survey> if it’s given at all */
703 compile_error_skip(-/*Survey name doesn’t match BEGIN*/193);
[647407d]704 }
[a420b49]705 } else {
[613028c]706 /* TRANSLATORS: Used when a BEGIN command has a survey name, but the
707 * END command omits it, e.g.:
708 *
709 * *begin entrance
710 * 1 2 10.00 178 -01
711 * *end <--[Message given here] */
712 compile_warning(-/*Survey name omitted from END*/194);
[a420b49]713 }
[5f1e194]714 }
715}
[d1b1380]716
[dfb4240]717static void
718cmd_entrance(void)
719{
[c458cf7]720 prefix *pfx = read_prefix(PFX_STATION);
[dfb4240]721 pfx->sflags |= BIT(SFLAGS_ENTRANCE);
722}
723
[56db37f]724static const prefix * first_fix_name = NULL;
725static const char * first_fix_filename;
726static unsigned first_fix_line;
[216ada0]727
[a420b49]728static void
[eb18f4d]729cmd_fix(void)
[a420b49]730{
[5f1e194]731 prefix *fix_name;
[153f951]732 node *stn = NULL;
[216ada0]733 static prefix *name_omit_already = NULL;
[31699b54]734 static const char * name_omit_already_filename = NULL;
735 static unsigned int name_omit_already_line;
[a420b49]736 real x, y, z;
[21c226e]737 int nx, ny, nz;
[c80bd34]738 filepos fp;
[5f1e194]739
[c458cf7]740 fix_name = read_prefix(PFX_STATION|PFX_ALLOW_ROOT);
[dfb4240]741 fix_name->sflags |= BIT(SFLAGS_FIXED);
[5f1e194]742
[c80bd34]743 get_pos(&fp);
[647407d]744 get_token();
[c80bd34]745 if (strcmp(ucbuffer, "REFERENCE") == 0) {
[f15cde77]746 /* suppress "unused fixed point" warnings for this station */
747 fix_name->sflags |= BIT(SFLAGS_USED);
[c80bd34]748 } else {
749 if (*ucbuffer) set_pos(&fp);
[647407d]750 }
[c80bd34]751
[21c226e]752 x = read_numeric(fTrue, &nx);
[2aa2f3f]753 if (x == HUGE_REAL) {
754 /* If the end of the line isn't blank, read a number after all to
755 * get a more helpful error message */
[21c226e]756 if (!isEol(ch) && !isComm(ch)) x = read_numeric(fFalse, &nx);
[2aa2f3f]757 }
[5f1e194]758 if (x == HUGE_REAL) {
[a4f1d96]759 if (pcs->proj || proj_out) {
760 compile_error_skip(/*Coordinates can't be omitted when coordinate system has been specified*/439);
761 return;
762 }
763
[6727d64]764 if (fix_name == name_omit_already) {
765 compile_warning(/*Same station fixed twice with no coordinates*/61);
[5f1e194]766 return;
767 }
[6727d64]768
[736f7df]769 /* TRANSLATORS: " *fix a " gives this message: */
[a420b49]770 compile_warning(/*FIX command with no coordinates - fixing at (0,0,0)*/54);
[6727d64]771
772 if (name_omit_already) {
773 /* TRANSLATORS: Emitted after second and subsequent "FIX command with
774 * no coordinates - fixing at (0,0,0)" warnings.
775 */
776 compile_error_at(name_omit_already_filename,
777 name_omit_already_line,
778 /*Already had FIX command with no coordinates for station “%s”*/441,
779 sprint_prefix(name_omit_already));
780 } else {
781 name_omit_already = fix_name;
782 name_omit_already_filename = file.filename;
783 name_omit_already_line = file.line;
784 }
785
[5f1e194]786 x = y = z = (real)0.0;
787 } else {
[647407d]788 real sdx;
[21c226e]789 y = read_numeric(fFalse, &ny);
790 z = read_numeric(fFalse, &nz);
791 sdx = read_numeric(fTrue, NULL);
[647407d]792 if (sdx != HUGE_REAL) {
793 real sdy, sdz;
[c80bd34]794 real cxy = 0, cyz = 0, czx = 0;
[21c226e]795 sdy = read_numeric(fTrue, NULL);
[647407d]796 if (sdy == HUGE_REAL) {
797 /* only one variance given */
798 sdy = sdz = sdx;
799 } else {
[21c226e]800 sdz = read_numeric(fTrue, NULL);
[647407d]801 if (sdz == HUGE_REAL) {
802 /* two variances given - horizontal & vertical */
803 sdz = sdy;
804 sdy = sdx;
[c80bd34]805 } else {
[21c226e]806 cxy = read_numeric(fTrue, NULL);
[c80bd34]807 if (cxy != HUGE_REAL) {
808 /* covariances given */
[21c226e]809 cyz = read_numeric(fFalse, NULL);
810 czx = read_numeric(fFalse, NULL);
[c2211a5]811 } else {
812 cxy = 0;
[c80bd34]813 }
[647407d]814 }
815 }
[153f951]816 stn = StnFromPfx(fix_name);
[647407d]817 if (!fixed(stn)) {
818 node *fixpt = osnew(node);
[be97baf]819 prefix *name;
820 name = osnew(prefix);
821 name->pos = osnew(pos);
[ff6cfe1]822 name->ident = NULL;
[6adb88c]823 name->shape = 0;
[647407d]824 fixpt->name = name;
825 name->stn = fixpt;
826 name->up = NULL;
[27b8b59]827 if (TSTBIT(pcs->infer, INFER_EXPORTS)) {
[c00c74a9]828 name->min_export = USHRT_MAX;
829 } else {
830 name->min_export = 0;
831 }
832 name->max_export = 0;
[95c3272]833 name->sflags = 0;
[cb3d1e2]834 add_stn_to_list(&stnlist, fixpt);
[647407d]835 POS(fixpt, 0) = x;
836 POS(fixpt, 1) = y;
837 POS(fixpt, 2) = z;
838 fix(fixpt);
[4c45ef1]839 fixpt->leg[0] = fixpt->leg[1] = fixpt->leg[2] = NULL;
[647407d]840 addfakeleg(fixpt, stn, 0, 0, 0,
841 sdx * sdx, sdy * sdy, sdz * sdz
842#ifndef NO_COVARIANCES
[c80bd34]843 , cxy, cyz, czx
[647407d]844#endif
845 );
846 }
[cb3d1e2]847 return;
[647407d]848 }
[c092d72]849
850 if (pcs->proj && proj_out) {
851 int r = pj_transform(pcs->proj, proj_out, 1, 1, &x, &y, &z);
852 if (r != 0) {
853 compile_error(/*Failed to convert coordinates*/436);
854 /* FIXME: report pj_strerrno(r) */
855 printf("[%s]\n", pj_strerrno(r));
856 }
857 } else if (pcs->proj) {
[29d1883f]858 compile_error(/*The input projection is set but the output projection isn't*/437);
[c092d72]859 } else if (proj_out) {
[29d1883f]860 compile_error(/*The output projection is set but the input projection isn't*/438);
[c092d72]861 }
[5f1e194]862 }
863
[56db37f]864 if (!first_fix_name) {
865 /* We track if we've fixed a station yet, and if so what the name of the
866 * first fix was, so that we can issue an error if the output coordinate
867 * system is set after fixing a station. */
868 first_fix_name = fix_name;
869 first_fix_filename = file.filename;
870 first_fix_line = file.line;
871 }
872
873 stn = StnFromPfx(fix_name);
[5f1e194]874 if (!fixed(stn)) {
875 POS(stn, 0) = x;
876 POS(stn, 1) = y;
877 POS(stn, 2) = z;
878 fix(stn);
879 return;
880 }
[d1b1380]881
[5f1e194]882 if (x != POS(stn, 0) || y != POS(stn, 1) || z != POS(stn, 2)) {
[a420b49]883 compile_error(/*Station already fixed or equated to a fixed point*/46);
[5f1e194]884 return;
885 }
[736f7df]886 /* TRANSLATORS: *fix a 1 2 3 / *fix a 1 2 3 */
[a420b49]887 compile_warning(/*Station already fixed at the same coordinates*/55);
[d1b1380]888}
889
[5c3c61a]890static void
891cmd_flags(void)
892{
[82919e07]893 static const sztok flagtab[] = {
[5c3c61a]894 {"DUPLICATE", FLAGS_DUPLICATE },
895 {"NOT", FLAGS_NOT },
[95c3272]896 {"SPLAY", FLAGS_SPLAY },
[5c3c61a]897 {"SURFACE", FLAGS_SURFACE },
[a4ae909]898 {NULL, FLAGS_UNKNOWN }
[5c3c61a]899 };
900 bool fNot = fFalse;
[9881759]901 bool fEmpty = fTrue;
[5c3c61a]902 while (1) {
903 int flag;
904 get_token();
[62bb4d3]905 /* If buffer is empty, it could mean end of line, or maybe
906 * some non-letter junk which is better reported later */
[9881759]907 if (!buffer[0]) break;
[62bb4d3]908
[9881759]909 fEmpty = fFalse;
[5c3c61a]910 flag = match_tok(flagtab, TABSIZE(flagtab));
911 /* treat the second NOT in "NOT NOT" as an unknown flag */
912 if (flag == FLAGS_UNKNOWN || (fNot && flag == FLAGS_NOT)) {
[da96015]913 file.lpos += strlen(buffer);
914 compile_error(-/*FLAG “%s” unknown*/68, buffer);
[0804fbe]915 /* Recover from “*FLAGS NOT BOGUS SURFACE” by ignoring "NOT BOGUS" */
[62bb4d3]916 fNot = fFalse;
917 } else if (flag == FLAGS_NOT) {
[421b7d2]918 fNot = fTrue;
[5c3c61a]919 } else if (fNot) {
[421b7d2]920 pcs->flags &= ~BIT(flag);
921 fNot = fFalse;
[5c3c61a]922 } else {
[421b7d2]923 pcs->flags |= BIT(flag);
[5c3c61a]924 }
925 }
[9881759]926
927 if (fNot) {
[da96015]928 file.lpos += strlen(buffer);
929 compile_error(-/*Expecting “DUPLICATE”, “SPLAY”, or “SURFACE”*/188);
[9881759]930 } else if (fEmpty) {
[da96015]931 file.lpos += strlen(buffer);
932 compile_error(-/*Expecting “NOT”, “DUPLICATE”, “SPLAY”, or “SURFACE”*/189);
[9881759]933 }
[5c3c61a]934}
935
[a420b49]936static void
[eb18f4d]937cmd_equate(void)
[a420b49]938{
[5f1e194]939 prefix *name1, *name2;
940 bool fOnlyOneStn = fTrue; /* to trap eg *equate entrance.6 */
941
[c458cf7]942 name1 = read_prefix(PFX_STATION|PFX_ALLOW_ROOT|PFX_SUSPECT_TYPO);
[5f1e194]943 while (fTrue) {
944 name2 = name1;
[c458cf7]945 name1 = read_prefix(PFX_STATION|PFX_ALLOW_ROOT|PFX_SUSPECT_TYPO|PFX_OPT);
[5f1e194]946 if (name1 == NULL) {
[11f9067]947 if (fOnlyOneStn) {
[736f7df]948 /* TRANSLATORS: EQUATE is a command name, so shouldn’t be
[0b8c321]949 * translated.
950 *
951 * Here "station" is a survey station, not a train station.
952 */
[da96015]953 compile_error_skip(-/*Only one station in EQUATE command*/33);
[11f9067]954 }
[5f1e194]955 return;
[d1b1380]956 }
[cb3d1e2]957
[be374fc]958 process_equate(name1, name2);
[5f1e194]959 fOnlyOneStn = fFalse;
960 }
[d1b1380]961}
962
[84c60fc]963static void
964report_missing_export(prefix *pfx, int depth)
965{
966 char *s;
[93e3492]967 const char *p;
968 prefix *survey = pfx;
[84c60fc]969 int i;
970 for (i = depth + 1; i; i--) {
971 survey = survey->up;
[4c07c51]972 SVX_ASSERT(survey);
[84c60fc]973 }
974 s = osstrdup(sprint_prefix(survey));
[93e3492]975 p = sprint_prefix(pfx);
[84c60fc]976 if (survey->filename) {
[93e3492]977 /* TRANSLATORS: A station must be exported out of each level it is in, so
978 * this would give "Station “\outer.inner.1” not exported from survey
979 * “\outer”)":
980 *
981 * *equate entrance outer.inner.1
982 * *begin outer
983 * *begin inner
984 * *export 1
985 * 1 2 1.23 045 -6
986 * *end inner
[0b8c321]987 * *end outer
988 *
989 * Here "survey" is a "cave map" rather than list of questions - it should be
990 * translated to the terminology that cavers using the language would use.
991 */
[93e3492]992 compile_error_pfx(survey,
993 /*Station “%s” not exported from survey “%s”*/26, p, s);
994 } else {
995 compile_error(/*Station “%s” not exported from survey “%s”*/26, p, s);
[84c60fc]996 }
997 osfree(s);
998}
999
[fb2e93c]1000static void
[d1878c51]1001cmd_export(void)
[fb2e93c]1002{
[d1878c51]1003 prefix *pfx;
1004
1005 fExportUsed = fTrue;
[c458cf7]1006 pfx = read_prefix(PFX_STATION);
[932f7e9]1007 do {
[fb2e93c]1008 int depth = 0;
1009 {
[421b7d2]1010 prefix *p = pfx;
[fb2e93c]1011 while (p != NULL && p != pcs->Prefix) {
1012 depth++;
1013 p = p->up;
1014 }
[84c60fc]1015 /* Something like: *export \foo, but we've excluded use of root */
[4c07c51]1016 SVX_ASSERT(p);
[fb2e93c]1017 }
[84c60fc]1018 /* *export \ or similar bogus stuff */
[4c07c51]1019 SVX_ASSERT(depth);
[932f7e9]1020#if 0
1021 printf("C min %d max %d depth %d pfx %s\n",
1022 pfx->min_export, pfx->max_export, depth, sprint_prefix(pfx));
1023#endif
1024 if (pfx->min_export == 0) {
[421b7d2]1025 /* not encountered *export for this name before */
1026 if (pfx->max_export > depth) report_missing_export(pfx, depth);
1027 pfx->min_export = pfx->max_export = depth;
[c00c74a9]1028 } else if (pfx->min_export != USHRT_MAX) {
1029 /* FIXME: what to do if a station is marked for inferred exports
1030 * but is then explicitly exported? Currently we just ignore the
1031 * explicit export... */
[421b7d2]1032 if (pfx->min_export - 1 > depth) {
[84c60fc]1033 report_missing_export(pfx, depth);
[421b7d2]1034 } else if (pfx->min_export - 1 < depth) {
[736f7df]1035 /* TRANSLATORS: Here "station" is a survey station, not a train station.
1036 *
1037 * Exporting a station twice gives this error:
1038 *
1039 * *begin example
1040 * *export 1
1041 * *export 1
1042 * 1 2 1.24 045 -6
1043 * *end example */
[0804fbe]1044 compile_error(/*Station “%s” already exported*/66,
[932f7e9]1045 sprint_prefix(pfx));
[421b7d2]1046 }
1047 pfx->min_export = depth;
[fb2e93c]1048 }
[c458cf7]1049 pfx = read_prefix(PFX_STATION|PFX_OPT);
[932f7e9]1050 } while (pfx);
[fb2e93c]1051}
1052
[a420b49]1053static void
[eb18f4d]1054cmd_data(void)
[a420b49]1055{
[82919e07]1056 static const sztok dtab[] = {
[9b5d785]1057 {"ALTITUDE", Dz },
[5b7c1b7]1058 {"BACKBEARING", BackComp },
1059 {"BACKCLINO", BackClino }, /* alternative name */
1060 {"BACKCOMPASS", BackComp }, /* alternative name */
1061 {"BACKGRADIENT", BackClino },
[5f1e194]1062 {"BEARING", Comp },
[ee05463]1063 {"CEILING", Up }, /* alternative name */
[a4ae909]1064 {"CLINO", Clino }, /* alternative name */
[a420b49]1065 {"COMPASS", Comp }, /* alternative name */
[a4ae909]1066 {"COUNT", Count }, /* FrCount&ToCount in multiline */
1067 {"DEPTH", Depth }, /* FrDepth&ToDepth in multiline */
[6114207]1068 {"DEPTHCHANGE", DepthChange },
[421b7d2]1069 {"DIRECTION", Dir },
[ee05463]1070 {"DOWN", Down },
[647407d]1071 {"DX", Dx },
1072 {"DY", Dy },
1073 {"DZ", Dz },
[9b5d785]1074 {"EASTING", Dx },
[ee05463]1075 {"FLOOR", Down }, /* alternative name */
[a4ae909]1076 {"FROM", Fr },
[647407d]1077 {"FROMCOUNT", FrCount },
[5f1e194]1078 {"FROMDEPTH", FrDepth },
1079 {"GRADIENT", Clino },
1080 {"IGNORE", Ignore },
1081 {"IGNOREALL", IgnoreAll },
[ee05463]1082 {"LEFT", Left },
[5f1e194]1083 {"LENGTH", Tape },
[a0027a2]1084 {"NEWLINE", Newline },
[9b5d785]1085 {"NORTHING", Dy },
[ee05463]1086 {"RIGHT", Right },
[421b7d2]1087 {"STATION", Station }, /* Fr&To in multiline */
[a4ae909]1088 {"TAPE", Tape }, /* alternative name */
1089 {"TO", To },
[647407d]1090 {"TOCOUNT", ToCount },
[5f1e194]1091 {"TODEPTH", ToDepth },
[ee05463]1092 {"UP", Up },
[a4ae909]1093 {NULL, End }
[5f1e194]1094 };
1095
[107b8bd]1096#define MASK_stns BIT(Fr) | BIT(To) | BIT(Station)
1097#define MASK_tape BIT(Tape) | BIT(FrCount) | BIT(ToCount) | BIT(Count)
1098#define MASK_dpth BIT(FrDepth) | BIT(ToDepth) | BIT(Depth) | BIT(DepthChange)
[5b7c1b7]1099#define MASK_comp BIT(Comp) | BIT(BackComp)
1100#define MASK_clin BIT(Clino) | BIT(BackClino)
[5f1e194]1101
[5b7c1b7]1102#define MASK_NORMAL MASK_stns | BIT(Dir) | MASK_tape | MASK_comp | MASK_clin
1103#define MASK_DIVING MASK_stns | BIT(Dir) | MASK_tape | MASK_comp | MASK_dpth
[107b8bd]1104#define MASK_CARTESIAN MASK_stns | BIT(Dx) | BIT(Dy) | BIT(Dz)
[5b7c1b7]1105#define MASK_CYLPOLAR MASK_stns | BIT(Dir) | MASK_tape | MASK_comp | MASK_dpth
[ee05463]1106#define MASK_PASSAGE BIT(Station) | BIT(Left) | BIT(Right) | BIT(Up) | BIT(Down)
[107b8bd]1107#define MASK_NOSURVEY MASK_stns
[421b7d2]1108
[5f1e194]1109 /* readings which may be given for each style */
[6114207]1110 static const unsigned long mask[] = {
[200a12c]1111 MASK_NORMAL, MASK_DIVING, MASK_CARTESIAN, MASK_CYLPOLAR, MASK_NOSURVEY,
1112 MASK_PASSAGE
[5f1e194]1113 };
1114
1115 /* readings which may be omitted for each style */
[6114207]1116 static const unsigned long mask_optional[] = {
[5b7c1b7]1117 BIT(Dir) | BIT(Clino) | BIT(BackClino),
[276286d]1118 BIT(Dir),
[86f26e2]1119 0,
[54c4612]1120 BIT(Dir),
[200a12c]1121 0,
1122 0 /* BIT(Left) | BIT(Right) | BIT(Up) | BIT(Down), */
[5f1e194]1123 };
1124
[6114207]1125 /* all valid readings */
1126 static const unsigned long mask_all[] = {
1127 MASK_NORMAL | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End),
1128 MASK_DIVING | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End),
1129 MASK_CARTESIAN | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End),
1130 MASK_CYLPOLAR | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End),
[200a12c]1131 MASK_NOSURVEY | BIT(Ignore) | BIT(IgnoreAll) | BIT(End),
1132 MASK_PASSAGE | BIT(Ignore) | BIT(IgnoreAll) | BIT(End)
[6114207]1133 };
[54c4612]1134#define STYLE_DEFAULT -2
1135#define STYLE_UNKNOWN -1
1136
[82919e07]1137 static const sztok styletab[] = {
[647407d]1138 {"CARTESIAN", STYLE_CARTESIAN },
[54c4612]1139 {"CYLPOLAR", STYLE_CYLPOLAR },
[a420b49]1140 {"DEFAULT", STYLE_DEFAULT },
[5f1e194]1141 {"DIVING", STYLE_DIVING },
1142 {"NORMAL", STYLE_NORMAL },
[647407d]1143 {"NOSURVEY", STYLE_NOSURVEY },
[ee05463]1144 {"PASSAGE", STYLE_PASSAGE },
[107b8bd]1145 {"TOPOFIL", STYLE_NORMAL },
[a4ae909]1146 {NULL, STYLE_UNKNOWN }
[5f1e194]1147 };
1148
[ee3a4ed]1149#define m_multi (BIT(Station) | BIT(Count) | BIT(Depth))
1150
[5f1e194]1151 int style, k = 0, kMac;
[0395657]1152 reading *new_order, d;
[6114207]1153 unsigned long mUsed = 0;
[1b34062]1154 char *style_name;
[5f1e194]1155
[50f6901]1156 /* after a bad *data command ignore survey data until the next
1157 * *data command to avoid an avalanche of errors */
[107b8bd]1158 pcs->style = STYLE_IGNORE;
[289b7a8]1159
[5f1e194]1160 kMac = 6; /* minimum for NORMAL style */
[0395657]1161 new_order = osmalloc(kMac * sizeof(reading));
[5f1e194]1162
[a420b49]1163 get_token();
[5f1e194]1164 style = match_tok(styletab, TABSIZE(styletab));
1165
[a420b49]1166 if (style == STYLE_DEFAULT) {
1167 default_style(pcs);
1168 return;
1169 }
1170
[5f1e194]1171 if (style == STYLE_UNKNOWN) {
[da96015]1172 file.lpos += strlen(buffer);
[736f7df]1173 /* TRANSLATORS: e.g. trying to refer to an invalid FNORD data style */
[da96015]1174 compile_error_skip(-/*Data style “%s” unknown*/65, buffer);
[5f1e194]1175 return;
1176 }
[a420b49]1177
[5f1e194]1178 skipblanks();
[d6d3576]1179#ifndef NO_DEPRECATED
1180 /* Olde syntax had optional field for survey grade, so allow an omit
1181 * but issue a warning about it */
1182 if (isOmit(ch)) {
1183 static int data_depr_count = 0;
1184 if (data_depr_count < 5) {
[da96015]1185 file.lpos += strlen(buffer);
1186 compile_warning(-/*“*data %s %c …” is deprecated - use “*data %s …” instead*/104,
[d6d3576]1187 buffer, ch, buffer);
1188 if (++data_depr_count == 5)
[c40038a]1189 compile_warning(/*Further uses of this deprecated feature will not be reported*/95);
[d6d3576]1190 }
1191 nextch();
1192 }
1193#endif
[d1b1380]1194
[1b34062]1195 style_name = osstrdup(buffer);
[421b7d2]1196 do {
[c80bd34]1197 filepos fp;
1198 get_pos(&fp);
[a420b49]1199 get_token();
[5f1e194]1200 d = match_tok(dtab, TABSIZE(dtab));
[90bb053f]1201 /* only token allowed after IGNOREALL is NEWLINE */
1202 if (k && new_order[k - 1] == IgnoreAll && d != Newline) {
[c80bd34]1203 set_pos(&fp);
[90bb053f]1204 break;
1205 }
[0395657]1206 /* Note: an unknown token is reported as trailing garbage */
[6114207]1207 if (!TSTBIT(mask_all[style], d)) {
[da96015]1208 file.lpos += strlen(buffer);
[736f7df]1209 /* TRANSLATORS: a data "style" is something like NORMAL, DIVING, etc.
1210 * a "reading" is one of FROM, TO, TAPE, COMPASS, CLINO for NORMAL
1211 * neither style nor reading is a keyword in the program This error
1212 * complains about a depth gauge reading in normal style, for example
1213 */
[da96015]1214 compile_error_skip(-/*Reading “%s” not allowed in data style “%s”*/63,
[1b34062]1215 buffer, style_name);
1216 osfree(style_name);
[a6d094f]1217 osfree(new_order);
[ee3a4ed]1218 return;
1219 }
[6114207]1220 if (TSTBIT(mUsed, Newline) && TSTBIT(m_multi, d)) {
[da96015]1221 file.lpos += strlen(buffer);
[736f7df]1222 /* TRANSLATORS: caused by e.g.
1223 *
1224 * *data diving station newline depth tape compass
1225 *
1226 * ("depth" needs to occur before "newline"). */
[da96015]1227 compile_error_skip(-/*Reading “%s” must occur before NEWLINE*/225, buffer);
[ee3a4ed]1228 osfree(style_name);
[a6d094f]1229 osfree(new_order);
[5f1e194]1230 return;
1231 }
[0395657]1232 /* Check for duplicates unless it's a special reading:
[ee3a4ed]1233 * IGNOREALL,IGNORE (duplicates allowed) ; END (not possible)
[5f1e194]1234 */
[ee3a4ed]1235 if (!((BIT(Ignore) | BIT(End) | BIT(IgnoreAll)) & BIT(d))) {
[95c3272]1236 if (TSTBIT(mUsed, d)) {
[da96015]1237 file.lpos += strlen(buffer);
[736f7df]1238 /* TRANSLATORS: complains about a situation like trying to define
1239 * two from stations per leg */
[da96015]1240 compile_error_skip(-/*Duplicate reading “%s”*/67, buffer);
[90bb053f]1241 osfree(style_name);
[a6d094f]1242 osfree(new_order);
[90bb053f]1243 return;
[62bb4d3]1244 } else {
[5b7c1b7]1245 /* Check for previously listed readings which are incompatible
1246 * with this one - e.g. Count vs FrCount */
[90bb053f]1247 bool fBad = fFalse;
1248 switch (d) {
1249 case Station:
1250 if (mUsed & (BIT(Fr) | BIT(To))) fBad = fTrue;
1251 break;
1252 case Fr: case To:
1253 if (TSTBIT(mUsed, Station)) fBad = fTrue;
1254 break;
1255 case Count:
[107b8bd]1256 if (mUsed & (BIT(FrCount) | BIT(ToCount) | BIT(Tape)))
1257 fBad = fTrue;
[90bb053f]1258 break;
1259 case FrCount: case ToCount:
[107b8bd]1260 if (mUsed & (BIT(Count) | BIT(Tape)))
1261 fBad = fTrue;
[90bb053f]1262 break;
1263 case Depth:
[6114207]1264 if (mUsed & (BIT(FrDepth) | BIT(ToDepth) | BIT(DepthChange)))
[a186573]1265 fBad = fTrue;
[90bb053f]1266 break;
1267 case FrDepth: case ToDepth:
[6114207]1268 if (mUsed & (BIT(Depth) | BIT(DepthChange))) fBad = fTrue;
[a186573]1269 break;
[6114207]1270 case DepthChange:
[a186573]1271 if (mUsed & (BIT(FrDepth) | BIT(ToDepth) | BIT(Depth)))
1272 fBad = fTrue;
[90bb053f]1273 break;
[ee3a4ed]1274 case Newline:
1275 if (mUsed & ~m_multi) {
[da96015]1276 file.lpos += strlen(buffer);
[736f7df]1277 /* TRANSLATORS: e.g.
1278 *
1279 * *data normal from to tape newline compass clino */
[da96015]1280 compile_error_skip(-/*NEWLINE can only be preceded by STATION, DEPTH, and COUNT*/226);
[ee3a4ed]1281 osfree(style_name);
[a6d094f]1282 osfree(new_order);
[ee3a4ed]1283 return;
1284 }
[6114207]1285 if (k == 0) {
[da96015]1286 file.lpos += strlen(buffer);
[736f7df]1287 /* TRANSLATORS: error from:
1288 *
1289 * *data normal newline from to tape compass clino */
[da96015]1290 compile_error_skip(-/*NEWLINE can’t be the first reading*/222);
[6114207]1291 osfree(style_name);
1292 osfree(new_order);
1293 return;
1294 }
[ee3a4ed]1295 break;
[90bb053f]1296 default: /* avoid compiler warnings about unhandled enums */
1297 break;
[421b7d2]1298 }
[90bb053f]1299 if (fBad) {
[46cb98f]1300 /* Not entirely happy with phrasing this... */
[da96015]1301 file.lpos += strlen(buffer);
[36efb03]1302 /* TRANSLATORS: This is an error from the *DATA command. It
1303 * means that a reading (which will appear where %s is isn't
1304 * valid as the list of readings has already included the same
1305 * reading, or an equivalent one (e.g. you can't have both
1306 * DEPTH and DEPTHCHANGE together). */
[da96015]1307 compile_error_skip(-/*Reading “%s” duplicates previous reading(s)*/77,
[46cb98f]1308 buffer);
[90bb053f]1309 osfree(style_name);
[a6d094f]1310 osfree(new_order);
[90bb053f]1311 return;
1312 }
[62bb4d3]1313 mUsed |= BIT(d); /* used to catch duplicates */
[a420b49]1314 }
[d1b1380]1315 }
[90bb053f]1316 if (k && new_order[k - 1] == IgnoreAll) {
[4c07c51]1317 SVX_ASSERT(d == Newline);
[90bb053f]1318 k--;
1319 d = IgnoreAllAndNewLine;
1320 }
[a420b49]1321 if (k >= kMac) {
1322 kMac = kMac * 2;
[0395657]1323 new_order = osrealloc(new_order, kMac * sizeof(reading));
[a420b49]1324 }
1325 new_order[k++] = d;
[90bb053f]1326 } while (d != End);
1327
[6114207]1328 if (k >= 2 && new_order[k - 2] == Newline) {
[da96015]1329 file.lpos += strlen(buffer);
[736f7df]1330 /* TRANSLATORS: error from:
1331 *
1332 * *data normal from to tape compass clino newline */
[da96015]1333 compile_error_skip(-/*NEWLINE can’t be the last reading*/223);
[6114207]1334 osfree(style_name);
1335 osfree(new_order);
1336 return;
1337 }
[421b7d2]1338
[6114207]1339 if (style == STYLE_NOSURVEY) {
1340 if (TSTBIT(mUsed, Station)) {
1341 if (k >= kMac) {
1342 kMac = kMac * 2;
1343 new_order = osrealloc(new_order, kMac * sizeof(reading));
1344 }
1345 new_order[k - 1] = Newline;
1346 new_order[k++] = End;
1347 }
[ee05463]1348 } else if (style == STYLE_PASSAGE) {
1349 /* Station doesn't mean "multiline" for STYLE_PASSAGE. */
[6114207]1350 } else if (!TSTBIT(mUsed, Newline) && (m_multi & mUsed)) {
[736f7df]1351 /* TRANSLATORS: Error given by something like:
1352 *
[6114207]1353 * *data normal station tape compass clino
[736f7df]1354 *
1355 * ("station" signifies interleaved data). */
[fa42426]1356 compile_error_skip(/*Interleaved readings, but no NEWLINE*/224);
[6114207]1357 osfree(style_name);
1358 osfree(new_order);
1359 return;
1360 }
1361
[a6d094f]1362#if 0
1363 printf("mUsed = 0x%x\n", mUsed);
1364#endif
[90bb053f]1365
[5b7c1b7]1366 /* Check the supplied readings form a sufficient set. */
[ee05463]1367 if (style != STYLE_PASSAGE) {
1368 if (mUsed & (BIT(Fr) | BIT(To)))
1369 mUsed |= BIT(Station);
1370 else if (TSTBIT(mUsed, Station))
1371 mUsed |= BIT(Fr) | BIT(To);
1372 }
[a186573]1373
[5b7c1b7]1374 if (mUsed & (BIT(Comp) | BIT(BackComp)))
1375 mUsed |= BIT(Comp) | BIT(BackComp);
1376
1377 if (mUsed & (BIT(Clino) | BIT(BackClino)))
1378 mUsed |= BIT(Clino) | BIT(BackClino);
1379
[a186573]1380 if (mUsed & (BIT(FrDepth) | BIT(ToDepth)))
[6114207]1381 mUsed |= BIT(Depth) | BIT(DepthChange);
[a186573]1382 else if (TSTBIT(mUsed, Depth))
[6114207]1383 mUsed |= BIT(FrDepth) | BIT(ToDepth) | BIT(DepthChange);
1384 else if (TSTBIT(mUsed, DepthChange))
[a186573]1385 mUsed |= BIT(FrDepth) | BIT(ToDepth) | BIT(Depth);
1386
1387 if (mUsed & (BIT(FrCount) | BIT(ToCount)))
[107b8bd]1388 mUsed |= BIT(Count) | BIT(Tape);
[a186573]1389 else if (TSTBIT(mUsed, Count))
[107b8bd]1390 mUsed |= BIT(FrCount) | BIT(ToCount) | BIT(Tape);
1391 else if (TSTBIT(mUsed, Tape))
1392 mUsed |= BIT(FrCount) | BIT(ToCount) | BIT(Count);
[90bb053f]1393
[a6d094f]1394#if 0
1395 printf("mUsed = 0x%x, opt = 0x%x, mask = 0x%x\n", mUsed,
1396 mask_optional[style], mask[style]);
1397#endif
[cb3d1e2]1398
[6114207]1399 if (((mUsed &~ BIT(Newline)) | mask_optional[style]) != mask[style]) {
1400 /* Test should only fail with too few bits set, not too many */
[4c07c51]1401 SVX_ASSERT((((mUsed &~ BIT(Newline)) | mask_optional[style])
[6114207]1402 &~ mask[style]) == 0);
[736f7df]1403 /* TRANSLATORS: i.e. not enough readings for the style. */
[0804fbe]1404 compile_error_skip(/*Too few readings for data style “%s”*/64, style_name);
[1b34062]1405 osfree(style_name);
[a6d094f]1406 osfree(new_order);
[a420b49]1407 return;
1408 }
[d1b1380]1409
[a420b49]1410 /* don't free default ordering or ordering used by parent */
1411 if (pcs->ordering != default_order &&
1412 !(pcs->next && pcs->next->ordering == pcs->ordering))
1413 osfree(pcs->ordering);
[cb3d1e2]1414
[107b8bd]1415 pcs->style = style;
[a420b49]1416 pcs->ordering = new_order;
[1b34062]1417
[421b7d2]1418 osfree(style_name);
[ee05463]1419
1420 if (style == STYLE_PASSAGE) {
1421 lrudlist * new_psg = osnew(lrudlist);
1422 new_psg->tube = NULL;
1423 new_psg->next = model;
1424 model = new_psg;
1425 next_lrud = &(new_psg->tube);
1426 }
[d1b1380]1427}
1428
[a420b49]1429static void
[eb18f4d]1430cmd_units(void)
[a420b49]1431{
[5f1e194]1432 int units, quantity;
[6114207]1433 unsigned long qmask;
1434 unsigned long m; /* mask with bit x set to indicate quantity x specified */
[5f1e194]1435 real factor;
[a420b49]1436
[699bf50]1437 qmask = get_qlist(BIT(Q_POS)|BIT(Q_PLUMB)|BIT(Q_LEVEL));
1438
[647407d]1439 if (!qmask) return;
[a420b49]1440 if (qmask == BIT(Q_DEFAULT)) {
1441 default_units(pcs);
[5f1e194]1442 return;
1443 }
[a420b49]1444
[21c226e]1445 factor = read_numeric(fTrue, NULL);
[fa42426]1446 if (factor == 0.0) {
[736f7df]1447 /* TRANSLATORS: error message given by "*units tape 0 feet" - it’s
1448 * meaningless to say your tape is marked in "0 feet" (but you might
1449 * measure distance by counting knots on a diving line, and tie them
1450 * every "2 feet"). */
[da96015]1451 compile_error_skip(-/**UNITS factor must be non-zero*/200);
[fa42426]1452 return;
1453 }
[a420b49]1454
[fa42426]1455 units = get_units(qmask, fTrue);
[647407d]1456 if (units == UNITS_NULL) return;
[fa42426]1457 if (TSTBIT(qmask, Q_GRADIENT))
1458 pcs->f_clino_percent = (units == UNITS_PERCENT);
1459 if (TSTBIT(qmask, Q_BACKGRADIENT))
1460 pcs->f_backclino_percent = (units == UNITS_PERCENT);
1461
[6eb50ab]1462 if (factor == HUGE_REAL) {
1463 factor = factor_tab[units];
1464 } else {
1465 factor *= factor_tab[units];
1466 }
[5f1e194]1467
1468 for (quantity = 0, m = BIT(quantity); m <= qmask; quantity++, m <<= 1)
1469 if (qmask & m) pcs->units[quantity] = factor;
[d1b1380]1470}
1471
[a420b49]1472static void
[eb18f4d]1473cmd_calibrate(void)
[a420b49]1474{
[d6d3576]1475 real sc, z;
[67508f0]1476 unsigned long qmask, m;
[a420b49]1477 int quantity;
[46cb98f]1478
[da96015]1479 qmask = get_qlist(BIT(Q_POS)|BIT(Q_PLUMB)|BIT(Q_LEVEL));
[46cb98f]1480 if (!qmask) return; /* error already reported */
1481
[a420b49]1482 if (qmask == BIT(Q_DEFAULT)) {
1483 default_calib(pcs);
1484 return;
1485 }
[46cb98f]1486
[a420b49]1487 if (((qmask & LEN_QMASK)) && ((qmask & ANG_QMASK))) {
[0b8c321]1488 /* TRANSLATORS: e.g.
1489 *
1490 * *calibrate tape compass 1 1
1491 */
[ee7511a]1492 compile_error_skip(/*Can’t calibrate angular and length quantities together*/227);
[647407d]1493 return;
[a420b49]1494 }
[46cb98f]1495
[21c226e]1496 z = read_numeric(fFalse, NULL);
1497 sc = read_numeric(fTrue, NULL);
[a420b49]1498 if (sc == HUGE_REAL) sc = (real)1.0;
[647407d]1499 /* check for declination scale */
[3a33d12]1500 /* perhaps "*calibrate declination XXX" should be "*declination XXX" ? */
[95c3272]1501 if (TSTBIT(qmask, Q_DECLINATION) && sc != 1.0) {
[736f7df]1502 /* TRANSLATORS: DECLINATION is a built-in keyword, so best not to
1503 * translate */
[da96015]1504 compile_error_skip(-/*Scale factor must be 1.0 for DECLINATION*/40);
[647407d]1505 return;
1506 }
[4b14118]1507 if (sc == 0.0) {
[736f7df]1508 /* TRANSLATORS: If the scale factor for an instrument is zero, then any
1509 * reading would be mapped to zero, which doesn't make sense. */
[da96015]1510 compile_error_skip(-/*Scale factor must be non-zero*/391);
[4b14118]1511 return;
1512 }
[647407d]1513 for (quantity = 0, m = BIT(quantity); m <= qmask; quantity++, m <<= 1) {
[a420b49]1514 if (qmask & m) {
[647407d]1515 pcs->z[quantity] = pcs->units[quantity] * z;
[a420b49]1516 pcs->sc[quantity] = sc;
1517 }
[647407d]1518 }
[d1b1380]1519}
1520
[7f1ab95]1521#ifndef NO_DEPRECATED
[a420b49]1522static void
[eb18f4d]1523cmd_default(void)
[a420b49]1524{
[82919e07]1525 static const sztok defaulttab[] = {
[c0563da]1526 { "CALIBRATE", CMD_CALIBRATE },
1527 { "DATA", CMD_DATA },
1528 { "UNITS", CMD_UNITS },
[a4ae909]1529 { NULL, CMD_NULL }
[c0563da]1530 };
[c86cc71]1531 static int default_depr_count = 0;
1532
1533 if (default_depr_count < 5) {
[736f7df]1534 /* TRANSLATORS: If you're unsure what "deprecated" means, see:
1535 * http://en.wikipedia.org/wiki/Deprecation */
[da96015]1536 compile_warning(-/**DEFAULT is deprecated - use *CALIBRATE/DATA/SD/UNITS with argument DEFAULT instead*/20);
[c86cc71]1537 if (++default_depr_count == 5)
[c40038a]1538 compile_warning(/*Further uses of this deprecated feature will not be reported*/95);
[c86cc71]1539 }
1540
[cb3d1e2]1541 get_token();
[c0563da]1542 switch (match_tok(defaulttab, TABSIZE(defaulttab))) {
1543 case CMD_CALIBRATE:
[5f1e194]1544 default_calib(pcs);
[c0563da]1545 break;
1546 case CMD_DATA:
[5f1e194]1547 default_style(pcs);
1548 default_grade(pcs);
[c0563da]1549 break;
1550 case CMD_UNITS:
[5f1e194]1551 default_units(pcs);
[c0563da]1552 break;
1553 default:
[da96015]1554 file.lpos += strlen(buffer);
1555 compile_error_skip(-/*Unknown setting “%s”*/41, buffer);
[5f1e194]1556 }
[d1b1380]1557}
[7f1ab95]1558#endif
[d1b1380]1559
[a420b49]1560static void
[eb18f4d]1561cmd_include(void)
[a420b49]1562{
[f97076a]1563 char *pth, *fnm = NULL;
[a420b49]1564 int fnm_len;
[a882316]1565#ifndef NO_DEPRECATED
[5f1e194]1566 prefix *root_store;
[a882316]1567#endif
[5f1e194]1568 int ch_store;
1569
[f97076a]1570 pth = path_from_fnm(file.filename);
1571
[a420b49]1572 read_string(&fnm, &fnm_len);
[d1b1380]1573
[a882316]1574#ifndef NO_DEPRECATED
1575 /* Since *begin / *end nesting cannot cross file boundaries we only
1576 * need to preserve the prefix if the deprecated *prefix command
1577 * can be used */
[5f1e194]1578 root_store = root;
1579 root = pcs->Prefix; /* Root for include file is current prefix */
[a882316]1580#endif
[5f1e194]1581 ch_store = ch;
[cb3d1e2]1582
[5f1e194]1583 data_file(pth, fnm);
[a420b49]1584
[a882316]1585#ifndef NO_DEPRECATED
[5f1e194]1586 root = root_store; /* and restore root */
[647407d]1587#endif
[5f1e194]1588 ch = ch_store;
[d1b1380]1589
[a420b49]1590 s_free(&fnm);
[f97076a]1591 osfree(pth);
[d1b1380]1592}
1593
[a420b49]1594static void
[eb18f4d]1595cmd_sd(void)
[a420b49]1596{
[5f1e194]1597 real sd, variance;
1598 int units;
[67508f0]1599 unsigned long qmask, m;
[5f1e194]1600 int quantity;
[b14f44f]1601 qmask = get_qlist(BIT(Q_DECLINATION));
[46cb98f]1602 if (!qmask) return; /* no quantities found - error already reported */
1603
[a420b49]1604 if (qmask == BIT(Q_DEFAULT)) {
1605 default_grade(pcs);
[5f1e194]1606 return;
1607 }
[21c226e]1608 sd = read_numeric(fFalse, NULL);
[d6d3576]1609 if (sd <= (real)0.0) {
[da96015]1610 compile_error_skip(-/*Standard deviation must be positive*/48);
[d6d3576]1611 return;
[5f1e194]1612 }
[fa42426]1613 units = get_units(qmask, fFalse);
[647407d]1614 if (units == UNITS_NULL) return;
[5f1e194]1615
1616 sd *= factor_tab[units];
1617 variance = sqrd(sd);
1618
1619 for (quantity = 0, m = BIT(quantity); m <= qmask; quantity++, m <<= 1)
1620 if (qmask & m) pcs->Var[quantity] = variance;
[d1b1380]1621}
[5f1e194]1622
[a420b49]1623static void
[eb18f4d]1624cmd_title(void)
[a420b49]1625{
[1925d66]1626 if (!fExplicitTitle && pcs->Prefix == root) {
1627 /* If we don't have an explicit title yet, and we're currently in the
1628 * root prefix, use this title explicitly. */
[a420b49]1629 fExplicitTitle = fTrue;
1630 read_string(&survey_title, &survey_title_len);
1631 } else {
1632 /* parse and throw away this title (but still check rest of line) */
1633 char *s = NULL;
1634 int len;
1635 read_string(&s, &len);
1636 s_free(&s);
1637 }
1638}
1639
[82919e07]1640static const sztok case_tab[] = {
[a420b49]1641 {"PRESERVE", OFF},
[c57e9da]1642 {"TOLOWER", LOWER},
1643 {"TOUPPER", UPPER},
[a420b49]1644 {NULL, -1}
1645};
[cb3d1e2]1646
[a420b49]1647static void
[eb18f4d]1648cmd_case(void)
[a420b49]1649{
1650 int setting;
1651 get_token();
1652 setting = match_tok(case_tab, TABSIZE(case_tab));
1653 if (setting != -1) {
1654 pcs->Case = setting;
1655 } else {
[da96015]1656 file.lpos += strlen(buffer);
1657 compile_error_skip(-/*Found “%s”, expecting “PRESERVE”, “TOUPPER”, or “TOLOWER”*/10,
1658 buffer);
[a420b49]1659 }
1660}
1661
[abd0310]1662typedef enum {
1663 CS_NONE = -1,
1664 CS_CUSTOM,
1665 CS_EPSG,
1666 CS_ESRI,
1667 CS_EUR,
1668 CS_IJTSK,
1669 CS_JTSK,
1670 CS_LAT,
1671 CS_LOCAL,
1672 CS_LONG,
1673 CS_OSGB,
1674 CS_S_MERC,
1675 CS_UTM
1676} cs_class;
1677
[82919e07]1678static const sztok cs_tab[] = {
[abd0310]1679 {"CUSTOM", CS_CUSTOM},
1680 {"EPSG", CS_EPSG}, /* EPSG:<number> */
1681 {"ESRI", CS_ESRI}, /* ESRI:<number> */
1682 {"EUR", CS_EUR}, /* EUR79Z30 */
1683 {"IJTSK", CS_IJTSK}, /* IJTSK or IJTSK03 */
1684 {"JTSK", CS_JTSK}, /* JTSK or JTSK03 */
1685 {"LAT", CS_LAT}, /* LAT-LONG */
1686 {"LOCAL", CS_LOCAL},
1687 {"LONG", CS_LONG}, /* LONG-LAT */
1688 {"OSGB", CS_OSGB}, /* OSGB:<H, N, O, S or T><A-Z except I> */
1689 {"S", CS_S_MERC}, /* S-MERC */
1690 {"UTM", CS_UTM}, /* UTM<zone><N or S or nothing> */
1691 {NULL, CS_NONE}
1692};
1693
1694static void
1695cmd_cs(void)
1696{
[e755560]1697 char * proj_str = NULL;
[abd0310]1698 int proj_str_len;
1699 cs_class cs;
1700 int cs_sub = INT_MIN;
1701 filepos fp;
[c092d72]1702 bool output = fFalse;
1703 enum { YES, NO, MAYBE } ok_for_output = YES;
[56db37f]1704 static bool had_cs = fFalse;
1705
1706 if (!had_cs) {
1707 had_cs = fTrue;
1708 if (first_fix_name) {
1709 compile_error_at(first_fix_filename, first_fix_line,
1710 /*Station “%s” fixed before CS command first used*/442,
1711 sprint_prefix(first_fix_name));
1712 }
1713 }
[abd0310]1714
1715 get_pos(&fp);
[acf82004]1716 /* Note get_token() only accepts letters - it'll stop at digits so "UTM12"
[abd0310]1717 * will give token "UTM". */
1718 get_token();
[c092d72]1719 if (strcmp(ucbuffer, "OUT") == 0) {
1720 output = fTrue;
1721 get_token();
1722 }
[abd0310]1723 cs = match_tok(cs_tab, TABSIZE(cs_tab));
1724 switch (cs) {
1725 case CS_NONE:
1726 break;
1727 case CS_CUSTOM:
[c092d72]1728 ok_for_output = MAYBE;
[abd0310]1729 read_string(&proj_str, &proj_str_len);
[c092d72]1730 cs_sub = 0;
[abd0310]1731 break;
1732 case CS_EPSG: case CS_ESRI:
[c092d72]1733 ok_for_output = MAYBE;
[abd0310]1734 if (ch == ':' && isdigit(nextch())) {
1735 unsigned n = read_uint();
[ddd24f28]1736 if (n < 1000000) {
[abd0310]1737 cs_sub = (int)n;
1738 }
1739 }
1740 break;
1741 case CS_EUR:
1742 if (isdigit(ch) &&
1743 read_uint() == 79 &&
1744 (ch == 'Z' || ch == 'z') &&
1745 isdigit(nextch()) &&
1746 read_uint() == 30) {
1747 cs_sub = 7930;
1748 }
1749 break;
[c092d72]1750 case CS_JTSK:
1751 ok_for_output = NO;
1752 /* FALLTHRU */
1753 case CS_IJTSK:
[abd0310]1754 if (ch == '0') {
1755 if (nextch() == '3') {
1756 nextch();
1757 cs_sub = 3;
1758 }
1759 } else {
1760 cs_sub = 0;
1761 }
1762 break;
1763 case CS_LAT: case CS_LONG:
[c092d72]1764 ok_for_output = NO;
[abd0310]1765 if (ch == '-') {
1766 nextch();
1767 get_token_no_blanks();
1768 cs_class cs2 = match_tok(cs_tab, TABSIZE(cs_tab));
[f21d797]1769 if ((cs ^ cs2) == (CS_LAT ^ CS_LONG)) {
[abd0310]1770 cs_sub = 0;
1771 }
1772 }
1773 break;
1774 case CS_LOCAL:
1775 cs_sub = 0;
1776 break;
1777 case CS_OSGB:
1778 if (ch == ':') {
1779 int uch1 = toupper(nextch());
1780 if (strchr("HNOST", uch1)) {
1781 int uch2 = toupper(nextch());
1782 if (uch2 >= 'A' && uch2 <= 'Z' && uch2 != 'I') {
[a4cd4eea]1783 int x, y;
[abd0310]1784 nextch();
[a4cd4eea]1785 if (uch1 > 'I') --uch1;
1786 uch1 -= 'A';
[abd0310]1787 if (uch2 > 'I') --uch2;
[a4cd4eea]1788 uch2 -= 'A';
1789 x = uch1 % 5;
1790 y = uch1 / 5;
1791 x = (x * 5) + uch2 % 5;
1792 y = (y * 5) + uch2 / 5;
1793 cs_sub = y * 25 + x;
[abd0310]1794 }
1795 }
1796 }
1797 break;
1798 case CS_S_MERC:
1799 if (ch == '-') {
[2076d59]1800 nextch();
[abd0310]1801 get_token_no_blanks();
1802 if (strcmp(ucbuffer, "MERC") == 0) {
1803 cs_sub = 0;
1804 }
1805 }
1806 break;
1807 case CS_UTM:
1808 if (isdigit(ch)) {
1809 unsigned n = read_uint();
1810 if (n >= 1 && n <= 60) {
1811 int uch = toupper(ch);
1812 cs_sub = (int)n;
1813 if (uch == 'S') {
1814 nextch();
1815 cs_sub = -cs_sub;
1816 } else if (uch == 'N') {
1817 nextch();
1818 }
1819 }
1820 }
1821 break;
1822 }
1823 if (cs_sub == INT_MIN || isalnum(ch)) {
1824 set_pos(&fp);
1825 compile_error_skip(-/*Unknown coordinate system*/434);
[c092d72]1826 return;
[abd0310]1827 }
1828 /* Actually handle the cs */
[c092d72]1829 switch (cs) {
[5d36f97]1830 case CS_NONE:
1831 break;
1832 case CS_CUSTOM:
1833 /* proj_str already set */
1834 break;
[ddd24f28]1835 case CS_EPSG:
1836 proj_str = osmalloc(32);
1837 sprintf(proj_str, "+init=epsg:%d +no_defs", cs_sub);
1838 break;
1839 case CS_ESRI:
1840 proj_str = osmalloc(32);
1841 sprintf(proj_str, "+init=esri:%d +no_defs", cs_sub);
1842 break;
1843 case CS_EUR:
1844 proj_str = osstrdup("+proj=utm +zone=30 +ellps=intl +towgs84=-86,-98,-119,0,0,0,0 +no_defs");
1845 break;
[5598e2c]1846 case CS_IJTSK:
1847 if (cs_sub == 0)
1848 proj_str = osstrdup("+proj=krovak +ellps=bessel +towgs84=570.8285,85.6769,462.842,4.9984,1.5867,5.2611,3.5623 +no_defs");
1849 else
1850 proj_str = osstrdup("+proj=krovak +ellps=bessel +towgs84=485.021,169.465,483.839,7.786342,4.397554,4.102655,0 +no_defs");
1851 break;
[10af28e]1852 case CS_JTSK:
1853 if (cs_sub == 0)
1854 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");
1855 else
1856 proj_str = osstrdup("+proj=krovak +czech +ellps=bessel +towgs84=485.021,169.465,483.839,7.786342,4.397554,4.102655,0 +no_defs");
1857 break;
[16734b2]1858 case CS_LAT:
1859 /* FIXME: Requires PROJ >= 4.8.0 for +axis, and the SDs will be
[10af28e]1860 * misapplied, so we may want to swap ourselves. Also, while
1861 * therion supports lat-long, I'm not totally convinced that it is
1862 * sensible to do so - people often say "lat-long", but probably
1863 * don't think that that's actually "Northing, Easting". This
1864 * seems like it'll result in people accidentally getting X and Y
1865 * swapped in their fixed points...
1866 */
[5d36f97]1867#if 0
[cb0a137]1868 proj_str = osstrdup("+proj=longlat +ellps=WGS84 +datum=WGS84 +axis=neu +no_defs");
[5d36f97]1869#endif
[16734b2]1870 break;
[10af28e]1871 case CS_LOCAL:
1872 /* FIXME: Is it useful to be able to explicitly specify this? */
1873 break;
[16734b2]1874 case CS_LONG:
[cb0a137]1875 proj_str = osstrdup("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs");
[16734b2]1876 break;
[a4cd4eea]1877 case CS_OSGB: {
1878 int x = 14 - (cs_sub % 25);
1879 int y = (cs_sub / 25) - 20;
1880 proj_str = osmalloc(160);
1881 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);
1882 break;
1883 }
[2076d59]1884 case CS_S_MERC:
[7ed1380]1885 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");
[2076d59]1886 break;
[c092d72]1887 case CS_UTM:
[cb0a137]1888 proj_str = osmalloc(74);
[c092d72]1889 if (cs_sub > 0) {
[cb0a137]1890 sprintf(proj_str, "+proj=utm +ellps=WGS84 +datum=WGS84 +units=m +zone=%d +no_defs", cs_sub);
[c092d72]1891 } else {
[cb0a137]1892 sprintf(proj_str, "+proj=utm +ellps=WGS84 +datum=WGS84 +units=m +zone=%d +south +no_defs", -cs_sub);
[c092d72]1893 }
1894 break;
1895 }
1896
[10af28e]1897 if (!proj_str) {
1898 /* printf("CS %d:%d\n", (int)cs, cs_sub); */
1899 set_pos(&fp);
1900 compile_error_skip(-/*Unknown coordinate system*/434);
1901 return;
1902 }
1903
[c092d72]1904 if (output) {
1905 if (ok_for_output == NO) {
1906 set_pos(&fp);
1907 compile_error_skip(-/*Coordinate system unsuitable for output*/435);
1908 return;
1909 }
1910
1911 /* If the output projection is already set, we still need to create the
1912 * projection object for a custom projection, so we can report errors.
1913 * But if the string is identical, we know it's valid.
1914 */
1915 if (!proj_out ||
1916 (ok_for_output == MAYBE && strcmp(proj_str, proj_str_out) != 0)) {
1917 projPJ pj = pj_init_plus(proj_str);
1918 if (!pj) {
1919 set_pos(&fp);
[5b107ee]1920 compile_error_skip(-/*Invalid coordinate system: %s*/443,
1921 pj_strerrno(pj_errno));
[c092d72]1922 return;
1923 }
1924 if (ok_for_output == MAYBE && pj_is_latlong(pj)) {
1925 compile_error_skip(-/*Coordinate system unsuitable for output*/435);
1926 return;
1927 }
1928 if (proj_out) {
1929 pj_free(pj);
1930 osfree(proj_str);
1931 } else {
1932 proj_out = pj;
1933 proj_str_out = proj_str;
1934 }
1935 }
1936 } else {
1937 projPJ pj;
1938 if (proj_str_out && strcmp(proj_str, proj_str_out) == 0) {
1939 /* Same as the current output projection. */
1940 pj = proj_out;
1941 } else {
1942 pj = pj_init_plus(proj_str);
1943 if (!pj) {
1944 set_pos(&fp);
[5b107ee]1945 compile_error_skip(-/*Invalid coordinate system: %s*/443,
1946 pj_strerrno(pj_errno));
[c092d72]1947 return;
1948 }
1949 }
1950
1951 /* Free proj if not used by parent, or as the output projection. */
1952 settings * p = pcs;
1953 if (p->proj && (!p->next || p->proj != p->next->proj))
1954 if (p->proj != proj_out)
1955 pj_free(p->proj);
1956 p->proj = pj;
1957 }
[abd0310]1958}
1959
[82919e07]1960static const sztok infer_tab[] = {
[27b8b59]1961 { "EQUATES", INFER_EQUATES },
1962 { "EXPORTS", INFER_EXPORTS },
1963 { "PLUMBS", INFER_PLUMBS },
1964#if 0 /* FIXME */
1965 { "SUBSURVEYS", INFER_SUBSURVEYS },
1966#endif
1967 { NULL, INFER_NULL }
[a420b49]1968};
[cb3d1e2]1969
[82919e07]1970static const sztok onoff_tab[] = {
[27b8b59]1971 { "OFF", 0 },
1972 { "ON", 1 },
1973 { NULL, -1 }
[a420b49]1974};
[cb3d1e2]1975
[a420b49]1976static void
[eb18f4d]1977cmd_infer(void)
[a420b49]1978{
[27b8b59]1979 infer_what setting;
[a420b49]1980 int on;
1981 get_token();
1982 setting = match_tok(infer_tab, TABSIZE(infer_tab));
[27b8b59]1983 if (setting == INFER_NULL) {
[da96015]1984 file.lpos += strlen(buffer);
1985 compile_error_skip(-/*Found “%s”, expecting “EQUATES”, “EXPORTS”, or “PLUMBS”*/31, buffer);
[647407d]1986 return;
[a420b49]1987 }
1988 get_token();
1989 on = match_tok(onoff_tab, TABSIZE(onoff_tab));
1990 if (on == -1) {
[da96015]1991 file.lpos += strlen(buffer);
1992 compile_error_skip(-/*Found “%s”, expecting “ON” or “OFF”*/32, buffer);
[647407d]1993 return;
[a420b49]1994 }
[cb3d1e2]1995
[27b8b59]1996 if (on) {
1997 pcs->infer |= BIT(setting);
1998 if (setting == INFER_EXPORTS) fExportUsed = fTrue;
1999 } else {
2000 pcs->infer &= ~BIT(setting);
[a420b49]2001 }
2002}
2003
2004static void
[eb18f4d]2005cmd_truncate(void)
[a420b49]2006{
[647407d]2007 unsigned int truncate_at = 0; /* default is no truncation */
[c80bd34]2008 filepos fp;
2009
2010 get_pos(&fp);
2011
[647407d]2012 get_token();
2013 if (strcmp(ucbuffer, "OFF") != 0) {
[c80bd34]2014 if (*ucbuffer) set_pos(&fp);
[647407d]2015 truncate_at = read_uint();
2016 }
2017 /* for backward compatibility, "*truncate 0" means "*truncate off" */
2018 pcs->Truncate = (truncate_at == 0) ? INT_MAX : truncate_at;
2019}
2020
2021static void
[eb18f4d]2022cmd_require(void)
[647407d]2023{
[e2546c0]2024 const unsigned int version[] = {COMMAVERSION};
2025 const unsigned int *ver = version;
[c80bd34]2026 filepos fp;
2027
[69c920d]2028 skipblanks();
[c80bd34]2029 get_pos(&fp);
[647407d]2030 while (1) {
[e2546c0]2031 int diff = *ver++ - read_uint();
2032 if (diff > 0) break;
2033 if (diff < 0) {
[647407d]2034 size_t i, len;
2035 char *v;
[c80bd34]2036 filepos fp_tmp;
2037
[647407d]2038 /* find end of version number */
2039 while (isdigit(ch) || ch == '.') nextch();
[c80bd34]2040 get_pos(&fp_tmp);
2041 len = (size_t)(fp_tmp.offset - fp.offset);
[647407d]2042 v = osmalloc(len + 1);
[c80bd34]2043 set_pos(&fp);
[647407d]2044 for (i = 0; i < len; i++) {
2045 v[i] = ch;
[69c920d]2046 nextch();
2047 }
[647407d]2048 v[i] = '\0';
[736f7df]2049 /* TRANSLATORS: Feel free to translate as "or newer" instead of "or
2050 * greater" if that gives a more natural translation. It's
2051 * technically not quite right when there are parallel active release
2052 * series (e.g. Survex 1.0.40 was released *after* 1.2.0), but this
[0b8c321]2053 * seems unlikely to confuse users. "Survex" is the name of the
2054 * software, so should not be translated.
2055 *
2056 * Here "survey" is a "cave map" rather than list of questions - it should be
2057 * translated to the terminology that cavers using the language would use.
2058 */
[647407d]2059 fatalerror_in_file(file.filename, file.line, /*Survex version %s or greater required to process this survey data.*/2, v);
2060 }
2061 if (ch != '.') break;
2062 nextch();
[e2546c0]2063 if (!isdigit(ch) || ver == version + sizeof(version)) break;
[69c920d]2064 }
[647407d]2065 /* skip rest of version number */
2066 while (isdigit(ch) || ch == '.') nextch();
[a420b49]2067}
2068
[b5a3219]2069/* allocate new meta_data if need be */
2070void
2071copy_on_write_meta(settings *s)
2072{
2073 if (!s->meta || s->meta->ref_count != 0) {
2074 meta_data * meta_new = osnew(meta_data);
2075 if (!s->meta) {
[1ee204e]2076 meta_new->days1 = meta_new->days2 = -1;
[b5a3219]2077 } else {
2078 *meta_new = *(s->meta);
2079 }
2080 meta_new->ref_count = 0;
2081 s->meta = meta_new;
2082 }
2083}
2084
[950a829]2085static void
2086cmd_date(void)
[421b7d2]2087{
[e0c7cd1]2088 int year, month, day;
[1ee204e]2089 int days1, days2;
2090 bool implicit_range = fFalse;
[e0c7cd1]2091
2092 read_date(&year, &month, &day);
[1ee204e]2093 days1 = days_since_1900(year, month ? month : 1, day ? day : 1);
2094
2095 if (days1 > current_days_since_1900) {
[da96015]2096 compile_warning(-/*Date is in the future!*/80);
[e0c7cd1]2097 }
2098
2099 skipblanks();
2100 if (ch == '-') {
2101 nextch();
2102 read_date(&year, &month, &day);
2103 } else {
[1ee204e]2104 if (month && day) {
2105 days2 = days1;
2106 goto read;
2107 }
2108 implicit_range = fTrue;
2109 }
2110
2111 if (month == 0) month = 12;
2112 if (day == 0) day = last_day(year, month);
2113 days2 = days_since_1900(year, month, day);
2114
2115 if (!implicit_range && days2 > current_days_since_1900) {
[da96015]2116 compile_warning(-/*Date is in the future!*/80);
[e0c7cd1]2117 }
[1ee204e]2118
2119 if (days2 < days1) {
[da96015]2120 compile_error(-/*End of date range is before the start*/81);
[1ee204e]2121 }
[e0c7cd1]2122
[1ee204e]2123read:
[e0c7cd1]2124 copy_on_write_meta(pcs);
[1ee204e]2125 pcs->meta->days1 = days1;
2126 pcs->meta->days2 = days2;
[950a829]2127}
2128
[3aafcee]2129typedef void (*cmd_fn)(void);
2130
[82919e07]2131static const cmd_fn cmd_funcs[] = {
[dcbcae0]2132 cmd_alias,
[3aafcee]2133 cmd_begin,
2134 cmd_calibrate,
2135 cmd_case,
2136 skipline, /*cmd_copyright,*/
[abd0310]2137 cmd_cs,
[3aafcee]2138 cmd_data,
[950a829]2139 cmd_date,
[7f1ab95]2140#ifndef NO_DEPRECATED
[3aafcee]2141 cmd_default,
[7f1ab95]2142#endif
[3aafcee]2143 cmd_end,
2144 cmd_entrance,
2145 cmd_equate,
2146 cmd_export,
2147 cmd_fix,
2148 cmd_flags,
2149 cmd_include,
2150 cmd_infer,
2151 skipline, /*cmd_instrument,*/
[7f1ab95]2152#ifndef NO_DEPRECATED
[3aafcee]2153 cmd_prefix,
[7f1ab95]2154#endif
[3aafcee]2155 cmd_require,
2156 cmd_sd,
2157 cmd_set,
2158 solve_network,
2159 skipline, /*cmd_team,*/
2160 cmd_title,
2161 cmd_truncate,
2162 cmd_units
2163};
2164
[a420b49]2165extern void
2166handle_command(void)
2167{
[932f7e9]2168 int cmdtok;
[a420b49]2169 get_token();
[932f7e9]2170 cmdtok = match_tok(cmd_tab, TABSIZE(cmd_tab));
2171
[3aafcee]2172 if (cmdtok < 0 || cmdtok >= (int)(sizeof(cmd_funcs) / sizeof(cmd_fn))) {
[da96015]2173 file.lpos += strlen(buffer);
2174 compile_error_skip(-/*Unknown command “%s”*/12, buffer);
[3aafcee]2175 return;
[932f7e9]2176 }
2177
2178 switch (cmdtok) {
[3aafcee]2179 case CMD_EXPORT:
2180 if (!f_export_ok)
[736f7df]2181 /* TRANSLATORS: The *EXPORT command is only valid just after *BEGIN
2182 * <SURVEY>, so this would generate this error:
2183 *
2184 * *begin fred
2185 * 1 2 1.23 045 -6
2186 * *export 2
2187 * *end fred */
[0804fbe]2188 compile_error(/**EXPORT must immediately follow “*BEGIN <SURVEY>”*/57);
[3aafcee]2189 break;
2190 case CMD_COPYRIGHT:
2191 case CMD_DATE:
2192 case CMD_INSTRUMENT:
2193 case CMD_TEAM:
2194 case CMD_TITLE:
[4dcd3af]2195 /* These can occur between *begin and *export */
[421b7d2]2196 break;
[a420b49]2197 default:
[4dcd3af]2198 /* NB: additional handling for "*begin <survey>" in cmd_begin */
[3aafcee]2199 f_export_ok = fFalse;
2200 break;
[647407d]2201 }
[421b7d2]2202
[3aafcee]2203 cmd_funcs[cmdtok]();
[5f1e194]2204}
Note: See TracBrowser for help on using the repository browser.