source: git/trunk/src/commands.c @ 7bb8184

Last change on this file since 7bb8184 was 7bb8184, checked in by Olly Betts <olly@…>, 13 years ago

Retagging 1.2.0

git-svn-id: file:///home/survex-svn/survex/tags/1.2.0@3664 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

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