source: git/src/commands.c @ 85c0078

RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-data
Last change on this file since 85c0078 was 85c0078, checked in by Olly Betts <olly@…>, 9 years ago

doc/TODO.htm,lib/,src/,tests/: In cavern, use the currently set units
when outputting measurements in warnings, errors, and the stats at
the end of the run.

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