source: git/src/commands.c @ cd5b089

RELEASE/1.2debug-cidebug-ci-sanitisersfaster-cavernlogstereowalls-datawalls-data-hanging-as-warning
Last change on this file since cd5b089 was dcbcae0, checked in by Olly Betts <olly@…>, 11 years ago

doc/manual.sgml,lib/survex.pot,src/,tests/Makefile.am,
tests/cavern.tst: Add more anonymous station types - a double
separator ('..' by default) is an anoymous wall point at the end
of an implicit splay (c.f. '.' being a non-wall point), and
a triple separator ('...' by default) is an anoymous point (with
nothing special about the leg). New *alias command allows '-' to be
mapped to '..' to match pocket topo conventions. 3d file format now
support a 'WALL' station flag.

  • Property mode set to 100644
File size: 44.1 KB
Line 
1/* commands.c
2 * Code for directives
3 * Copyright (C) 1991-2003,2004,2005,2006,2010,2011,2012,2013 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 "cavern.h"
29#include "commands.h"
30#include "datain.h"
31#include "date.h"
32#include "debug.h"
33#include "filename.h"
34#include "message.h"
35#include "netbits.h"
36#include "netskel.h"
37#include "out.h"
38#include "readval.h"
39#include "str.h"
40
41static void
42default_grade(settings *s)
43{
44   /* Values correspond to those in bcra5.svx */
45   s->Var[Q_POS] = (real)sqrd(0.05);
46   s->Var[Q_LENGTH] = (real)sqrd(0.05);
47   s->Var[Q_COUNT] = (real)sqrd(0.05);
48   s->Var[Q_DX] = s->Var[Q_DY] = s->Var[Q_DZ] = (real)sqrd(0.05);
49   s->Var[Q_BEARING] = (real)sqrd(rad(0.5));
50   s->Var[Q_GRADIENT] = (real)sqrd(rad(0.5));
51   s->Var[Q_BACKBEARING] = (real)sqrd(rad(0.5));
52   s->Var[Q_BACKGRADIENT] = (real)sqrd(rad(0.5));
53   /* SD of plumbed legs (0.25 degrees?) */
54   s->Var[Q_PLUMB] = (real)sqrd(rad(0.25));
55   /* SD of level legs (0.25 degrees?) */
56   s->Var[Q_LEVEL] = (real)sqrd(rad(0.25));
57   s->Var[Q_DEPTH] = (real)sqrd(0.05);
58}
59
60static void
61default_truncate(settings *s)
62{
63   s->Truncate = INT_MAX;
64}
65
66static void
67default_case(settings *s)
68{
69   s->Case = LOWER;
70}
71
72static reading default_order[] = { Fr, To, Tape, Comp, Clino, End };
73
74static void
75default_style(settings *s)
76{
77   s->style = STYLE_NORMAL;
78   s->ordering = default_order;
79   s->dash_for_anon_wall_station = fFalse;
80}
81
82static void
83default_prefix(settings *s)
84{
85   s->Prefix = root;
86}
87
88static void
89default_translate(settings *s)
90{
91   int i;
92   short *t;
93   if (s->next && s->next->Translate == s->Translate) {
94      t = ((short*)osmalloc(ossizeof(short) * 257)) + 1;
95      memcpy(t - 1, s->Translate - 1, sizeof(short) * 257);
96      s->Translate = t;
97   }
98/*  SVX_ASSERT(EOF==-1);*/ /* important, since we rely on this */
99   t = s->Translate;
100   memset(t - 1, 0, sizeof(short) * 257);
101   for (i = '0'; i <= '9'; i++) t[i] = SPECIAL_NAMES;
102   for (i = 'A'; i <= 'Z'; i++) t[i] = SPECIAL_NAMES;
103   for (i = 'a'; i <= 'z'; i++) t[i] = SPECIAL_NAMES;
104
105   t['\t'] |= SPECIAL_BLANK;
106   t[' '] |= SPECIAL_BLANK;
107   t[','] |= SPECIAL_BLANK;
108   t[';'] |= SPECIAL_COMMENT;
109   t['\032'] |= SPECIAL_EOL; /* Ctrl-Z, so olde DOS text files are handled ok */
110   t[EOF] |= SPECIAL_EOL;
111   t['\n'] |= SPECIAL_EOL;
112   t['\r'] |= SPECIAL_EOL;
113   t['*'] |= SPECIAL_KEYWORD;
114   t['-'] |= SPECIAL_OMIT;
115   t['\\'] |= SPECIAL_ROOT;
116   t['.'] |= SPECIAL_SEPARATOR;
117   t['_'] |= SPECIAL_NAMES;
118   t['-'] |= SPECIAL_NAMES; /* Added in 0.97 prerelease 4 */
119   t['.'] |= SPECIAL_DECIMAL;
120   t['-'] |= SPECIAL_MINUS;
121   t['+'] |= SPECIAL_PLUS;
122#if 0 /* FIXME */
123   t['{'] |= SPECIAL_OPEN;
124   t['}'] |= SPECIAL_CLOSE;
125#endif
126}
127
128void
129default_units(settings *s)
130{
131   int quantity;
132   for (quantity = 0; quantity < Q_MAC; quantity++) {
133      if (TSTBIT(ANG_QMASK, quantity))
134         s->units[quantity] = (real)(M_PI / 180.0); /* degrees */
135      else
136         s->units[quantity] = (real)1.0; /* metres */
137   }
138   s->f_clino_percent = s->f_backclino_percent = fFalse;
139}
140
141void
142default_calib(settings *s)
143{
144   int quantity;
145   for (quantity = 0; quantity < Q_MAC; quantity++) {
146      s->z[quantity] = (real)0.0;
147      s->sc[quantity] = (real)1.0;
148   }
149}
150
151static void
152default_flags(settings *s)
153{
154   s->flags = 0;
155}
156
157extern void
158default_all(settings *s)
159{
160   default_truncate(s);
161   s->infer = 0;
162   default_case(s);
163   default_style(s);
164   default_prefix(s);
165   default_translate(s);
166   default_grade(s);
167   default_units(s);
168   default_calib(s);
169   default_flags(s);
170}
171
172char *buffer = NULL;
173static int buf_len;
174
175static char *ucbuffer = NULL;
176
177/* read token */
178extern void
179get_token(void)
180{
181   int i = -1;
182
183   s_zero(&buffer);
184   osfree(ucbuffer);
185   skipblanks();
186   while (isalpha(ch)) {
187      s_catchar(&buffer, &buf_len, (char)ch);
188      nextch();
189   }
190
191   if (!buffer) s_catchar(&buffer, &buf_len, '\0');
192
193   ucbuffer = osmalloc(buf_len);
194   do {
195      i++;
196      ucbuffer[i] = toupper(buffer[i]);
197   } while (buffer[i]);
198#if 0
199   printf("get_token() got “%s”\n", buffer);
200#endif
201}
202
203/* read word */
204static void
205get_word(void)
206{
207   s_zero(&buffer);
208   skipblanks();
209   while (!isBlank(ch) && !isEol(ch)) {
210      s_catchar(&buffer, &buf_len, (char)ch);
211      nextch();
212   }
213
214   if (!buffer) s_catchar(&buffer, &buf_len, '\0');
215#if 0
216   printf("get_word() got “%s”\n", buffer);
217#endif
218}
219
220/* match_tok() now uses binary chop
221 * tab argument should be alphabetically sorted (ascending)
222 */
223extern int
224match_tok(const sztok *tab, int tab_size)
225{
226   int a = 0, b = tab_size - 1, c;
227   int r;
228   assert(tab_size > 0); /* catch empty table */
229/*  printf("[%d,%d]",a,b); */
230   while (a <= b) {
231      c = (unsigned)(a + b) / 2;
232/*     printf(" %d",c); */
233      r = strcmp(tab[c].sz, ucbuffer);
234      if (r == 0) return tab[c].tok; /* match */
235      if (r < 0)
236         a = c + 1;
237      else
238         b = c - 1;
239   }
240   return tab[tab_size].tok; /* no match */
241}
242
243typedef enum {
244   CMD_NULL = -1, CMD_ALIAS, CMD_BEGIN, CMD_CALIBRATE, CMD_CASE, CMD_COPYRIGHT,
245   CMD_DATA, CMD_DATE, CMD_DEFAULT, CMD_END, CMD_ENTRANCE, CMD_EQUATE,
246   CMD_EXPORT, CMD_FIX, CMD_FLAGS, CMD_INCLUDE, CMD_INFER, CMD_INSTRUMENT,
247   CMD_PREFIX, CMD_REQUIRE, CMD_SD, CMD_SET, CMD_SOLVE,
248   CMD_TEAM, CMD_TITLE, CMD_TRUNCATE, CMD_UNITS
249} cmds;
250
251static sztok cmd_tab[] = {
252     {"ALIAS",     CMD_ALIAS},
253     {"BEGIN",     CMD_BEGIN},
254     {"CALIBRATE", CMD_CALIBRATE},
255     {"CASE",      CMD_CASE},
256     {"COPYRIGHT", CMD_COPYRIGHT},
257     {"DATA",      CMD_DATA},
258     {"DATE",      CMD_DATE},
259#ifndef NO_DEPRECATED
260     {"DEFAULT",   CMD_DEFAULT},
261#endif
262     {"END",       CMD_END},
263     {"ENTRANCE",  CMD_ENTRANCE},
264     {"EQUATE",    CMD_EQUATE},
265     {"EXPORT",    CMD_EXPORT},
266     {"FIX",       CMD_FIX},
267     {"FLAGS",     CMD_FLAGS},
268     {"INCLUDE",   CMD_INCLUDE},
269     {"INFER",     CMD_INFER},
270     {"INSTRUMENT",CMD_INSTRUMENT},
271#ifndef NO_DEPRECATED
272     {"PREFIX",    CMD_PREFIX},
273#endif
274     {"REQUIRE",   CMD_REQUIRE},
275     {"SD",        CMD_SD},
276     {"SET",       CMD_SET},
277     {"SOLVE",     CMD_SOLVE},
278     {"TEAM",      CMD_TEAM},
279     {"TITLE",     CMD_TITLE},
280     {"TRUNCATE",  CMD_TRUNCATE},
281     {"UNITS",     CMD_UNITS},
282     {NULL,        CMD_NULL}
283};
284
285/* masks for units which are length and angles respectively */
286#define LEN_UMASK (BIT(UNITS_METRES) | BIT(UNITS_FEET) | BIT(UNITS_YARDS))
287#define ANG_UMASK (BIT(UNITS_DEGS) | BIT(UNITS_GRADS) | BIT(UNITS_MINUTES))
288
289/* ordering must be the same as the units enum */
290static real factor_tab[] = {
291   1.0, METRES_PER_FOOT, (METRES_PER_FOOT*3.0),
292   (M_PI/180.0), (M_PI/200.0), 0.01, (M_PI/180.0/60.0)
293};
294
295static int
296get_units(unsigned long qmask, bool percent_ok)
297{
298   static sztok utab[] = {
299        {"DEGREES",       UNITS_DEGS },
300        {"DEGS",          UNITS_DEGS },
301        {"FEET",          UNITS_FEET },
302        {"GRADS",         UNITS_GRADS },
303        {"METERS",        UNITS_METRES },
304        {"METRES",        UNITS_METRES },
305        {"METRIC",        UNITS_METRES },
306        {"MILS",          UNITS_GRADS },
307        {"MINUTES",       UNITS_MINUTES },
308        {"PERCENT",       UNITS_PERCENT },
309        {"PERCENTAGE",    UNITS_PERCENT },
310        {"YARDS",         UNITS_YARDS },
311        {NULL,            UNITS_NULL }
312   };
313   int units;
314   get_token();
315   units = match_tok(utab, TABSIZE(utab));
316   if (units == UNITS_NULL) {
317      file.lpos += strlen(buffer);
318      compile_error_skip(-/*Unknown units “%s”*/35, buffer);
319      return UNITS_NULL;
320   }
321   if (units == UNITS_PERCENT && percent_ok &&
322       !(qmask & ~(BIT(Q_GRADIENT)|BIT(Q_BACKGRADIENT)))) {
323      return units;
324   }
325   if (((qmask & LEN_QMASK) && !TSTBIT(LEN_UMASK, units)) ||
326       ((qmask & ANG_QMASK) && !TSTBIT(ANG_UMASK, units))) {
327      file.lpos += strlen(buffer);
328      compile_error_skip(-/*Invalid units “%s” for quantity*/37, buffer);
329      return UNITS_NULL;
330   }
331   return units;
332}
333
334/* returns mask with bit x set to indicate quantity x specified */
335static unsigned long
336get_qlist(unsigned long mask_bad)
337{
338   static sztok qtab[] = {
339        {"ALTITUDE",     Q_DZ },
340        {"BACKBEARING",  Q_BACKBEARING },
341        {"BACKCLINO",    Q_BACKGRADIENT },    /* alternative name */
342        {"BACKCOMPASS",  Q_BACKBEARING },     /* alternative name */
343        {"BACKGRADIENT", Q_BACKGRADIENT },
344        {"BEARING",      Q_BEARING },
345        {"CEILING",      Q_UP },          /* alternative name */
346        {"CLINO",        Q_GRADIENT },    /* alternative name */
347        {"COMPASS",      Q_BEARING },     /* alternative name */
348        {"COUNT",        Q_COUNT },
349        {"COUNTER",      Q_COUNT },       /* alternative name */
350        {"DECLINATION",  Q_DECLINATION },
351        {"DEFAULT",      Q_DEFAULT }, /* not a real quantity... */
352        {"DEPTH",        Q_DEPTH },
353        {"DOWN",         Q_DOWN },
354        {"DX",           Q_DX },          /* alternative name */
355        {"DY",           Q_DY },          /* alternative name */
356        {"DZ",           Q_DZ },          /* alternative name */
357        {"EASTING",      Q_DX },
358        {"FLOOR",        Q_DOWN },        /* alternative name */
359        {"GRADIENT",     Q_GRADIENT },
360        {"LEFT",         Q_LEFT },
361        {"LENGTH",       Q_LENGTH },
362        {"LEVEL",        Q_LEVEL},
363        {"NORTHING",     Q_DY },
364        {"PLUMB",        Q_PLUMB},
365        {"POSITION",     Q_POS },
366        {"RIGHT",        Q_RIGHT },
367        {"TAPE",         Q_LENGTH },      /* alternative name */
368        {"UP",           Q_UP },
369        {NULL,           Q_NULL }
370   };
371   unsigned long qmask = 0;
372   int tok;
373   filepos fp;
374
375   while (1) {
376      get_pos(&fp);
377      get_token();
378      tok = match_tok(qtab, TABSIZE(qtab));
379      if (tok == Q_DEFAULT && !(mask_bad & BIT(Q_DEFAULT))) {
380          /* Only recognise DEFAULT if it is the first quantity, and then don't
381           * look for any more. */
382          if (qmask == 0)
383              return BIT(Q_DEFAULT);
384          break;
385      }
386      /* bail out if we reach the table end with no match */
387      if (tok == Q_NULL) break;
388      qmask |= BIT(tok);
389      if (qmask & mask_bad) {
390         file.lpos += strlen(buffer);
391         compile_error_skip(-/*Unknown instrument “%s”*/39, buffer);
392         return 0;
393      }
394   }
395
396   if (qmask == 0) {
397      file.lpos += strlen(buffer);
398      compile_error_skip(-/*Unknown quantity “%s”*/34, buffer);
399   } else {
400      set_pos(&fp);
401   }
402
403   return qmask;
404}
405
406#define SPECIAL_UNKNOWN 0
407static void
408cmd_set(void)
409{
410   static sztok chartab[] = {
411        {"BLANK",     SPECIAL_BLANK },
412/*FIXME {"CLOSE",     SPECIAL_CLOSE }, */
413        {"COMMENT",   SPECIAL_COMMENT },
414        {"DECIMAL",   SPECIAL_DECIMAL },
415        {"EOL",       SPECIAL_EOL }, /* EOL won't work well */
416        {"KEYWORD",   SPECIAL_KEYWORD },
417        {"MINUS",     SPECIAL_MINUS },
418        {"NAMES",     SPECIAL_NAMES },
419        {"OMIT",      SPECIAL_OMIT },
420/*FIXME {"OPEN",      SPECIAL_OPEN }, */
421        {"PLUS",      SPECIAL_PLUS },
422#ifndef NO_DEPRECATED
423        {"ROOT",      SPECIAL_ROOT },
424#endif
425        {"SEPARATOR", SPECIAL_SEPARATOR },
426        {NULL,        SPECIAL_UNKNOWN }
427   };
428   int mask;
429   int i;
430
431   get_token();
432   mask = match_tok(chartab, TABSIZE(chartab));
433
434   if (mask == SPECIAL_UNKNOWN) {
435      file.lpos += strlen(buffer);
436      compile_error_skip(-/*Unknown character class “%s”*/42, buffer);
437      return;
438   }
439
440#ifndef NO_DEPRECATED
441   if (mask == SPECIAL_ROOT) {
442      if (root_depr_count < 5) {
443         file.lpos += strlen(buffer);
444         compile_warning(-/*ROOT is deprecated*/25);
445         if (++root_depr_count == 5)
446            compile_warning(/*Further uses of this deprecated feature will not be reported*/95);
447      }
448   }
449#endif
450
451   /* if we're currently using an inherited translation table, allocate a new
452    * table, and copy old one into it */
453   if (pcs->next && pcs->next->Translate == pcs->Translate) {
454      short *p;
455      p = ((short*)osmalloc(ossizeof(short) * 257)) + 1;
456      memcpy(p - 1, pcs->Translate - 1, sizeof(short) * 257);
457      pcs->Translate = p;
458   }
459
460   skipblanks();
461
462   /* clear this flag for all non-alphanums */
463   for (i = 0; i < 256; i++)
464      if (!isalnum(i)) pcs->Translate[i] &= ~mask;
465
466   /* now set this flag for all specified chars */
467   while (!isEol(ch)) {
468      if (!isalnum(ch)) {
469         pcs->Translate[ch] |= mask;
470      } else if (tolower(ch) == 'x') {
471         int hex;
472         filepos fp;
473         get_pos(&fp);
474         nextch();
475         if (!isxdigit(ch)) {
476            set_pos(&fp);
477            break;
478         }
479         hex = isdigit(ch) ? ch - '0' : tolower(ch) - 'a';
480         nextch();
481         if (!isxdigit(ch)) {
482            set_pos(&fp);
483            break;
484         }
485         hex = hex << 4 | (isdigit(ch) ? ch - '0' : tolower(ch) - 'a');
486         pcs->Translate[hex] |= mask;
487      } else {
488         break;
489      }
490      nextch();
491   }
492}
493
494static void
495check_reentry(prefix *tag)
496{
497   /* Don't try to check "*prefix \" or "*begin \" */
498   if (!tag->up) return;
499   if (TSTBIT(tag->sflags, SFLAGS_PREFIX_ENTERED)) {
500      if (tag->line != file.line ||
501          strcmp(tag->filename, file.filename) != 0) {
502         const char *filename_store = file.filename;
503         unsigned int line_store = file.line;
504         static int reenter_depr_count = 0;
505
506         if (reenter_depr_count < 5) {
507            compile_warning(/*Reentering an existing prefix level is deprecated*/29);
508            if (++reenter_depr_count == 5)
509               compile_warning(/*Further uses of this deprecated feature will not be reported*/95);
510         }
511
512         file.filename = tag->filename;
513         file.line = tag->line;
514         compile_warning(/*Originally entered here*/30);
515         file.filename = filename_store;
516         file.line = line_store;
517      }
518   } else {
519      tag->sflags |= BIT(SFLAGS_PREFIX_ENTERED);
520      tag->filename = file.filename;
521      tag->line = file.line;
522   }
523}
524
525#ifndef NO_DEPRECATED
526static void
527cmd_prefix(void)
528{
529   static int prefix_depr_count = 0;
530   prefix *tag;
531   /* Issue warning first, so "*prefix \" warns first that *prefix is
532    * deprecated and then that ROOT is...
533    */
534   if (prefix_depr_count < 5) {
535      compile_warning(-/**prefix is deprecated - use *begin and *end instead*/6);
536      if (++prefix_depr_count == 5)
537         compile_warning(/*Further uses of this deprecated feature will not be reported*/95);
538   }
539   tag = read_prefix(PFX_SURVEY|PFX_ALLOW_ROOT);
540   pcs->Prefix = tag;
541   check_reentry(tag);
542}
543#endif
544
545static void
546cmd_alias(void)
547{
548   /* Currently only one form is supported:
549    * *alias station - ..
550    */
551   get_token();
552   if (strcmp(ucbuffer, "STATION") != 0)
553      goto bad;
554   get_word();
555   if (strcmp(buffer, "-") != 0)
556      goto bad;
557   get_word();
558   if (*buffer && strcmp(buffer, "..") != 0)
559      goto bad;
560   pcs->dash_for_anon_wall_station = (*buffer != '\0');
561   return;
562bad:
563   compile_error_skip(/*Bad *alias command*/397);
564}
565
566static void
567cmd_begin(void)
568{
569   prefix *tag;
570   settings *pcsNew;
571
572   pcsNew = osnew(settings);
573   *pcsNew = *pcs; /* copy contents */
574   pcsNew->begin_lineno = file.line;
575   pcsNew->next = pcs;
576   pcs = pcsNew;
577
578   tag = read_prefix(PFX_SURVEY|PFX_OPT|PFX_ALLOW_ROOT|PFX_WARN_SEPARATOR);
579   pcs->tag = tag;
580   if (tag) {
581      pcs->Prefix = tag;
582      check_reentry(tag);
583      f_export_ok = fTrue;
584   }
585}
586
587extern void
588free_settings(settings *p) {
589   /* don't free default ordering or ordering used by parent */
590   reading *order = p->ordering;
591   if (order != default_order && (!p->next || order != p->next->ordering))
592      osfree(order);
593
594   /* free Translate if not used by parent */
595   if (!p->next || p->Translate != p->next->Translate)
596      osfree(p->Translate - 1);
597
598   /* free meta is not used by parent, or in this block */
599   if (p->meta && p->meta != p->next->meta && p->meta->ref_count == 0)
600       osfree(p->meta);
601
602   osfree(p);
603}
604
605static void
606cmd_end(void)
607{
608   settings *pcsParent;
609   prefix *tag, *tagBegin;
610
611   pcsParent = pcs->next;
612
613   if (pcs->begin_lineno == 0) {
614      if (pcsParent == NULL) {
615         /* more ENDs than BEGINs */
616         compile_error_skip(/*No matching BEGIN*/192);
617      } else {
618         compile_error_skip(/*END with no matching BEGIN in this file*/22);
619      }
620      return;
621   }
622
623   tagBegin = pcs->tag;
624
625   SVX_ASSERT(pcsParent);
626   free_settings(pcs);
627   pcs = pcsParent;
628
629   /* note need to read using root *before* BEGIN */
630   tag = read_prefix(PFX_SURVEY|PFX_OPT|PFX_ALLOW_ROOT);
631   if (tag != tagBegin) {
632      if (tag) {
633         if (!tagBegin) {
634            /* "*begin" / "*end foo" */
635            compile_error_skip(-/*Matching BEGIN tag has no prefix*/36);
636         } else {
637            /* tag mismatch */
638            compile_error_skip(-/*Prefix tag doesn’t match BEGIN*/193);
639         }
640      } else {
641         /* close tag omitted; open tag given */
642         compile_warning(-/*Closing prefix omitted from END*/194);
643      }
644   }
645}
646
647static void
648cmd_entrance(void)
649{
650   prefix *pfx = read_prefix(PFX_STATION);
651   pfx->sflags |= BIT(SFLAGS_ENTRANCE);
652}
653
654static void
655cmd_fix(void)
656{
657   prefix *fix_name;
658   node *stn = NULL;
659   static node *stnOmitAlready = NULL;
660   real x, y, z;
661   int nx, ny, nz;
662   filepos fp;
663
664   fix_name = read_prefix(PFX_STATION|PFX_ALLOW_ROOT);
665   fix_name->sflags |= BIT(SFLAGS_FIXED);
666
667   get_pos(&fp);
668   get_token();
669   if (strcmp(ucbuffer, "REFERENCE") == 0) {
670      /* suppress "unused fixed point" warnings for this station */
671      fix_name->sflags |= BIT(SFLAGS_USED);
672   } else {
673      if (*ucbuffer) set_pos(&fp);
674   }
675
676   x = read_numeric(fTrue, &nx);
677   if (x == HUGE_REAL) {
678      /* If the end of the line isn't blank, read a number after all to
679       * get a more helpful error message */
680      if (!isEol(ch) && !isComm(ch)) x = read_numeric(fFalse, &nx);
681   }
682   if (x == HUGE_REAL) {
683      if (stnOmitAlready) {
684         if (fix_name != stnOmitAlready->name) {
685            compile_error_skip(/*More than one FIX command with no coordinates*/56);
686         } else {
687            compile_warning(/*Same station fixed twice with no coordinates*/61);
688         }
689         return;
690      }
691      stn = StnFromPfx(fix_name);
692      compile_warning(/*FIX command with no coordinates - fixing at (0,0,0)*/54);
693      x = y = z = (real)0.0;
694      stnOmitAlready = stn;
695   } else {
696      real sdx;
697      y = read_numeric(fFalse, &ny);
698      z = read_numeric(fFalse, &nz);
699      sdx = read_numeric(fTrue, NULL);
700      if (sdx != HUGE_REAL) {
701         real sdy, sdz;
702         real cxy = 0, cyz = 0, czx = 0;
703         sdy = read_numeric(fTrue, NULL);
704         if (sdy == HUGE_REAL) {
705            /* only one variance given */
706            sdy = sdz = sdx;
707         } else {
708            sdz = read_numeric(fTrue, NULL);
709            if (sdz == HUGE_REAL) {
710               /* two variances given - horizontal & vertical */
711               sdz = sdy;
712               sdy = sdx;
713            } else {
714               cxy = read_numeric(fTrue, NULL);
715               if (cxy != HUGE_REAL) {
716                  /* covariances given */
717                  cyz = read_numeric(fFalse, NULL);
718                  czx = read_numeric(fFalse, NULL);
719               } else {
720                  cxy = 0;
721               }
722            }
723         }
724         stn = StnFromPfx(fix_name);
725         if (!fixed(stn)) {
726            node *fixpt = osnew(node);
727            prefix *name;
728            name = osnew(prefix);
729            name->pos = osnew(pos);
730            name->ident = NULL;
731            name->shape = 0;
732            fixpt->name = name;
733            name->stn = fixpt;
734            name->up = NULL;
735            if (TSTBIT(pcs->infer, INFER_EXPORTS)) {
736               name->min_export = USHRT_MAX;
737            } else {
738               name->min_export = 0;
739            }
740            name->max_export = 0;
741            name->sflags = 0;
742            add_stn_to_list(&stnlist, fixpt);
743            POS(fixpt, 0) = x;
744            POS(fixpt, 1) = y;
745            POS(fixpt, 2) = z;
746            fix(fixpt);
747            fixpt->leg[0] = fixpt->leg[1] = fixpt->leg[2] = NULL;
748            addfakeleg(fixpt, stn, 0, 0, 0,
749                       sdx * sdx, sdy * sdy, sdz * sdz
750#ifndef NO_COVARIANCES
751                       , cxy, cyz, czx
752#endif
753                       );
754         }
755         return;
756      }
757      stn = StnFromPfx(fix_name);
758   }
759
760   if (!fixed(stn)) {
761      POS(stn, 0) = x;
762      POS(stn, 1) = y;
763      POS(stn, 2) = z;
764      fix(stn);
765      return;
766   }
767
768   if (x != POS(stn, 0) || y != POS(stn, 1) || z != POS(stn, 2)) {
769      compile_error(/*Station already fixed or equated to a fixed point*/46);
770      return;
771   }
772   compile_warning(/*Station already fixed at the same coordinates*/55);
773}
774
775static void
776cmd_flags(void)
777{
778   static sztok flagtab[] = {
779        {"DUPLICATE", FLAGS_DUPLICATE },
780        {"NOT",       FLAGS_NOT },
781        {"SPLAY",     FLAGS_SPLAY },
782        {"SURFACE",   FLAGS_SURFACE },
783        {NULL,        FLAGS_UNKNOWN }
784   };
785   bool fNot = fFalse;
786   bool fEmpty = fTrue;
787   while (1) {
788      int flag;
789      get_token();
790      /* If buffer is empty, it could mean end of line, or maybe
791       * some non-letter junk which is better reported later */
792      if (!buffer[0]) break;
793
794      fEmpty = fFalse;
795      flag = match_tok(flagtab, TABSIZE(flagtab));
796      /* treat the second NOT in "NOT NOT" as an unknown flag */
797      if (flag == FLAGS_UNKNOWN || (fNot && flag == FLAGS_NOT)) {
798         file.lpos += strlen(buffer);
799         compile_error(-/*FLAG “%s” unknown*/68, buffer);
800         /* Recover from “*FLAGS NOT BOGUS SURFACE” by ignoring "NOT BOGUS" */
801         fNot = fFalse;
802      } else if (flag == FLAGS_NOT) {
803         fNot = fTrue;
804      } else if (fNot) {
805         pcs->flags &= ~BIT(flag);
806         fNot = fFalse;
807      } else {
808         pcs->flags |= BIT(flag);
809      }
810   }
811
812   if (fNot) {
813      file.lpos += strlen(buffer);
814      compile_error(-/*Expecting “DUPLICATE”, “SPLAY”, or “SURFACE”*/188);
815   } else if (fEmpty) {
816      file.lpos += strlen(buffer);
817      compile_error(-/*Expecting “NOT”, “DUPLICATE”, “SPLAY”, or “SURFACE”*/189);
818   }
819}
820
821static void
822cmd_equate(void)
823{
824   prefix *name1, *name2;
825   bool fOnlyOneStn = fTrue; /* to trap eg *equate entrance.6 */
826
827   name1 = read_prefix(PFX_STATION|PFX_ALLOW_ROOT|PFX_SUSPECT_TYPO);
828   while (fTrue) {
829      name2 = name1;
830      name1 = read_prefix(PFX_STATION|PFX_ALLOW_ROOT|PFX_SUSPECT_TYPO|PFX_OPT);
831      if (name1 == NULL) {
832         if (fOnlyOneStn) {
833            compile_error_skip(-/*Only one station in EQUATE command*/33);
834         }
835         return;
836      }
837
838      process_equate(name1, name2);
839      fOnlyOneStn = fFalse;
840   }
841}
842
843static void
844report_missing_export(prefix *pfx, int depth)
845{
846   const char *filename_store = file.filename;
847   unsigned int line_store = file.line;
848   prefix *survey = pfx;
849   char *s;
850   int i;
851   for (i = depth + 1; i; i--) {
852      survey = survey->up;
853      SVX_ASSERT(survey);
854   }
855   s = osstrdup(sprint_prefix(survey));
856   if (survey->filename) {
857      file.filename = survey->filename;
858      file.line = survey->line;
859   }
860   compile_error(/*Station “%s” not exported from survey “%s”*/26,
861                 sprint_prefix(pfx), s);
862   if (survey->filename) {
863      file.filename = filename_store;
864      file.line = line_store;
865   }
866   osfree(s);
867}
868
869static void
870cmd_export(void)
871{
872   prefix *pfx;
873
874   fExportUsed = fTrue;
875   pfx = read_prefix(PFX_STATION);
876   do {
877      int depth = 0;
878      {
879         prefix *p = pfx;
880         while (p != NULL && p != pcs->Prefix) {
881            depth++;
882            p = p->up;
883         }
884         /* Something like: *export \foo, but we've excluded use of root */
885         SVX_ASSERT(p);
886      }
887      /* *export \ or similar bogus stuff */
888      SVX_ASSERT(depth);
889#if 0
890      printf("C min %d max %d depth %d pfx %s\n",
891             pfx->min_export, pfx->max_export, depth, sprint_prefix(pfx));
892#endif
893      if (pfx->min_export == 0) {
894         /* not encountered *export for this name before */
895         if (pfx->max_export > depth) report_missing_export(pfx, depth);
896         pfx->min_export = pfx->max_export = depth;
897      } else if (pfx->min_export != USHRT_MAX) {
898         /* FIXME: what to do if a station is marked for inferred exports
899          * but is then explicitly exported?  Currently we just ignore the
900          * explicit export... */
901         if (pfx->min_export - 1 > depth) {
902            report_missing_export(pfx, depth);
903         } else if (pfx->min_export - 1 < depth) {
904            compile_error(/*Station “%s” already exported*/66,
905                          sprint_prefix(pfx));
906         }
907         pfx->min_export = depth;
908      }
909      pfx = read_prefix(PFX_STATION|PFX_OPT);
910   } while (pfx);
911}
912
913static void
914cmd_data(void)
915{
916   static sztok dtab[] = {
917        {"ALTITUDE",     Dz },
918        {"BACKBEARING",  BackComp },
919        {"BACKCLINO",    BackClino }, /* alternative name */
920        {"BACKCOMPASS",  BackComp }, /* alternative name */
921        {"BACKGRADIENT", BackClino },
922        {"BEARING",      Comp },
923        {"CEILING",      Up }, /* alternative name */
924        {"CLINO",        Clino }, /* alternative name */
925        {"COMPASS",      Comp }, /* alternative name */
926        {"COUNT",        Count }, /* FrCount&ToCount in multiline */
927        {"DEPTH",        Depth }, /* FrDepth&ToDepth in multiline */
928        {"DEPTHCHANGE",  DepthChange },
929        {"DIRECTION",    Dir },
930        {"DOWN",         Down },
931        {"DX",           Dx },
932        {"DY",           Dy },
933        {"DZ",           Dz },
934        {"EASTING",      Dx },
935        {"FLOOR",        Down }, /* alternative name */
936        {"FROM",         Fr },
937        {"FROMCOUNT",    FrCount },
938        {"FROMDEPTH",    FrDepth },
939        {"GRADIENT",     Clino },
940        {"IGNORE",       Ignore },
941        {"IGNOREALL",    IgnoreAll },
942        {"LEFT",         Left },
943        {"LENGTH",       Tape },
944        {"NEWLINE",      Newline },
945        {"NORTHING",     Dy },
946        {"RIGHT",        Right },
947        {"STATION",      Station }, /* Fr&To in multiline */
948        {"TAPE",         Tape }, /* alternative name */
949        {"TO",           To },
950        {"TOCOUNT",      ToCount },
951        {"TODEPTH",      ToDepth },
952        {"UP",           Up },
953        {NULL,           End }
954   };
955
956#define MASK_stns BIT(Fr) | BIT(To) | BIT(Station)
957#define MASK_tape BIT(Tape) | BIT(FrCount) | BIT(ToCount) | BIT(Count)
958#define MASK_dpth BIT(FrDepth) | BIT(ToDepth) | BIT(Depth) | BIT(DepthChange)
959#define MASK_comp BIT(Comp) | BIT(BackComp)
960#define MASK_clin BIT(Clino) | BIT(BackClino)
961
962#define MASK_NORMAL MASK_stns | BIT(Dir) | MASK_tape | MASK_comp | MASK_clin
963#define MASK_DIVING MASK_stns | BIT(Dir) | MASK_tape | MASK_comp | MASK_dpth
964#define MASK_CARTESIAN MASK_stns | BIT(Dx) | BIT(Dy) | BIT(Dz)
965#define MASK_CYLPOLAR  MASK_stns | BIT(Dir) | MASK_tape | MASK_comp | MASK_dpth
966#define MASK_PASSAGE BIT(Station) | BIT(Left) | BIT(Right) | BIT(Up) | BIT(Down)
967#define MASK_NOSURVEY MASK_stns
968
969   /* readings which may be given for each style */
970   static const unsigned long mask[] = {
971      MASK_NORMAL, MASK_DIVING, MASK_CARTESIAN, MASK_CYLPOLAR, MASK_PASSAGE,
972      MASK_NOSURVEY
973   };
974
975   /* readings which may be omitted for each style */
976   static const unsigned long mask_optional[] = {
977      BIT(Dir) | BIT(Clino) | BIT(BackClino),
978      BIT(Dir),
979      0,
980      BIT(Dir),
981      0, /* BIT(Left) | BIT(Right) | BIT(Up) | BIT(Down), */
982      0
983   };
984
985   /* all valid readings */
986   static const unsigned long mask_all[] = {
987      MASK_NORMAL | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End),
988      MASK_DIVING | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End),
989      MASK_CARTESIAN | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End),
990      MASK_CYLPOLAR | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End),
991      MASK_PASSAGE | BIT(Ignore) | BIT(IgnoreAll) | BIT(End),
992      MASK_NOSURVEY | BIT(Ignore) | BIT(IgnoreAll) | BIT(End)
993   };
994#define STYLE_DEFAULT   -2
995#define STYLE_UNKNOWN   -1
996
997   static sztok styletab[] = {
998        {"CARTESIAN",    STYLE_CARTESIAN },
999        {"CYLPOLAR",     STYLE_CYLPOLAR },
1000        {"DEFAULT",      STYLE_DEFAULT },
1001        {"DIVING",       STYLE_DIVING },
1002        {"NORMAL",       STYLE_NORMAL },
1003        {"NOSURVEY",     STYLE_NOSURVEY },
1004        {"PASSAGE",      STYLE_PASSAGE },
1005        {"TOPOFIL",      STYLE_NORMAL },
1006        {NULL,           STYLE_UNKNOWN }
1007   };
1008
1009#define m_multi (BIT(Station) | BIT(Count) | BIT(Depth))
1010
1011   int style, k = 0, kMac;
1012   reading *new_order, d;
1013   unsigned long mUsed = 0;
1014   char *style_name;
1015
1016   /* after a bad *data command ignore survey data until the next
1017    * *data command to avoid an avalanche of errors */
1018   pcs->style = STYLE_IGNORE;
1019
1020   kMac = 6; /* minimum for NORMAL style */
1021   new_order = osmalloc(kMac * sizeof(reading));
1022
1023   get_token();
1024   style = match_tok(styletab, TABSIZE(styletab));
1025
1026   if (style == STYLE_DEFAULT) {
1027      default_style(pcs);
1028      return;
1029   }
1030
1031   if (style == STYLE_UNKNOWN) {
1032      file.lpos += strlen(buffer);
1033      compile_error_skip(-/*Data style “%s” unknown*/65, buffer);
1034      return;
1035   }
1036
1037   skipblanks();
1038#ifndef NO_DEPRECATED
1039   /* Olde syntax had optional field for survey grade, so allow an omit
1040    * but issue a warning about it */
1041   if (isOmit(ch)) {
1042      static int data_depr_count = 0;
1043      if (data_depr_count < 5) {
1044         file.lpos += strlen(buffer);
1045         compile_warning(-/*“*data %s %c …” is deprecated - use “*data %s …” instead*/104,
1046                         buffer, ch, buffer);
1047         if (++data_depr_count == 5)
1048            compile_warning(/*Further uses of this deprecated feature will not be reported*/95);
1049      }
1050      nextch();
1051   }
1052#endif
1053
1054   style_name = osstrdup(buffer);
1055   do {
1056      filepos fp;
1057      get_pos(&fp);
1058      get_token();
1059      d = match_tok(dtab, TABSIZE(dtab));
1060      /* only token allowed after IGNOREALL is NEWLINE */
1061      if (k && new_order[k - 1] == IgnoreAll && d != Newline) {
1062         set_pos(&fp);
1063         break;
1064      }
1065      /* Note: an unknown token is reported as trailing garbage */
1066      if (!TSTBIT(mask_all[style], d)) {
1067         file.lpos += strlen(buffer);
1068         compile_error_skip(-/*Reading “%s” not allowed in data style “%s”*/63,
1069                       buffer, style_name);
1070         osfree(style_name);
1071         osfree(new_order);
1072         return;
1073      }
1074      if (TSTBIT(mUsed, Newline) && TSTBIT(m_multi, d)) {
1075         /* e.g. "*data diving station newline tape depth compass" */
1076         file.lpos += strlen(buffer);
1077         compile_error_skip(-/*Reading “%s” must occur before NEWLINE*/225, buffer);
1078         osfree(style_name);
1079         osfree(new_order);
1080         return;
1081      }
1082      /* Check for duplicates unless it's a special reading:
1083       *   IGNOREALL,IGNORE (duplicates allowed) ; END (not possible)
1084       */
1085      if (!((BIT(Ignore) | BIT(End) | BIT(IgnoreAll)) & BIT(d))) {
1086         if (TSTBIT(mUsed, d)) {
1087            file.lpos += strlen(buffer);
1088            compile_error_skip(-/*Duplicate reading “%s”*/67, buffer);
1089            osfree(style_name);
1090            osfree(new_order);
1091            return;
1092         } else {
1093            /* Check for previously listed readings which are incompatible
1094             * with this one - e.g. Count vs FrCount */
1095            bool fBad = fFalse;
1096            switch (d) {
1097             case Station:
1098               if (mUsed & (BIT(Fr) | BIT(To))) fBad = fTrue;
1099               break;
1100             case Fr: case To:
1101               if (TSTBIT(mUsed, Station)) fBad = fTrue;
1102               break;
1103             case Count:
1104               if (mUsed & (BIT(FrCount) | BIT(ToCount) | BIT(Tape)))
1105                  fBad = fTrue;
1106               break;
1107             case FrCount: case ToCount:
1108               if (mUsed & (BIT(Count) | BIT(Tape)))
1109                  fBad = fTrue;
1110               break;
1111             case Depth:
1112               if (mUsed & (BIT(FrDepth) | BIT(ToDepth) | BIT(DepthChange)))
1113                  fBad = fTrue;
1114               break;
1115             case FrDepth: case ToDepth:
1116               if (mUsed & (BIT(Depth) | BIT(DepthChange))) fBad = fTrue;
1117               break;
1118             case DepthChange:
1119               if (mUsed & (BIT(FrDepth) | BIT(ToDepth) | BIT(Depth)))
1120                  fBad = fTrue;
1121               break;
1122             case Newline:
1123               if (mUsed & ~m_multi) {
1124                  /* e.g. "*data normal from to tape newline compass clino" */
1125                  file.lpos += strlen(buffer);
1126                  compile_error_skip(-/*NEWLINE can only be preceded by STATION, DEPTH, and COUNT*/226);
1127                  osfree(style_name);
1128                  osfree(new_order);
1129                  return;
1130               }
1131               if (k == 0) {
1132                  file.lpos += strlen(buffer);
1133                  compile_error_skip(-/*NEWLINE can’t be the first reading*/222);
1134                  osfree(style_name);
1135                  osfree(new_order);
1136                  return;
1137               }
1138               break;
1139             default: /* avoid compiler warnings about unhandled enums */
1140               break;
1141            }
1142            if (fBad) {
1143               /* Not entirely happy with phrasing this... */
1144               file.lpos += strlen(buffer);
1145               compile_error_skip(-/*Reading “%s” duplicates previous reading(s)*/77,
1146                             buffer);
1147               osfree(style_name);
1148               osfree(new_order);
1149               return;
1150            }
1151            mUsed |= BIT(d); /* used to catch duplicates */
1152         }
1153      }
1154      if (k && new_order[k - 1] == IgnoreAll) {
1155         SVX_ASSERT(d == Newline);
1156         k--;
1157         d = IgnoreAllAndNewLine;
1158      }
1159      if (k >= kMac) {
1160         kMac = kMac * 2;
1161         new_order = osrealloc(new_order, kMac * sizeof(reading));
1162      }
1163      new_order[k++] = d;
1164   } while (d != End);
1165
1166   if (k >= 2 && new_order[k - 2] == Newline) {
1167      file.lpos += strlen(buffer);
1168      compile_error_skip(-/*NEWLINE can’t be the last reading*/223);
1169      osfree(style_name);
1170      osfree(new_order);
1171      return;
1172   }
1173
1174   if (style == STYLE_NOSURVEY) {
1175      if (TSTBIT(mUsed, Station)) {
1176         if (k >= kMac) {
1177            kMac = kMac * 2;
1178            new_order = osrealloc(new_order, kMac * sizeof(reading));
1179         }
1180         new_order[k - 1] = Newline;
1181         new_order[k++] = End;
1182      }
1183   } else if (style == STYLE_PASSAGE) {
1184      /* Station doesn't mean "multiline" for STYLE_PASSAGE. */
1185   } else if (!TSTBIT(mUsed, Newline) && (m_multi & mUsed)) {
1186      /* This is for when they write
1187       * *data normal station tape compass clino
1188       * (i.e. no newline, but interleaved readings)
1189       */
1190      compile_error_skip(/*Interleaved readings, but no NEWLINE*/224);
1191      osfree(style_name);
1192      osfree(new_order);
1193      return;
1194   }
1195
1196#if 0
1197   printf("mUsed = 0x%x\n", mUsed);
1198#endif
1199
1200   /* Check the supplied readings form a sufficient set. */
1201   if (style != STYLE_PASSAGE) {
1202       if (mUsed & (BIT(Fr) | BIT(To)))
1203           mUsed |= BIT(Station);
1204       else if (TSTBIT(mUsed, Station))
1205           mUsed |= BIT(Fr) | BIT(To);
1206   }
1207
1208   if (mUsed & (BIT(Comp) | BIT(BackComp)))
1209      mUsed |= BIT(Comp) | BIT(BackComp);
1210
1211   if (mUsed & (BIT(Clino) | BIT(BackClino)))
1212      mUsed |= BIT(Clino) | BIT(BackClino);
1213
1214   if (mUsed & (BIT(FrDepth) | BIT(ToDepth)))
1215      mUsed |= BIT(Depth) | BIT(DepthChange);
1216   else if (TSTBIT(mUsed, Depth))
1217      mUsed |= BIT(FrDepth) | BIT(ToDepth) | BIT(DepthChange);
1218   else if (TSTBIT(mUsed, DepthChange))
1219      mUsed |= BIT(FrDepth) | BIT(ToDepth) | BIT(Depth);
1220
1221   if (mUsed & (BIT(FrCount) | BIT(ToCount)))
1222      mUsed |= BIT(Count) | BIT(Tape);
1223   else if (TSTBIT(mUsed, Count))
1224      mUsed |= BIT(FrCount) | BIT(ToCount) | BIT(Tape);
1225   else if (TSTBIT(mUsed, Tape))
1226      mUsed |= BIT(FrCount) | BIT(ToCount) | BIT(Count);
1227
1228#if 0
1229   printf("mUsed = 0x%x, opt = 0x%x, mask = 0x%x\n", mUsed,
1230          mask_optional[style], mask[style]);
1231#endif
1232
1233   if (((mUsed &~ BIT(Newline)) | mask_optional[style]) != mask[style]) {
1234      /* Test should only fail with too few bits set, not too many */
1235      SVX_ASSERT((((mUsed &~ BIT(Newline)) | mask_optional[style])
1236              &~ mask[style]) == 0);
1237      compile_error_skip(/*Too few readings for data style “%s”*/64, style_name);
1238      osfree(style_name);
1239      osfree(new_order);
1240      return;
1241   }
1242
1243   /* don't free default ordering or ordering used by parent */
1244   if (pcs->ordering != default_order &&
1245       !(pcs->next && pcs->next->ordering == pcs->ordering))
1246      osfree(pcs->ordering);
1247
1248   pcs->style = style;
1249   pcs->ordering = new_order;
1250
1251   osfree(style_name);
1252
1253   if (style == STYLE_PASSAGE) {
1254      lrudlist * new_psg = osnew(lrudlist);
1255      new_psg->tube = NULL;
1256      new_psg->next = model;
1257      model = new_psg;
1258      next_lrud = &(new_psg->tube);
1259   }
1260}
1261
1262static void
1263cmd_units(void)
1264{
1265   int units, quantity;
1266   unsigned long qmask;
1267   unsigned long m; /* mask with bit x set to indicate quantity x specified */
1268   real factor;
1269
1270   qmask = get_qlist(0);
1271   if (!qmask) return;
1272   if (qmask == BIT(Q_DEFAULT)) {
1273      default_units(pcs);
1274      return;
1275   }
1276
1277   factor = read_numeric(fTrue, NULL);
1278   if (factor == 0.0) {
1279      compile_error_skip(-/**UNITS factor must be non-zero*/200);
1280      return;
1281   }
1282   if (factor == HUGE_REAL) factor = (real)1.0;
1283
1284   units = get_units(qmask, fTrue);
1285   if (units == UNITS_NULL) return;
1286   if (TSTBIT(qmask, Q_GRADIENT))
1287      pcs->f_clino_percent = (units == UNITS_PERCENT);
1288   if (TSTBIT(qmask, Q_BACKGRADIENT))
1289      pcs->f_backclino_percent = (units == UNITS_PERCENT);
1290
1291   factor *= factor_tab[units];
1292
1293   for (quantity = 0, m = BIT(quantity); m <= qmask; quantity++, m <<= 1)
1294      if (qmask & m) pcs->units[quantity] = factor;
1295}
1296
1297static void
1298cmd_calibrate(void)
1299{
1300   real sc, z;
1301   unsigned long qmask, m;
1302   int quantity;
1303
1304   qmask = get_qlist(BIT(Q_POS)|BIT(Q_PLUMB)|BIT(Q_LEVEL));
1305   if (!qmask) return; /* error already reported */
1306
1307   if (qmask == BIT(Q_DEFAULT)) {
1308      default_calib(pcs);
1309      return;
1310   }
1311
1312   if (((qmask & LEN_QMASK)) && ((qmask & ANG_QMASK))) {
1313      compile_error_skip(/*Can’t calibrate angular and length quantities together*/227);
1314      return;
1315   }
1316
1317   z = read_numeric(fFalse, NULL);
1318   sc = read_numeric(fTrue, NULL);
1319   if (sc == HUGE_REAL) sc = (real)1.0;
1320   /* check for declination scale */
1321   /* perhaps "*calibrate declination XXX" should be "*declination XXX" ? */
1322   if (TSTBIT(qmask, Q_DECLINATION) && sc != 1.0) {
1323      compile_error_skip(-/*Scale factor must be 1.0 for DECLINATION*/40);
1324      return;
1325   }
1326   if (sc == 0.0) {
1327      compile_error_skip(-/*Scale factor must be non-zero*/391);
1328      return;
1329   }
1330   for (quantity = 0, m = BIT(quantity); m <= qmask; quantity++, m <<= 1) {
1331      if (qmask & m) {
1332         pcs->z[quantity] = pcs->units[quantity] * z;
1333         pcs->sc[quantity] = sc;
1334      }
1335   }
1336}
1337
1338#ifndef NO_DEPRECATED
1339static void
1340cmd_default(void)
1341{
1342   static sztok defaulttab[] = {
1343      { "CALIBRATE", CMD_CALIBRATE },
1344      { "DATA",      CMD_DATA },
1345      { "UNITS",     CMD_UNITS },
1346      { NULL,        CMD_NULL }
1347   };
1348   static int default_depr_count = 0;
1349
1350   if (default_depr_count < 5) {
1351      compile_warning(-/**DEFAULT is deprecated - use *CALIBRATE/DATA/SD/UNITS with argument DEFAULT instead*/20);
1352      if (++default_depr_count == 5)
1353         compile_warning(/*Further uses of this deprecated feature will not be reported*/95);
1354   }
1355
1356   get_token();
1357   switch (match_tok(defaulttab, TABSIZE(defaulttab))) {
1358    case CMD_CALIBRATE:
1359      default_calib(pcs);
1360      break;
1361    case CMD_DATA:
1362      default_style(pcs);
1363      default_grade(pcs);
1364      break;
1365    case CMD_UNITS:
1366      default_units(pcs);
1367      break;
1368    default:
1369      file.lpos += strlen(buffer);
1370      compile_error_skip(-/*Unknown setting “%s”*/41, buffer);
1371   }
1372}
1373#endif
1374
1375static void
1376cmd_include(void)
1377{
1378   char *pth, *fnm = NULL;
1379   int fnm_len;
1380#ifndef NO_DEPRECATED
1381   prefix *root_store;
1382#endif
1383   int ch_store;
1384
1385   pth = path_from_fnm(file.filename);
1386
1387   read_string(&fnm, &fnm_len);
1388
1389#ifndef NO_DEPRECATED
1390   /* Since *begin / *end nesting cannot cross file boundaries we only
1391    * need to preserve the prefix if the deprecated *prefix command
1392    * can be used */
1393   root_store = root;
1394   root = pcs->Prefix; /* Root for include file is current prefix */
1395#endif
1396   ch_store = ch;
1397
1398   data_file(pth, fnm);
1399
1400#ifndef NO_DEPRECATED
1401   root = root_store; /* and restore root */
1402#endif
1403   ch = ch_store;
1404
1405   s_free(&fnm);
1406   osfree(pth);
1407}
1408
1409static void
1410cmd_sd(void)
1411{
1412   real sd, variance;
1413   int units;
1414   unsigned long qmask, m;
1415   int quantity;
1416   qmask = get_qlist(BIT(Q_DECLINATION));
1417   if (!qmask) return; /* no quantities found - error already reported */
1418
1419   if (qmask == BIT(Q_DEFAULT)) {
1420      default_grade(pcs);
1421      return;
1422   }
1423   sd = read_numeric(fFalse, NULL);
1424   if (sd <= (real)0.0) {
1425      compile_error_skip(-/*Standard deviation must be positive*/48);
1426      return;
1427   }
1428   units = get_units(qmask, fFalse);
1429   if (units == UNITS_NULL) return;
1430
1431   sd *= factor_tab[units];
1432   variance = sqrd(sd);
1433
1434   for (quantity = 0, m = BIT(quantity); m <= qmask; quantity++, m <<= 1)
1435      if (qmask & m) pcs->Var[quantity] = variance;
1436}
1437
1438static void
1439cmd_title(void)
1440{
1441   if (!fExplicitTitle && pcs->Prefix == root) {
1442       /* If we don't have an explicit title yet, and we're currently in the
1443        * root prefix, use this title explicitly. */
1444      fExplicitTitle = fTrue;
1445      read_string(&survey_title, &survey_title_len);
1446   } else {
1447      /* parse and throw away this title (but still check rest of line) */
1448      char *s = NULL;
1449      int len;
1450      read_string(&s, &len);
1451      s_free(&s);
1452   }
1453}
1454
1455static sztok case_tab[] = {
1456     {"PRESERVE", OFF},
1457     {"TOLOWER",  LOWER},
1458     {"TOUPPER",  UPPER},
1459     {NULL,       -1}
1460};
1461
1462static void
1463cmd_case(void)
1464{
1465   int setting;
1466   get_token();
1467   setting = match_tok(case_tab, TABSIZE(case_tab));
1468   if (setting != -1) {
1469      pcs->Case = setting;
1470   } else {
1471      file.lpos += strlen(buffer);
1472      compile_error_skip(-/*Found “%s”, expecting “PRESERVE”, “TOUPPER”, or “TOLOWER”*/10,
1473                         buffer);
1474   }
1475}
1476
1477static sztok infer_tab[] = {
1478     { "EQUATES",       INFER_EQUATES },
1479     { "EXPORTS",       INFER_EXPORTS },
1480     { "PLUMBS",        INFER_PLUMBS },
1481#if 0 /* FIXME */
1482     { "SUBSURVEYS",    INFER_SUBSURVEYS },
1483#endif
1484     { NULL,            INFER_NULL }
1485};
1486
1487static sztok onoff_tab[] = {
1488     { "OFF", 0 },
1489     { "ON",  1 },
1490     { NULL, -1 }
1491};
1492
1493static void
1494cmd_infer(void)
1495{
1496   infer_what setting;
1497   int on;
1498   get_token();
1499   setting = match_tok(infer_tab, TABSIZE(infer_tab));
1500   if (setting == INFER_NULL) {
1501      file.lpos += strlen(buffer);
1502      compile_error_skip(-/*Found “%s”, expecting “EQUATES”, “EXPORTS”, or “PLUMBS”*/31, buffer);
1503      return;
1504   }
1505   get_token();
1506   on = match_tok(onoff_tab, TABSIZE(onoff_tab));
1507   if (on == -1) {
1508      file.lpos += strlen(buffer);
1509      compile_error_skip(-/*Found “%s”, expecting “ON” or “OFF”*/32, buffer);
1510      return;
1511   }
1512
1513   if (on) {
1514      pcs->infer |= BIT(setting);
1515      if (setting == INFER_EXPORTS) fExportUsed = fTrue;
1516   } else {
1517      pcs->infer &= ~BIT(setting);
1518   }
1519}
1520
1521static void
1522cmd_truncate(void)
1523{
1524   unsigned int truncate_at = 0; /* default is no truncation */
1525   filepos fp;
1526
1527   get_pos(&fp);
1528
1529   get_token();
1530   if (strcmp(ucbuffer, "OFF") != 0) {
1531      if (*ucbuffer) set_pos(&fp);
1532      truncate_at = read_uint();
1533   }
1534   /* for backward compatibility, "*truncate 0" means "*truncate off" */
1535   pcs->Truncate = (truncate_at == 0) ? INT_MAX : truncate_at;
1536}
1537
1538static void
1539cmd_require(void)
1540{
1541   const unsigned int version[] = {COMMAVERSION};
1542   const unsigned int *ver = version;
1543   filepos fp;
1544
1545   skipblanks();
1546   get_pos(&fp);
1547   while (1) {
1548      int diff = *ver++ - read_uint();
1549      if (diff > 0) break;
1550      if (diff < 0) {
1551         size_t i, len;
1552         char *v;
1553         filepos fp_tmp;
1554
1555         /* find end of version number */
1556         while (isdigit(ch) || ch == '.') nextch();
1557         get_pos(&fp_tmp);
1558         len = (size_t)(fp_tmp.offset - fp.offset);
1559         v = osmalloc(len + 1);
1560         set_pos(&fp);
1561         for (i = 0; i < len; i++) {
1562            v[i] = ch;
1563            nextch();
1564         }
1565         v[i] = '\0';
1566         fatalerror_in_file(file.filename, file.line, /*Survex version %s or greater required to process this survey data.*/2, v);
1567      }
1568      if (ch != '.') break;
1569      nextch();
1570      if (!isdigit(ch) || ver == version + sizeof(version)) break;
1571   }
1572   /* skip rest of version number */
1573   while (isdigit(ch) || ch == '.') nextch();
1574}
1575
1576/* allocate new meta_data if need be */
1577void
1578copy_on_write_meta(settings *s)
1579{
1580   if (!s->meta || s->meta->ref_count != 0) {
1581       meta_data * meta_new = osnew(meta_data);
1582       if (!s->meta) {
1583           meta_new->days1 = meta_new->days2 = -1;
1584       } else {
1585           *meta_new = *(s->meta);
1586       }
1587       meta_new->ref_count = 0;
1588       s->meta = meta_new;
1589   }
1590}
1591
1592static void
1593cmd_date(void)
1594{
1595    int year, month, day;
1596    int days1, days2;
1597    bool implicit_range = fFalse;
1598
1599    read_date(&year, &month, &day);
1600    days1 = days_since_1900(year, month ? month : 1, day ? day : 1);
1601
1602    if (days1 > current_days_since_1900) {
1603        compile_warning(-/*Date is in the future!*/80);
1604    }
1605
1606    skipblanks();
1607    if (ch == '-') {
1608        nextch();
1609        read_date(&year, &month, &day);
1610    } else {
1611        if (month && day) {
1612            days2 = days1;
1613            goto read;
1614        }
1615        implicit_range = fTrue;
1616    }
1617
1618    if (month == 0) month = 12;
1619    if (day == 0) day = last_day(year, month);
1620    days2 = days_since_1900(year, month, day);
1621
1622    if (!implicit_range && days2 > current_days_since_1900) {
1623        compile_warning(-/*Date is in the future!*/80);
1624    }
1625
1626    if (days2 < days1) {
1627        compile_error(-/*End of date range is before the start*/81);
1628    }
1629
1630read:
1631    copy_on_write_meta(pcs);
1632    pcs->meta->days1 = days1;
1633    pcs->meta->days2 = days2;
1634}
1635
1636typedef void (*cmd_fn)(void);
1637
1638static cmd_fn cmd_funcs[] = {
1639   cmd_alias,
1640   cmd_begin,
1641   cmd_calibrate,
1642   cmd_case,
1643   skipline, /*cmd_copyright,*/
1644   cmd_data,
1645   cmd_date,
1646#ifndef NO_DEPRECATED
1647   cmd_default,
1648#endif
1649   cmd_end,
1650   cmd_entrance,
1651   cmd_equate,
1652   cmd_export,
1653   cmd_fix,
1654   cmd_flags,
1655   cmd_include,
1656   cmd_infer,
1657   skipline, /*cmd_instrument,*/
1658#ifndef NO_DEPRECATED
1659   cmd_prefix,
1660#endif
1661   cmd_require,
1662   cmd_sd,
1663   cmd_set,
1664   solve_network,
1665   skipline, /*cmd_team,*/
1666   cmd_title,
1667   cmd_truncate,
1668   cmd_units
1669};
1670
1671extern void
1672handle_command(void)
1673{
1674   int cmdtok;
1675   get_token();
1676   cmdtok = match_tok(cmd_tab, TABSIZE(cmd_tab));
1677
1678   if (cmdtok < 0 || cmdtok >= (int)(sizeof(cmd_funcs) / sizeof(cmd_fn))) {
1679      file.lpos += strlen(buffer);
1680      compile_error_skip(-/*Unknown command “%s”*/12, buffer);
1681      return;
1682   }
1683
1684   switch (cmdtok) {
1685    case CMD_EXPORT:
1686      if (!f_export_ok)
1687         compile_error(/**EXPORT must immediately follow “*BEGIN <SURVEY>”*/57);
1688      break;
1689    case CMD_COPYRIGHT:
1690    case CMD_DATE:
1691    case CMD_INSTRUMENT:
1692    case CMD_TEAM:
1693    case CMD_TITLE:
1694      /* These can occur between *begin and *export */
1695      break;
1696    default:
1697      /* NB: additional handling for "*begin <survey>" in cmd_begin */
1698      f_export_ok = fFalse;
1699      break;
1700   }
1701
1702   cmd_funcs[cmdtok]();
1703}
Note: See TracBrowser for help on using the repository browser.