source: git/src/commands.c @ 9fcc81a

RELEASE/1.2debug-cidebug-ci-sanitiserswalls-datawalls-data-hanging-as-warning
Last change on this file since 9fcc81a was 9699215, checked in by Olly Betts <olly@…>, 5 years ago

Update PROJ project name and website

The name is now "PROJ" (all caps and no ".4" suffix) and the website
is: https://proj.org/

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