source: git/src/commands.c @ f21d797

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

src/commands.c: Tweak code so one side of comparison is a constant.

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