[ff6cfe1] | 1 | /* commands.c |
---|
[d1b1380] | 2 | * Code for directives |
---|
[0f8216c] | 3 | * Copyright (C) 1991-2025 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 | #include <config.h> |
---|
[d1b1380] | 21 | |
---|
[a420b49] | 22 | #include <limits.h> |
---|
[be97baf] | 23 | #include <stddef.h> /* for offsetof */ |
---|
[aeeb3de] | 24 | #include <string.h> |
---|
[a420b49] | 25 | |
---|
[b39e24a] | 26 | #include <proj.h> |
---|
[d9d8f21] | 27 | #if PROJ_VERSION_MAJOR < 8 |
---|
| 28 | # define proj_context_errno_string(CTX, ERR) proj_errno_string(ERR) |
---|
| 29 | #endif |
---|
[c092d72] | 30 | |
---|
[a420b49] | 31 | #include "cavern.h" |
---|
[d1b1380] | 32 | #include "commands.h" |
---|
| 33 | #include "datain.h" |
---|
[1ee204e] | 34 | #include "date.h" |
---|
[d1b1380] | 35 | #include "debug.h" |
---|
[5853657] | 36 | #include "filename.h" |
---|
| 37 | #include "message.h" |
---|
| 38 | #include "netbits.h" |
---|
| 39 | #include "netskel.h" |
---|
[a49a80c0] | 40 | #include "osalloc.h" |
---|
[5f1e194] | 41 | #include "out.h" |
---|
[5853657] | 42 | #include "readval.h" |
---|
[69c920d] | 43 | #include "str.h" |
---|
[a420b49] | 44 | |
---|
[bf9faf6] | 45 | static void |
---|
| 46 | move_to_fixedlist(node *stn, int ignore_dirn) |
---|
| 47 | { |
---|
| 48 | remove_stn_from_list(&stnlist, stn); |
---|
| 49 | add_stn_to_list(&fixedlist, stn); |
---|
| 50 | pos *p = stn->name->pos; |
---|
| 51 | for (int d = 0; d < 3; d++) { |
---|
| 52 | if (d == ignore_dirn) continue; |
---|
| 53 | linkfor *leg = stn->leg[d]; |
---|
| 54 | if (!leg) break; |
---|
| 55 | node *to = leg->l.to; |
---|
| 56 | if (to->name->pos == p) { |
---|
| 57 | move_to_fixedlist(to, reverse_leg_dirn(leg)); |
---|
| 58 | } |
---|
| 59 | } |
---|
| 60 | } |
---|
| 61 | |
---|
[05b93bc0] | 62 | int fix_station(prefix *fix_name, const double* coords) { |
---|
[e315359] | 63 | bool new_stn = (fix_name->stn == NULL && |
---|
| 64 | !TSTBIT(fix_name->sflags, SFLAGS_SOLVED)); |
---|
[725d3b1] | 65 | fix_name->sflags |= BIT(SFLAGS_FIXED); |
---|
[e315359] | 66 | if (new_stn) fix_name->sflags |= BIT(SFLAGS_UNUSED_FIXED_POINT); |
---|
[725d3b1] | 67 | node *stn = StnFromPfx(fix_name); |
---|
| 68 | if (fixed(stn)) { |
---|
| 69 | if (coords[0] != POS(stn, 0) || |
---|
| 70 | coords[1] != POS(stn, 1) || |
---|
| 71 | coords[2] != POS(stn, 2)) { |
---|
| 72 | return -1; |
---|
| 73 | } |
---|
| 74 | return 1; |
---|
| 75 | } |
---|
| 76 | |
---|
| 77 | POS(stn, 0) = coords[0]; |
---|
| 78 | POS(stn, 1) = coords[1]; |
---|
| 79 | POS(stn, 2) = coords[2]; |
---|
| 80 | |
---|
[bf9faf6] | 81 | if (new_stn) { |
---|
| 82 | remove_stn_from_list(&stnlist, stn); |
---|
| 83 | add_stn_to_list(&fixedlist, stn); |
---|
| 84 | } else { |
---|
| 85 | move_to_fixedlist(stn, -1); |
---|
| 86 | } |
---|
| 87 | |
---|
[725d3b1] | 88 | // Make the station's file:line location reflect where it was fixed. |
---|
| 89 | fix_name->filename = file.filename; |
---|
| 90 | fix_name->line = file.line; |
---|
| 91 | return 0; |
---|
| 92 | } |
---|
| 93 | |
---|
[05b93bc0] | 94 | void fix_station_with_variance(prefix *fix_name, const double* coords, |
---|
[725d3b1] | 95 | real var_x, real var_y, real var_z, |
---|
| 96 | #ifndef NO_COVARIANCES |
---|
| 97 | real cxy, real cyz, real czx |
---|
| 98 | #endif |
---|
| 99 | ) |
---|
| 100 | { |
---|
[e315359] | 101 | bool new_stn = (fix_name->stn == NULL && |
---|
| 102 | !TSTBIT(fix_name->sflags, SFLAGS_SOLVED)); |
---|
| 103 | if (new_stn) fix_name->sflags |= BIT(SFLAGS_UNUSED_FIXED_POINT); |
---|
| 104 | |
---|
[725d3b1] | 105 | node *stn = StnFromPfx(fix_name); |
---|
| 106 | if (!fixed(stn)) { |
---|
| 107 | node *fixpt = osnew(node); |
---|
| 108 | prefix *name; |
---|
| 109 | name = osnew(prefix); |
---|
| 110 | name->pos = osnew(pos); |
---|
[ba84079] | 111 | name->ident.p = NULL; |
---|
[725d3b1] | 112 | fixpt->name = name; |
---|
| 113 | name->stn = fixpt; |
---|
| 114 | name->up = NULL; |
---|
| 115 | if (TSTBIT(pcs->infer, INFER_EXPORTS)) { |
---|
| 116 | name->min_export = USHRT_MAX; |
---|
| 117 | } else { |
---|
| 118 | name->min_export = 0; |
---|
| 119 | } |
---|
| 120 | name->max_export = 0; |
---|
| 121 | name->sflags = 0; |
---|
[bf9faf6] | 122 | add_stn_to_list(&fixedlist, fixpt); |
---|
[725d3b1] | 123 | POS(fixpt, 0) = coords[0]; |
---|
| 124 | POS(fixpt, 1) = coords[1]; |
---|
| 125 | POS(fixpt, 2) = coords[2]; |
---|
| 126 | fixpt->leg[0] = fixpt->leg[1] = fixpt->leg[2] = NULL; |
---|
| 127 | addfakeleg(fixpt, stn, 0, 0, 0, |
---|
| 128 | var_x, var_y, var_z |
---|
| 129 | #ifndef NO_COVARIANCES |
---|
| 130 | , cxy, cyz, czx |
---|
| 131 | #endif |
---|
| 132 | ); |
---|
| 133 | } |
---|
| 134 | } |
---|
| 135 | |
---|
[a420b49] | 136 | static void |
---|
| 137 | default_grade(settings *s) |
---|
| 138 | { |
---|
[770157e] | 139 | /* Values correspond to those in bcra5.svx */ |
---|
| 140 | s->Var[Q_POS] = (real)sqrd(0.05); |
---|
| 141 | s->Var[Q_LENGTH] = (real)sqrd(0.05); |
---|
[4f38f94] | 142 | s->Var[Q_BACKLENGTH] = (real)sqrd(0.05); |
---|
[770157e] | 143 | s->Var[Q_COUNT] = (real)sqrd(0.05); |
---|
| 144 | s->Var[Q_DX] = s->Var[Q_DY] = s->Var[Q_DZ] = (real)sqrd(0.05); |
---|
| 145 | s->Var[Q_BEARING] = (real)sqrd(rad(0.5)); |
---|
| 146 | s->Var[Q_GRADIENT] = (real)sqrd(rad(0.5)); |
---|
| 147 | s->Var[Q_BACKBEARING] = (real)sqrd(rad(0.5)); |
---|
| 148 | s->Var[Q_BACKGRADIENT] = (real)sqrd(rad(0.5)); |
---|
[a420b49] | 149 | /* SD of plumbed legs (0.25 degrees?) */ |
---|
| 150 | s->Var[Q_PLUMB] = (real)sqrd(rad(0.25)); |
---|
[6b7079f] | 151 | /* SD of level legs (0.25 degrees?) */ |
---|
| 152 | s->Var[Q_LEVEL] = (real)sqrd(rad(0.25)); |
---|
[770157e] | 153 | s->Var[Q_DEPTH] = (real)sqrd(0.05); |
---|
[a420b49] | 154 | } |
---|
| 155 | |
---|
| 156 | static void |
---|
| 157 | default_truncate(settings *s) |
---|
| 158 | { |
---|
| 159 | s->Truncate = INT_MAX; |
---|
| 160 | } |
---|
| 161 | |
---|
| 162 | static void |
---|
| 163 | default_case(settings *s) |
---|
| 164 | { |
---|
| 165 | s->Case = LOWER; |
---|
| 166 | } |
---|
| 167 | |
---|
[27b8b59] | 168 | static reading default_order[] = { Fr, To, Tape, Comp, Clino, End }; |
---|
[a420b49] | 169 | |
---|
| 170 | static void |
---|
| 171 | default_style(settings *s) |
---|
| 172 | { |
---|
[fdffa7d] | 173 | s->recorded_style = s->style = STYLE_NORMAL; |
---|
[a420b49] | 174 | s->ordering = default_order; |
---|
[63d4f07] | 175 | s->dash_for_anon_wall_station = false; |
---|
[a420b49] | 176 | } |
---|
| 177 | |
---|
| 178 | static void |
---|
| 179 | default_prefix(settings *s) |
---|
| 180 | { |
---|
| 181 | s->Prefix = root; |
---|
| 182 | } |
---|
| 183 | |
---|
| 184 | static void |
---|
[5d59477] | 185 | init_default_translate_map(short * t) |
---|
[a420b49] | 186 | { |
---|
| 187 | int i; |
---|
[5d59477] | 188 | for (i = '0'; i <= '9'; i++) t[i] |= SPECIAL_NAMES; |
---|
| 189 | for (i = 'A'; i <= 'Z'; i++) t[i] |= SPECIAL_NAMES; |
---|
| 190 | for (i = 'a'; i <= 'z'; i++) t[i] |= SPECIAL_NAMES; |
---|
[a420b49] | 191 | |
---|
| 192 | t['\t'] |= SPECIAL_BLANK; |
---|
| 193 | t[' '] |= SPECIAL_BLANK; |
---|
| 194 | t[','] |= SPECIAL_BLANK; |
---|
| 195 | t[';'] |= SPECIAL_COMMENT; |
---|
| 196 | t['\032'] |= SPECIAL_EOL; /* Ctrl-Z, so olde DOS text files are handled ok */ |
---|
| 197 | t['\n'] |= SPECIAL_EOL; |
---|
| 198 | t['\r'] |= SPECIAL_EOL; |
---|
| 199 | t['*'] |= SPECIAL_KEYWORD; |
---|
| 200 | t['-'] |= SPECIAL_OMIT; |
---|
| 201 | t['\\'] |= SPECIAL_ROOT; |
---|
| 202 | t['.'] |= SPECIAL_SEPARATOR; |
---|
| 203 | t['_'] |= SPECIAL_NAMES; |
---|
[f3ac7d4] | 204 | t['-'] |= SPECIAL_NAMES; /* Added in 0.97 prerelease 4 */ |
---|
[a420b49] | 205 | t['.'] |= SPECIAL_DECIMAL; |
---|
| 206 | t['-'] |= SPECIAL_MINUS; |
---|
| 207 | t['+'] |= SPECIAL_PLUS; |
---|
[3593388] | 208 | #if 0 /* FIXME */ |
---|
[7d40549] | 209 | t['{'] |= SPECIAL_OPEN; |
---|
| 210 | t['}'] |= SPECIAL_CLOSE; |
---|
[3593388] | 211 | #endif |
---|
[a420b49] | 212 | } |
---|
| 213 | |
---|
[5d59477] | 214 | static void |
---|
| 215 | default_translate(settings *s) |
---|
| 216 | { |
---|
| 217 | if (s->next && s->next->Translate == s->Translate) { |
---|
| 218 | /* We're currently using the same character translation map as our parent |
---|
| 219 | * scope so allocate a new one before we modify it. |
---|
| 220 | */ |
---|
[ae917b96] | 221 | s->Translate = ((short*)osmalloc(sizeof(short) * 257)) + 1; |
---|
[5d59477] | 222 | } else { |
---|
| 223 | /* SVX_ASSERT(EOF==-1);*/ /* important, since we rely on this */ |
---|
| 224 | } |
---|
| 225 | s->Translate[EOF] = SPECIAL_EOL; |
---|
| 226 | memset(s->Translate, 0, sizeof(short) * 256); |
---|
| 227 | init_default_translate_map(s->Translate); |
---|
| 228 | } |
---|
| 229 | |
---|
| 230 | /* Flag anything used in SPECIAL_* cumulatively to help us pick a suitable |
---|
| 231 | * separator to use in the .3d file. */ |
---|
| 232 | static short separator_map[256]; |
---|
| 233 | |
---|
| 234 | void |
---|
| 235 | scan_compass_station_name(prefix *stn) |
---|
| 236 | { |
---|
| 237 | /* We only need to scan the leaf station name - any survey hierarchy above |
---|
| 238 | * that must have been set up in .svx files for which we update |
---|
| 239 | * separator_map via cmd_set() plus adding the defaults in |
---|
| 240 | * find_output_separator(). |
---|
| 241 | */ |
---|
[ba84079] | 242 | for (const char *p = prefix_ident(stn); *p; ++p) { |
---|
[5d59477] | 243 | separator_map[(unsigned char)*p] |= SPECIAL_NAMES; |
---|
| 244 | } |
---|
| 245 | } |
---|
| 246 | |
---|
| 247 | static char |
---|
| 248 | find_output_separator(void) |
---|
| 249 | { |
---|
| 250 | // Fast path to handle most common cases where we'd pick '.'. |
---|
| 251 | if ((separator_map['.'] & SPECIAL_NAMES) == 0) { |
---|
| 252 | return '.'; |
---|
| 253 | } |
---|
| 254 | |
---|
| 255 | static bool added_defaults = false; |
---|
| 256 | if (!added_defaults) { |
---|
| 257 | /* Add the default settings to separator_map. */ |
---|
| 258 | init_default_translate_map(separator_map); |
---|
| 259 | added_defaults = true; |
---|
| 260 | } |
---|
| 261 | |
---|
| 262 | /* 30 punctuation characters plus space to try arranged in a sensible order |
---|
| 263 | * of decreasing preference (these are all the ASCII punctuation characters |
---|
| 264 | * excluding '_' and '-' since those are allowed in names by default so are |
---|
| 265 | * poor choices for the separator). |
---|
| 266 | */ |
---|
| 267 | int best = -1; |
---|
| 268 | for (const char *p = "./:;,!|\\ ~+*^='`\"#$%&?@<>()[]{}"; *p; ++p) { |
---|
| 269 | unsigned char candidate = *p; |
---|
| 270 | int mask = separator_map[candidate]; |
---|
| 271 | switch (mask & (SPECIAL_SEPARATOR|SPECIAL_NAMES)) { |
---|
| 272 | case SPECIAL_SEPARATOR: |
---|
| 273 | /* A character which is set as a separator character at some |
---|
| 274 | * point but never set as a name character is perfect. |
---|
| 275 | */ |
---|
| 276 | return candidate; |
---|
| 277 | case 0: |
---|
| 278 | /* A character which is never set as either a separator |
---|
| 279 | * character or a name character is a reasonable option. |
---|
| 280 | */ |
---|
| 281 | if (best < 0) best = candidate; |
---|
| 282 | break; |
---|
| 283 | } |
---|
| 284 | } |
---|
| 285 | if (best < 0) { |
---|
| 286 | /* Argh, no plausible choice! Just return the default for now. */ |
---|
| 287 | return '.'; |
---|
| 288 | } |
---|
| 289 | return best; |
---|
| 290 | } |
---|
| 291 | |
---|
[be97baf] | 292 | void |
---|
[a420b49] | 293 | default_units(settings *s) |
---|
| 294 | { |
---|
| 295 | int quantity; |
---|
| 296 | for (quantity = 0; quantity < Q_MAC; quantity++) { |
---|
[7f08c83] | 297 | if (TSTBIT(ANG_QMASK, quantity)) |
---|
[bca0071] | 298 | s->units[quantity] = (real)(M_PI / 180.0); /* degrees */ |
---|
[a420b49] | 299 | else |
---|
| 300 | s->units[quantity] = (real)1.0; /* metres */ |
---|
| 301 | } |
---|
[63d4f07] | 302 | s->f_clino_percent = s->f_backclino_percent = false; |
---|
| 303 | s->f_bearing_quadrants = s->f_backbearing_quadrants = false; |
---|
[a420b49] | 304 | } |
---|
| 305 | |
---|
[be97baf] | 306 | void |
---|
[a420b49] | 307 | default_calib(settings *s) |
---|
| 308 | { |
---|
| 309 | int quantity; |
---|
| 310 | for (quantity = 0; quantity < Q_MAC; quantity++) { |
---|
| 311 | s->z[quantity] = (real)0.0; |
---|
| 312 | s->sc[quantity] = (real)1.0; |
---|
| 313 | } |
---|
| 314 | } |
---|
| 315 | |
---|
[5c3c61a] | 316 | static void |
---|
| 317 | default_flags(settings *s) |
---|
| 318 | { |
---|
| 319 | s->flags = 0; |
---|
| 320 | } |
---|
| 321 | |
---|
[a420b49] | 322 | extern void |
---|
| 323 | default_all(settings *s) |
---|
| 324 | { |
---|
| 325 | default_truncate(s); |
---|
[27b8b59] | 326 | s->infer = 0; |
---|
[a420b49] | 327 | default_case(s); |
---|
| 328 | default_style(s); |
---|
| 329 | default_prefix(s); |
---|
| 330 | default_translate(s); |
---|
| 331 | default_grade(s); |
---|
| 332 | default_units(s); |
---|
| 333 | default_calib(s); |
---|
[5c3c61a] | 334 | default_flags(s); |
---|
[a420b49] | 335 | } |
---|
| 336 | |
---|
[0532954] | 337 | string token = S_INIT; |
---|
[d1b1380] | 338 | |
---|
[725d3b1] | 339 | string uctoken = S_INIT; |
---|
[5f1e194] | 340 | |
---|
[a420b49] | 341 | extern void |
---|
[e1cbc0d] | 342 | get_token_legacy(void) |
---|
[f6bdb01] | 343 | { |
---|
| 344 | skipblanks(); |
---|
[e1cbc0d] | 345 | get_token_legacy_no_blanks(); |
---|
[f6bdb01] | 346 | } |
---|
| 347 | |
---|
| 348 | extern void |
---|
[e1cbc0d] | 349 | get_token_legacy_no_blanks(void) |
---|
[a420b49] | 350 | { |
---|
[0532954] | 351 | s_clear(&token); |
---|
| 352 | s_clear(&uctoken); |
---|
[5f1e194] | 353 | while (isalpha(ch)) { |
---|
[94d9f64] | 354 | s_appendch(&token, ch); |
---|
| 355 | s_appendch(&uctoken, toupper(ch)); |
---|
[5f1e194] | 356 | nextch(); |
---|
| 357 | } |
---|
[cfe093e] | 358 | |
---|
| 359 | #if 0 |
---|
[e1cbc0d] | 360 | printf("get_token_legacy_no_blanks() got “%s”\n", s_str(&token)); |
---|
[cfe093e] | 361 | #endif |
---|
[d1b1380] | 362 | } |
---|
| 363 | |
---|
[05b9de76] | 364 | void |
---|
| 365 | do_legacy_token_warning(void) |
---|
| 366 | { |
---|
| 367 | if (!s_empty(&token)) { |
---|
| 368 | if (!isBlank(ch) && !isComm(ch) && !isEol(ch)) { |
---|
| 369 | compile_diagnostic(DIAG_WARN|DIAG_COL, /*No blank after token*/74); |
---|
| 370 | } |
---|
| 371 | } |
---|
| 372 | } |
---|
| 373 | |
---|
[725d3b1] | 374 | extern void |
---|
[e1cbc0d] | 375 | get_token(void) |
---|
[725d3b1] | 376 | { |
---|
[e1cbc0d] | 377 | skipblanks(); |
---|
| 378 | get_token_no_blanks(); |
---|
| 379 | } |
---|
| 380 | |
---|
| 381 | extern void |
---|
| 382 | get_token_no_blanks(void) |
---|
| 383 | { |
---|
| 384 | s_clear(&token); |
---|
| 385 | s_clear(&uctoken); |
---|
| 386 | if (isalpha(ch)) { |
---|
| 387 | do { |
---|
[94d9f64] | 388 | s_appendch(&token, ch); |
---|
| 389 | s_appendch(&uctoken, toupper(ch)); |
---|
[e1cbc0d] | 390 | nextch(); |
---|
| 391 | } while (isalnum(ch)); |
---|
| 392 | } |
---|
[725d3b1] | 393 | } |
---|
| 394 | |
---|
[dcbcae0] | 395 | /* read word */ |
---|
[725d3b1] | 396 | void |
---|
[dcbcae0] | 397 | get_word(void) |
---|
| 398 | { |
---|
[2f246b0] | 399 | s_clear(&token); |
---|
[dcbcae0] | 400 | skipblanks(); |
---|
[2aa37a8] | 401 | while (!isBlank(ch) && !isComm(ch) && !isEol(ch)) { |
---|
[94d9f64] | 402 | s_appendch(&token, ch); |
---|
[dcbcae0] | 403 | nextch(); |
---|
| 404 | } |
---|
| 405 | #if 0 |
---|
[2f246b0] | 406 | printf("get_word() got “%s”\n", s_str(&token)); |
---|
[dcbcae0] | 407 | #endif |
---|
| 408 | } |
---|
| 409 | |
---|
[d1b1380] | 410 | /* match_tok() now uses binary chop |
---|
| 411 | * tab argument should be alphabetically sorted (ascending) |
---|
| 412 | */ |
---|
[a420b49] | 413 | extern int |
---|
| 414 | match_tok(const sztok *tab, int tab_size) |
---|
| 415 | { |
---|
[5f1e194] | 416 | int a = 0, b = tab_size - 1, c; |
---|
| 417 | int r; |
---|
[0532954] | 418 | const char* tok = s_str(&uctoken); |
---|
[cf0560b] | 419 | SVX_ASSERT(tab_size > 0); /* catch empty table */ |
---|
[d1b1380] | 420 | /* printf("[%d,%d]",a,b); */ |
---|
[5f1e194] | 421 | while (a <= b) { |
---|
| 422 | c = (unsigned)(a + b) / 2; |
---|
[d1b1380] | 423 | /* printf(" %d",c); */ |
---|
[0532954] | 424 | r = strcmp(tab[c].sz, tok); |
---|
[5f1e194] | 425 | if (r == 0) return tab[c].tok; /* match */ |
---|
| 426 | if (r < 0) |
---|
| 427 | a = c + 1; |
---|
| 428 | else |
---|
| 429 | b = c - 1; |
---|
| 430 | } |
---|
| 431 | return tab[tab_size].tok; /* no match */ |
---|
[d1b1380] | 432 | } |
---|
| 433 | |
---|
[fb2e93c] | 434 | typedef enum { |
---|
[abe7192] | 435 | CMD_NULL = -1, CMD_ALIAS, CMD_BEGIN, CMD_CALIBRATE, CMD_CARTESIAN, CMD_CASE, |
---|
| 436 | CMD_COPYRIGHT, CMD_CS, CMD_DATA, CMD_DATE, CMD_DECLINATION, CMD_DEFAULT, |
---|
| 437 | CMD_END, CMD_ENTRANCE, CMD_EQUATE, CMD_EXPORT, CMD_FIX, CMD_FLAGS, |
---|
| 438 | CMD_INCLUDE, CMD_INFER, CMD_INSTRUMENT, CMD_PREFIX, CMD_REF, CMD_REQUIRE, |
---|
| 439 | CMD_SD, CMD_SET, CMD_SOLVE, CMD_TEAM, CMD_TITLE, CMD_TRUNCATE, CMD_UNITS |
---|
[5f1e194] | 440 | } cmds; |
---|
[cb3d1e2] | 441 | |
---|
[82919e07] | 442 | static const sztok cmd_tab[] = { |
---|
[dcbcae0] | 443 | {"ALIAS", CMD_ALIAS}, |
---|
[5f1e194] | 444 | {"BEGIN", CMD_BEGIN}, |
---|
| 445 | {"CALIBRATE", CMD_CALIBRATE}, |
---|
[abe7192] | 446 | {"CARTESIAN", CMD_CARTESIAN}, |
---|
[a420b49] | 447 | {"CASE", CMD_CASE}, |
---|
[13a48f6] | 448 | {"COPYRIGHT", CMD_COPYRIGHT}, |
---|
[abd0310] | 449 | {"CS", CMD_CS}, |
---|
[5f1e194] | 450 | {"DATA", CMD_DATA}, |
---|
[13a48f6] | 451 | {"DATE", CMD_DATE}, |
---|
[58c7b459] | 452 | {"DECLINATION", CMD_DECLINATION}, |
---|
[a7d5f1c] | 453 | #ifndef NO_DEPRECATED |
---|
[5f1e194] | 454 | {"DEFAULT", CMD_DEFAULT}, |
---|
[a7d5f1c] | 455 | #endif |
---|
[5f1e194] | 456 | {"END", CMD_END}, |
---|
[dfb4240] | 457 | {"ENTRANCE", CMD_ENTRANCE}, |
---|
[5f1e194] | 458 | {"EQUATE", CMD_EQUATE}, |
---|
[fb2e93c] | 459 | {"EXPORT", CMD_EXPORT}, |
---|
[5f1e194] | 460 | {"FIX", CMD_FIX}, |
---|
[5c3c61a] | 461 | {"FLAGS", CMD_FLAGS}, |
---|
[5f1e194] | 462 | {"INCLUDE", CMD_INCLUDE}, |
---|
[a420b49] | 463 | {"INFER", CMD_INFER}, |
---|
[ec6a4b3] | 464 | {"INSTRUMENT",CMD_INSTRUMENT}, |
---|
[a7d5f1c] | 465 | #ifndef NO_DEPRECATED |
---|
[5f1e194] | 466 | {"PREFIX", CMD_PREFIX}, |
---|
[a7d5f1c] | 467 | #endif |
---|
[ce15637] | 468 | {"REF", CMD_REF}, |
---|
[647407d] | 469 | {"REQUIRE", CMD_REQUIRE}, |
---|
[a4ae909] | 470 | {"SD", CMD_SD}, |
---|
[5f1e194] | 471 | {"SET", CMD_SET}, |
---|
| 472 | {"SOLVE", CMD_SOLVE}, |
---|
[13a48f6] | 473 | {"TEAM", CMD_TEAM}, |
---|
[a420b49] | 474 | {"TITLE", CMD_TITLE}, |
---|
| 475 | {"TRUNCATE", CMD_TRUNCATE}, |
---|
[5f1e194] | 476 | {"UNITS", CMD_UNITS}, |
---|
[a4ae909] | 477 | {NULL, CMD_NULL} |
---|
[5f1e194] | 478 | }; |
---|
| 479 | |
---|
[fa42426] | 480 | /* masks for units which are length and angles respectively */ |
---|
| 481 | #define LEN_UMASK (BIT(UNITS_METRES) | BIT(UNITS_FEET) | BIT(UNITS_YARDS)) |
---|
| 482 | #define ANG_UMASK (BIT(UNITS_DEGS) | BIT(UNITS_GRADS) | BIT(UNITS_MINUTES)) |
---|
| 483 | |
---|
| 484 | /* ordering must be the same as the units enum */ |
---|
[85c0078] | 485 | const real factor_tab[] = { |
---|
[fa42426] | 486 | 1.0, METRES_PER_FOOT, (METRES_PER_FOOT*3.0), |
---|
[00b10c1] | 487 | (M_PI/180.0), (M_PI/180.0), (M_PI/200.0), 0.01, (M_PI/180.0/60.0) |
---|
[fa42426] | 488 | }; |
---|
| 489 | |
---|
[85c0078] | 490 | const int units_to_msgno[] = { |
---|
| 491 | /*m*/424, |
---|
[f516c1e] | 492 | /*′*/428, |
---|
[85c0078] | 493 | -1, /* yards */ |
---|
[e343cfc0] | 494 | /*°*/344, /* quadrants */ |
---|
[85c0078] | 495 | /*°*/344, |
---|
[85dcdcd] | 496 | /*ᵍ*/345, |
---|
[85c0078] | 497 | /*%*/96, |
---|
| 498 | -1 /* minutes */ |
---|
| 499 | }; |
---|
| 500 | |
---|
| 501 | int get_length_units(int quantity) { |
---|
| 502 | double factor = pcs->units[quantity]; |
---|
| 503 | if (fabs(factor - METRES_PER_FOOT) <= REAL_EPSILON || |
---|
| 504 | fabs(factor - METRES_PER_FOOT * 3.0) <= REAL_EPSILON) { |
---|
| 505 | return UNITS_FEET; |
---|
| 506 | } |
---|
| 507 | return UNITS_METRES; |
---|
| 508 | } |
---|
| 509 | |
---|
| 510 | int get_angle_units(int quantity) { |
---|
| 511 | double factor = pcs->units[quantity]; |
---|
| 512 | if (fabs(factor - M_PI / 200.0) <= REAL_EPSILON) { |
---|
| 513 | return UNITS_GRADS; |
---|
| 514 | } |
---|
| 515 | return UNITS_DEGS; |
---|
| 516 | } |
---|
| 517 | |
---|
[a420b49] | 518 | static int |
---|
[fa42426] | 519 | get_units(unsigned long qmask, bool percent_ok) |
---|
[a420b49] | 520 | { |
---|
[82919e07] | 521 | static const sztok utab[] = { |
---|
[5f1e194] | 522 | {"DEGREES", UNITS_DEGS }, |
---|
[a4ae909] | 523 | {"DEGS", UNITS_DEGS }, |
---|
| 524 | {"FEET", UNITS_FEET }, |
---|
| 525 | {"GRADS", UNITS_GRADS }, |
---|
| 526 | {"METERS", UNITS_METRES }, |
---|
| 527 | {"METRES", UNITS_METRES }, |
---|
| 528 | {"METRIC", UNITS_METRES }, |
---|
[70fa970] | 529 | {"MILS", UNITS_DEPRECATED_ALIAS_FOR_GRADS }, |
---|
[a4ae909] | 530 | {"MINUTES", UNITS_MINUTES }, |
---|
| 531 | {"PERCENT", UNITS_PERCENT }, |
---|
[5f1e194] | 532 | {"PERCENTAGE", UNITS_PERCENT }, |
---|
[00b10c1] | 533 | {"QUADRANTS", UNITS_QUADRANTS }, |
---|
[51951b9] | 534 | {"QUADS", UNITS_QUADRANTS }, |
---|
| 535 | {"YARDS", UNITS_YARDS }, |
---|
[a4ae909] | 536 | {NULL, UNITS_NULL } |
---|
[5f1e194] | 537 | }; |
---|
[3a33d12] | 538 | get_token(); |
---|
[e1cbc0d] | 539 | int units = match_tok(utab, TABSIZE(utab)); |
---|
[647407d] | 540 | if (units == UNITS_NULL) { |
---|
[caae6cd] | 541 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Unknown units “%s”*/35, |
---|
[0532954] | 542 | s_str(&token)); |
---|
[fa42426] | 543 | return UNITS_NULL; |
---|
| 544 | } |
---|
[70fa970] | 545 | /* Survex has long misdefined "mils" as an alias for "grads", of which |
---|
| 546 | * there are 400 in a circle. There are several definitions of "mils" |
---|
| 547 | * with a circle containing 2000π SI milliradians, 6400 NATO mils, 6000 |
---|
| 548 | * Warsaw Pact mils, and 6300 Swedish streck, and they aren't in common |
---|
| 549 | * use by cave surveyors, so we now just warn if mils are used. |
---|
| 550 | */ |
---|
| 551 | if (units == UNITS_DEPRECATED_ALIAS_FOR_GRADS) { |
---|
[caae6cd] | 552 | compile_diagnostic(DIAG_WARN|DIAG_TOKEN|DIAG_SKIP, |
---|
[70fa970] | 553 | /*Units “%s” are deprecated, assuming “grads” - see manual for details*/479, |
---|
[0532954] | 554 | s_str(&token)); |
---|
[70fa970] | 555 | units = UNITS_GRADS; |
---|
| 556 | } |
---|
[fa42426] | 557 | if (units == UNITS_PERCENT && percent_ok && |
---|
| 558 | !(qmask & ~(BIT(Q_GRADIENT)|BIT(Q_BACKGRADIENT)))) { |
---|
| 559 | return units; |
---|
| 560 | } |
---|
[00b10c1] | 561 | if (units == UNITS_QUADRANTS && |
---|
| 562 | !(qmask & ~(BIT(Q_BEARING)|BIT(Q_BACKBEARING)))) { |
---|
| 563 | return units; |
---|
| 564 | } |
---|
[fa42426] | 565 | if (((qmask & LEN_QMASK) && !TSTBIT(LEN_UMASK, units)) || |
---|
| 566 | ((qmask & ANG_QMASK) && !TSTBIT(ANG_UMASK, units))) { |
---|
[736f7df] | 567 | /* TRANSLATORS: Note: In English you talk about the *units* of a single |
---|
| 568 | * measurement, but the correct term in other languages may be singular. |
---|
| 569 | */ |
---|
[caae6cd] | 570 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Invalid units “%s” for quantity*/37, s_str(&token)); |
---|
[fa42426] | 571 | return UNITS_NULL; |
---|
[647407d] | 572 | } |
---|
[5f1e194] | 573 | return units; |
---|
[d1b1380] | 574 | } |
---|
| 575 | |
---|
| 576 | /* returns mask with bit x set to indicate quantity x specified */ |
---|
[67508f0] | 577 | static unsigned long |
---|
[46cb98f] | 578 | get_qlist(unsigned long mask_bad) |
---|
[a420b49] | 579 | { |
---|
[82919e07] | 580 | static const sztok qtab[] = { |
---|
[9b5d785] | 581 | {"ALTITUDE", Q_DZ }, |
---|
[b14f44f] | 582 | {"BACKBEARING", Q_BACKBEARING }, |
---|
| 583 | {"BACKCLINO", Q_BACKGRADIENT }, /* alternative name */ |
---|
| 584 | {"BACKCOMPASS", Q_BACKBEARING }, /* alternative name */ |
---|
| 585 | {"BACKGRADIENT", Q_BACKGRADIENT }, |
---|
[4f38f94] | 586 | {"BACKLENGTH", Q_BACKLENGTH }, |
---|
| 587 | {"BACKTAPE", Q_BACKLENGTH }, /* alternative name */ |
---|
[5f1e194] | 588 | {"BEARING", Q_BEARING }, |
---|
[ee05463] | 589 | {"CEILING", Q_UP }, /* alternative name */ |
---|
[a4ae909] | 590 | {"CLINO", Q_GRADIENT }, /* alternative name */ |
---|
[5f1e194] | 591 | {"COMPASS", Q_BEARING }, /* alternative name */ |
---|
[a4ae909] | 592 | {"COUNT", Q_COUNT }, |
---|
[5f1e194] | 593 | {"COUNTER", Q_COUNT }, /* alternative name */ |
---|
| 594 | {"DECLINATION", Q_DECLINATION }, |
---|
[a420b49] | 595 | {"DEFAULT", Q_DEFAULT }, /* not a real quantity... */ |
---|
[a4ae909] | 596 | {"DEPTH", Q_DEPTH }, |
---|
[ee05463] | 597 | {"DOWN", Q_DOWN }, |
---|
[a4ae909] | 598 | {"DX", Q_DX }, /* alternative name */ |
---|
| 599 | {"DY", Q_DY }, /* alternative name */ |
---|
| 600 | {"DZ", Q_DZ }, /* alternative name */ |
---|
| 601 | {"EASTING", Q_DX }, |
---|
[ee05463] | 602 | {"FLOOR", Q_DOWN }, /* alternative name */ |
---|
[5f1e194] | 603 | {"GRADIENT", Q_GRADIENT }, |
---|
[ee05463] | 604 | {"LEFT", Q_LEFT }, |
---|
[5f1e194] | 605 | {"LENGTH", Q_LENGTH }, |
---|
[a4ae909] | 606 | {"LEVEL", Q_LEVEL}, |
---|
[9b5d785] | 607 | {"NORTHING", Q_DY }, |
---|
[a4ae909] | 608 | {"PLUMB", Q_PLUMB}, |
---|
[5f1e194] | 609 | {"POSITION", Q_POS }, |
---|
[ee05463] | 610 | {"RIGHT", Q_RIGHT }, |
---|
[a4ae909] | 611 | {"TAPE", Q_LENGTH }, /* alternative name */ |
---|
[ee05463] | 612 | {"UP", Q_UP }, |
---|
[a4ae909] | 613 | {NULL, Q_NULL } |
---|
[5f1e194] | 614 | }; |
---|
[67508f0] | 615 | unsigned long qmask = 0; |
---|
[5f1e194] | 616 | int tok; |
---|
[3a33d12] | 617 | filepos fp; |
---|
| 618 | |
---|
[a420b49] | 619 | while (1) { |
---|
[3a33d12] | 620 | get_pos(&fp); |
---|
[e1cbc0d] | 621 | get_token_legacy(); |
---|
[5f1e194] | 622 | tok = match_tok(qtab, TABSIZE(qtab)); |
---|
[da96015] | 623 | if (tok == Q_DEFAULT && !(mask_bad & BIT(Q_DEFAULT))) { |
---|
| 624 | /* Only recognise DEFAULT if it is the first quantity, and then don't |
---|
| 625 | * look for any more. */ |
---|
| 626 | if (qmask == 0) |
---|
| 627 | return BIT(Q_DEFAULT); |
---|
| 628 | break; |
---|
| 629 | } |
---|
[5f1e194] | 630 | /* bail out if we reach the table end with no match */ |
---|
| 631 | if (tok == Q_NULL) break; |
---|
| 632 | qmask |= BIT(tok); |
---|
[da96015] | 633 | if (qmask & mask_bad) { |
---|
[caae6cd] | 634 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Unknown instrument “%s”*/39, s_str(&token)); |
---|
[46cb98f] | 635 | return 0; |
---|
| 636 | } |
---|
[5f1e194] | 637 | } |
---|
[3a33d12] | 638 | |
---|
[647407d] | 639 | if (qmask == 0) { |
---|
[36efb03] | 640 | /* TRANSLATORS: A "quantity" is something measured like "LENGTH", |
---|
| 641 | * "BEARING", "ALTITUDE", etc. */ |
---|
[caae6cd] | 642 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Unknown quantity “%s”*/34, s_str(&token)); |
---|
[3a33d12] | 643 | } else { |
---|
| 644 | set_pos(&fp); |
---|
[647407d] | 645 | } |
---|
[3a33d12] | 646 | |
---|
[5f1e194] | 647 | return qmask; |
---|
[d1b1380] | 648 | } |
---|
| 649 | |
---|
| 650 | #define SPECIAL_UNKNOWN 0 |
---|
[a420b49] | 651 | static void |
---|
[eb18f4d] | 652 | cmd_set(void) |
---|
[a420b49] | 653 | { |
---|
[82919e07] | 654 | static const sztok chartab[] = { |
---|
[5f1e194] | 655 | {"BLANK", SPECIAL_BLANK }, |
---|
[3593388] | 656 | /*FIXME {"CLOSE", SPECIAL_CLOSE }, */ |
---|
[5f1e194] | 657 | {"COMMENT", SPECIAL_COMMENT }, |
---|
| 658 | {"DECIMAL", SPECIAL_DECIMAL }, |
---|
| 659 | {"EOL", SPECIAL_EOL }, /* EOL won't work well */ |
---|
| 660 | {"KEYWORD", SPECIAL_KEYWORD }, |
---|
| 661 | {"MINUS", SPECIAL_MINUS }, |
---|
| 662 | {"NAMES", SPECIAL_NAMES }, |
---|
| 663 | {"OMIT", SPECIAL_OMIT }, |
---|
[3593388] | 664 | /*FIXME {"OPEN", SPECIAL_OPEN }, */ |
---|
[5f1e194] | 665 | {"PLUS", SPECIAL_PLUS }, |
---|
[7f1ab95] | 666 | #ifndef NO_DEPRECATED |
---|
[5f1e194] | 667 | {"ROOT", SPECIAL_ROOT }, |
---|
[7f1ab95] | 668 | #endif |
---|
[5f1e194] | 669 | {"SEPARATOR", SPECIAL_SEPARATOR }, |
---|
[a4ae909] | 670 | {NULL, SPECIAL_UNKNOWN } |
---|
[5f1e194] | 671 | }; |
---|
| 672 | int i; |
---|
[b15eeda] | 673 | |
---|
[a420b49] | 674 | get_token(); |
---|
[e1cbc0d] | 675 | int mask = match_tok(chartab, TABSIZE(chartab)); |
---|
[b15eeda] | 676 | |
---|
[f74d0cb] | 677 | if (mask == SPECIAL_UNKNOWN) { |
---|
[caae6cd] | 678 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Unknown character class “%s”*/42, |
---|
[0532954] | 679 | s_str(&token)); |
---|
[5f1e194] | 680 | return; |
---|
| 681 | } |
---|
[b15eeda] | 682 | |
---|
[7f1ab95] | 683 | #ifndef NO_DEPRECATED |
---|
[c86cc71] | 684 | if (mask == SPECIAL_ROOT) { |
---|
| 685 | if (root_depr_count < 5) { |
---|
[736f7df] | 686 | /* TRANSLATORS: Use of the ROOT character (which is "\" by default) is |
---|
| 687 | * deprecated, so this error would be generated by: |
---|
[b49ac56] | 688 | * |
---|
[736f7df] | 689 | * *equate \foo.7 1 |
---|
[b49ac56] | 690 | * |
---|
[736f7df] | 691 | * If you're unsure what "deprecated" means, see: |
---|
[2c1c52e] | 692 | * https://en.wikipedia.org/wiki/Deprecation */ |
---|
[caae6cd] | 693 | compile_diagnostic(DIAG_WARN|DIAG_TOKEN, /*ROOT is deprecated*/25); |
---|
[c86cc71] | 694 | if (++root_depr_count == 5) |
---|
[736f7df] | 695 | /* TRANSLATORS: If you're unsure what "deprecated" means, see: |
---|
[2c1c52e] | 696 | * https://en.wikipedia.org/wiki/Deprecation */ |
---|
[64544daf] | 697 | compile_diagnostic(DIAG_INFO, /*Further uses of this deprecated feature will not be reported*/95); |
---|
[c86cc71] | 698 | } |
---|
| 699 | } |
---|
[7f1ab95] | 700 | #endif |
---|
[b15eeda] | 701 | |
---|
[5f1e194] | 702 | /* if we're currently using an inherited translation table, allocate a new |
---|
| 703 | * table, and copy old one into it */ |
---|
| 704 | if (pcs->next && pcs->next->Translate == pcs->Translate) { |
---|
| 705 | short *p; |
---|
[ae917b96] | 706 | p = ((short*)osmalloc(sizeof(short) * 257)) + 1; |
---|
[5f1e194] | 707 | memcpy(p - 1, pcs->Translate - 1, sizeof(short) * 257); |
---|
| 708 | pcs->Translate = p; |
---|
| 709 | } |
---|
[11f9067] | 710 | |
---|
| 711 | skipblanks(); |
---|
| 712 | |
---|
[5f1e194] | 713 | /* clear this flag for all non-alphanums */ |
---|
[a420b49] | 714 | for (i = 0; i < 256; i++) |
---|
| 715 | if (!isalnum(i)) pcs->Translate[i] &= ~mask; |
---|
[11f9067] | 716 | |
---|
[5f1e194] | 717 | /* now set this flag for all specified chars */ |
---|
| 718 | while (!isEol(ch)) { |
---|
[5d59477] | 719 | int char_to_set; |
---|
[11f9067] | 720 | if (!isalnum(ch)) { |
---|
[5d59477] | 721 | char_to_set = ch; |
---|
[11f9067] | 722 | } else if (tolower(ch) == 'x') { |
---|
| 723 | int hex; |
---|
| 724 | filepos fp; |
---|
| 725 | get_pos(&fp); |
---|
| 726 | nextch(); |
---|
| 727 | if (!isxdigit(ch)) { |
---|
| 728 | set_pos(&fp); |
---|
| 729 | break; |
---|
| 730 | } |
---|
| 731 | hex = isdigit(ch) ? ch - '0' : tolower(ch) - 'a'; |
---|
| 732 | nextch(); |
---|
| 733 | if (!isxdigit(ch)) { |
---|
| 734 | set_pos(&fp); |
---|
| 735 | break; |
---|
| 736 | } |
---|
| 737 | hex = hex << 4 | (isdigit(ch) ? ch - '0' : tolower(ch) - 'a'); |
---|
[5d59477] | 738 | char_to_set = hex; |
---|
[11f9067] | 739 | } else { |
---|
| 740 | break; |
---|
| 741 | } |
---|
[5d59477] | 742 | pcs->Translate[char_to_set] |= mask; |
---|
| 743 | separator_map[char_to_set] |= mask; |
---|
[5f1e194] | 744 | nextch(); |
---|
| 745 | } |
---|
[5d59477] | 746 | |
---|
| 747 | output_separator = find_output_separator(); |
---|
| 748 | } |
---|
| 749 | |
---|
| 750 | void |
---|
| 751 | update_output_separator(void) |
---|
| 752 | { |
---|
| 753 | output_separator = find_output_separator(); |
---|
[d1b1380] | 754 | } |
---|
| 755 | |
---|
[4d9eecd] | 756 | static void |
---|
[23e64c14] | 757 | check_reentry(prefix *survey, const filepos* fpos_ptr) |
---|
[4d9eecd] | 758 | { |
---|
[91912b4] | 759 | /* Don't try to check "*prefix \" or "*begin \" */ |
---|
[613028c] | 760 | if (!survey->up) return; |
---|
| 761 | if (TSTBIT(survey->sflags, SFLAGS_PREFIX_ENTERED)) { |
---|
[15696f3] | 762 | static int reenter_depr_count = 0; |
---|
[23e64c14] | 763 | filepos fp_tmp; |
---|
[c86cc71] | 764 | |
---|
[e1a66da] | 765 | if (reenter_depr_count >= 5) |
---|
| 766 | return; |
---|
| 767 | |
---|
[23e64c14] | 768 | get_pos(&fp_tmp); |
---|
| 769 | set_pos(fpos_ptr); |
---|
[1c507cf] | 770 | /* TRANSLATORS: The first of two warnings given when a survey which has |
---|
| 771 | * already been completed is reentered. This example file crawl.svx: |
---|
[e1a66da] | 772 | * |
---|
[5a9e47c] | 773 | * *begin crawl ; <- second warning here |
---|
| 774 | * 1 2 9.45 234 -01 |
---|
[e1a66da] | 775 | * *end crawl |
---|
[5a9e47c] | 776 | * *begin crawl ; <- first warning here |
---|
[e1a66da] | 777 | * 2 3 7.67 223 -03 |
---|
| 778 | * *end crawl |
---|
[1c507cf] | 779 | * |
---|
| 780 | * Would lead to: |
---|
| 781 | * |
---|
[b0a2ddb] | 782 | * crawl.svx:4:8: warning: Reentering an existing survey is deprecated |
---|
| 783 | * crawl.svx:1: info: Originally entered here |
---|
[1c507cf] | 784 | * |
---|
[e1a66da] | 785 | * If you're unsure what "deprecated" means, see: |
---|
[2c1c52e] | 786 | * https://en.wikipedia.org/wiki/Deprecation */ |
---|
[d0be687d] | 787 | compile_diagnostic(DIAG_WARN|DIAG_WORD, /*Reentering an existing survey is deprecated*/29); |
---|
[23e64c14] | 788 | set_pos(&fp_tmp); |
---|
[1c507cf] | 789 | /* TRANSLATORS: The second of two warnings given when a survey which has |
---|
| 790 | * already been completed is reentered. This example file crawl.svx: |
---|
| 791 | * |
---|
[9c25fac] | 792 | * *begin crawl ; <- second warning here |
---|
| 793 | * 1 2 9.45 234 -01 |
---|
[1c507cf] | 794 | * *end crawl |
---|
[9c25fac] | 795 | * *begin crawl ; <- first warning here |
---|
[1c507cf] | 796 | * 2 3 7.67 223 -03 |
---|
| 797 | * *end crawl |
---|
| 798 | * |
---|
| 799 | * Would lead to: |
---|
| 800 | * |
---|
[b0a2ddb] | 801 | * crawl.svx:4:8: warning: Reentering an existing survey is deprecated |
---|
| 802 | * crawl.svx:1: info: Originally entered here |
---|
[1c507cf] | 803 | * |
---|
| 804 | * If you're unsure what "deprecated" means, see: |
---|
[2c1c52e] | 805 | * https://en.wikipedia.org/wiki/Deprecation */ |
---|
[b0a2ddb] | 806 | compile_diagnostic_pfx(DIAG_INFO, survey, /*Originally entered here*/30); |
---|
[1c507cf] | 807 | if (++reenter_depr_count == 5) { |
---|
| 808 | /* After we've warned about 5 uses of the same deprecated feature, we |
---|
| 809 | * give up for the rest of the current processing run. |
---|
| 810 | * |
---|
| 811 | * If you're unsure what "deprecated" means, see: |
---|
[2c1c52e] | 812 | * https://en.wikipedia.org/wiki/Deprecation */ |
---|
[64544daf] | 813 | compile_diagnostic(DIAG_INFO, /*Further uses of this deprecated feature will not be reported*/95); |
---|
[1c507cf] | 814 | } |
---|
[4d9eecd] | 815 | } else { |
---|
[613028c] | 816 | survey->sflags |= BIT(SFLAGS_PREFIX_ENTERED); |
---|
| 817 | survey->filename = file.filename; |
---|
| 818 | survey->line = file.line; |
---|
[4d9eecd] | 819 | } |
---|
| 820 | } |
---|
| 821 | |
---|
[7f1ab95] | 822 | #ifndef NO_DEPRECATED |
---|
[a420b49] | 823 | static void |
---|
[eb18f4d] | 824 | cmd_prefix(void) |
---|
[a420b49] | 825 | { |
---|
[c86cc71] | 826 | static int prefix_depr_count = 0; |
---|
[613028c] | 827 | prefix *survey; |
---|
[23e64c14] | 828 | filepos fp; |
---|
[91912b4] | 829 | /* Issue warning first, so "*prefix \" warns first that *prefix is |
---|
| 830 | * deprecated and then that ROOT is... |
---|
| 831 | */ |
---|
[c86cc71] | 832 | if (prefix_depr_count < 5) { |
---|
[736f7df] | 833 | /* TRANSLATORS: If you're unsure what "deprecated" means, see: |
---|
[2c1c52e] | 834 | * https://en.wikipedia.org/wiki/Deprecation */ |
---|
[7962c9d] | 835 | compile_diagnostic(DIAG_WARN|DIAG_TOKEN, /**prefix is deprecated - use *begin and *end instead*/109); |
---|
[c86cc71] | 836 | if (++prefix_depr_count == 5) |
---|
[64544daf] | 837 | compile_diagnostic(DIAG_INFO, /*Further uses of this deprecated feature will not be reported*/95); |
---|
[c86cc71] | 838 | } |
---|
[23e64c14] | 839 | get_pos(&fp); |
---|
[613028c] | 840 | survey = read_prefix(PFX_SURVEY|PFX_ALLOW_ROOT); |
---|
| 841 | pcs->Prefix = survey; |
---|
[23e64c14] | 842 | check_reentry(survey, &fp); |
---|
[d1b1380] | 843 | } |
---|
[7f1ab95] | 844 | #endif |
---|
[d1b1380] | 845 | |
---|
[dcbcae0] | 846 | static void |
---|
| 847 | cmd_alias(void) |
---|
| 848 | { |
---|
[45dcea2] | 849 | /* Currently only two forms are supported: |
---|
[dcbcae0] | 850 | * *alias station - .. |
---|
[45dcea2] | 851 | * *alias station - |
---|
[dcbcae0] | 852 | */ |
---|
| 853 | get_token(); |
---|
[4772e46] | 854 | if (!S_EQ(&uctoken, "STATION")) { |
---|
| 855 | compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_TOKEN, /*Bad *alias command*/397); |
---|
| 856 | return; |
---|
| 857 | } |
---|
[dcbcae0] | 858 | get_word(); |
---|
[2f246b0] | 859 | if (!S_EQ(&token, "-")) |
---|
[4772e46] | 860 | goto bad_word; |
---|
[dcbcae0] | 861 | get_word(); |
---|
[2f246b0] | 862 | if (!s_empty(&token) && !S_EQ(&token, "..")) |
---|
[4772e46] | 863 | goto bad_word; |
---|
[2f246b0] | 864 | pcs->dash_for_anon_wall_station = !s_empty(&token); |
---|
[dcbcae0] | 865 | return; |
---|
[4772e46] | 866 | bad_word: |
---|
[2aa37a8] | 867 | compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_TOKEN, /*Bad *alias command*/397); |
---|
[dcbcae0] | 868 | } |
---|
| 869 | |
---|
[a420b49] | 870 | static void |
---|
[eb18f4d] | 871 | cmd_begin(void) |
---|
[a420b49] | 872 | { |
---|
[5f1e194] | 873 | settings *pcsNew; |
---|
[4be360f] | 874 | |
---|
[5f1e194] | 875 | pcsNew = osnew(settings); |
---|
| 876 | *pcsNew = *pcs; /* copy contents */ |
---|
[47c7a94] | 877 | pcsNew->begin_lineno = file.line; |
---|
[94aeeaf] | 878 | pcsNew->begin_lpos = file.lpos; |
---|
[5f1e194] | 879 | pcsNew->next = pcs; |
---|
| 880 | pcs = pcsNew; |
---|
[4be360f] | 881 | |
---|
[c230d677] | 882 | skipblanks(); |
---|
[3be21a9] | 883 | pcs->begin_survey = NULL; |
---|
[94aeeaf] | 884 | pcs->begin_col = 0; |
---|
[3be21a9] | 885 | if (!isEol(ch) && !isComm(ch)) { |
---|
[23e64c14] | 886 | filepos fp; |
---|
| 887 | prefix *survey; |
---|
| 888 | get_pos(&fp); |
---|
[94aeeaf] | 889 | int begin_col = fp.offset - file.lpos; |
---|
[23e64c14] | 890 | survey = read_prefix(PFX_SURVEY|PFX_ALLOW_ROOT|PFX_WARN_SEPARATOR); |
---|
[94aeeaf] | 891 | // read_prefix() might fail and longjmp() so only set begin_col if |
---|
| 892 | // it succeeds. |
---|
| 893 | pcs->begin_col = begin_col; |
---|
[c230d677] | 894 | pcs->begin_survey = survey; |
---|
[613028c] | 895 | pcs->Prefix = survey; |
---|
[23e64c14] | 896 | check_reentry(survey, &fp); |
---|
[63d4f07] | 897 | f_export_ok = true; |
---|
[d53b0f5] | 898 | } |
---|
[647407d] | 899 | } |
---|
| 900 | |
---|
[d624d86] | 901 | void |
---|
[da9163b] | 902 | invalidate_pj_cached(void) |
---|
| 903 | { |
---|
| 904 | /* Invalidate the cached PJ. */ |
---|
| 905 | if (pj_cached) { |
---|
| 906 | proj_destroy(pj_cached); |
---|
| 907 | pj_cached = NULL; |
---|
| 908 | } |
---|
| 909 | } |
---|
| 910 | |
---|
[37d6b84] | 911 | void |
---|
| 912 | report_declination(settings *p) |
---|
[da9163b] | 913 | { |
---|
[37d6b84] | 914 | if (p->min_declination <= p->max_declination) { |
---|
| 915 | int y, m, d; |
---|
| 916 | char range[128]; |
---|
| 917 | const char* deg_sign = msg(/*°*/344); |
---|
| 918 | ymd_from_days_since_1900(p->min_declination_days, &y, &m, &d); |
---|
| 919 | snprintf(range, sizeof(range), |
---|
| 920 | "%.1f%s @ %04d-%02d-%02d", |
---|
| 921 | deg(p->min_declination), deg_sign, y, m, d); |
---|
| 922 | if (p->min_declination_days != p->max_declination_days) { |
---|
| 923 | size_t len = strlen(range); |
---|
| 924 | ymd_from_days_since_1900(p->max_declination_days, &y, &m, &d); |
---|
| 925 | snprintf(range + len, sizeof(range) - len, |
---|
| 926 | " / %.1f%s @ %04d-%02d-%02d", |
---|
| 927 | deg(p->max_declination), deg_sign, y, m, d); |
---|
| 928 | } |
---|
| 929 | /* TRANSLATORS: This message gives information about the range of |
---|
| 930 | * declination values and the grid convergence value calculated for |
---|
| 931 | * each "*declination auto ..." command. |
---|
| 932 | * |
---|
| 933 | * The first %s will be replaced by the declination range (or single |
---|
| 934 | * value), and %.1f%s by the grid convergence angle. |
---|
| 935 | */ |
---|
[501dd27] | 936 | compile_diagnostic_at(DIAG_INFO, p->dec_filename, p->dec_line, |
---|
[37d6b84] | 937 | /*Declination: %s, grid convergence: %.1f%s*/484, |
---|
| 938 | range, |
---|
| 939 | deg(p->convergence), deg_sign); |
---|
| 940 | PUTC(' ', STDERR); |
---|
| 941 | fputs(p->dec_context, STDERR); |
---|
| 942 | fputnl(STDERR); |
---|
[725d3b1] | 943 | if (p->next && p->dec_context != p->next->dec_context) |
---|
| 944 | free(p->dec_context); |
---|
[37d6b84] | 945 | p->dec_context = NULL; |
---|
[c1fe79a] | 946 | p->min_declination = HUGE_VAL; |
---|
| 947 | p->max_declination = -HUGE_VAL; |
---|
[37d6b84] | 948 | } |
---|
| 949 | } |
---|
[da9163b] | 950 | |
---|
[69a9e01] | 951 | void |
---|
| 952 | set_declination_location(real x, real y, real z, const char *proj_str) |
---|
| 953 | { |
---|
| 954 | /* Convert to WGS84 lat long. */ |
---|
| 955 | PJ *transform = proj_create_crs_to_crs(PJ_DEFAULT_CTX, |
---|
| 956 | proj_str, |
---|
| 957 | WGS84_DATUM_STRING, |
---|
| 958 | NULL); |
---|
| 959 | if (transform) { |
---|
| 960 | /* Normalise the output order so x is longitude and y latitude - by |
---|
| 961 | * default new PROJ has them switched for EPSG:4326 which just seems |
---|
| 962 | * confusing. |
---|
| 963 | */ |
---|
| 964 | PJ* pj_norm = proj_normalize_for_visualization(PJ_DEFAULT_CTX, |
---|
| 965 | transform); |
---|
| 966 | proj_destroy(transform); |
---|
| 967 | transform = pj_norm; |
---|
| 968 | } |
---|
| 969 | |
---|
| 970 | if (proj_angular_input(transform, PJ_FWD)) { |
---|
| 971 | /* Input coordinate system expects radians. */ |
---|
| 972 | x = rad(x); |
---|
| 973 | y = rad(y); |
---|
| 974 | } |
---|
| 975 | |
---|
| 976 | PJ_COORD coord = {{x, y, z, HUGE_VAL}}; |
---|
| 977 | coord = proj_trans(transform, PJ_FWD, coord); |
---|
| 978 | x = coord.xyzt.x; |
---|
| 979 | y = coord.xyzt.y; |
---|
| 980 | z = coord.xyzt.z; |
---|
| 981 | |
---|
| 982 | if (x == HUGE_VAL || y == HUGE_VAL || z == HUGE_VAL) { |
---|
| 983 | compile_diagnostic(DIAG_ERR, /*Failed to convert coordinates: %s*/436, |
---|
[d9d8f21] | 984 | proj_context_errno_string(PJ_DEFAULT_CTX, |
---|
| 985 | proj_errno(transform))); |
---|
[69a9e01] | 986 | /* Set dummy values which are finite. */ |
---|
| 987 | x = y = z = 0; |
---|
| 988 | } |
---|
| 989 | proj_destroy(transform); |
---|
| 990 | |
---|
| 991 | report_declination(pcs); |
---|
| 992 | |
---|
| 993 | double lon = rad(x); |
---|
| 994 | double lat = rad(y); |
---|
| 995 | pcs->z[Q_DECLINATION] = HUGE_REAL; |
---|
| 996 | pcs->dec_lat = lat; |
---|
| 997 | pcs->dec_lon = lon; |
---|
| 998 | pcs->dec_alt = z; |
---|
| 999 | pcs->dec_filename = file.filename; |
---|
| 1000 | pcs->dec_line = file.line; |
---|
| 1001 | pcs->dec_context = grab_line(); |
---|
| 1002 | /* Invalidate cached declination. */ |
---|
| 1003 | pcs->declination = HUGE_REAL; |
---|
[abe7192] | 1004 | /* Invalidate cached grid convergence values. */ |
---|
[69a9e01] | 1005 | pcs->convergence = HUGE_REAL; |
---|
[abe7192] | 1006 | pcs->input_convergence = HUGE_REAL; |
---|
[69a9e01] | 1007 | } |
---|
| 1008 | |
---|
[37d6b84] | 1009 | void |
---|
| 1010 | pop_settings(void) |
---|
| 1011 | { |
---|
| 1012 | settings * p = pcs; |
---|
| 1013 | pcs = pcs->next; |
---|
| 1014 | |
---|
| 1015 | SVX_ASSERT(pcs); |
---|
| 1016 | |
---|
| 1017 | if (pcs->dec_lat != p->dec_lat || |
---|
| 1018 | pcs->dec_lon != p->dec_lon || |
---|
| 1019 | pcs->dec_alt != p->dec_alt) { |
---|
| 1020 | report_declination(p); |
---|
| 1021 | } else { |
---|
| 1022 | pcs->min_declination_days = p->min_declination_days; |
---|
| 1023 | pcs->max_declination_days = p->max_declination_days; |
---|
| 1024 | pcs->min_declination = p->min_declination; |
---|
| 1025 | pcs->max_declination = p->max_declination; |
---|
[4bea0f8] | 1026 | pcs->convergence = p->convergence; |
---|
[37d6b84] | 1027 | } |
---|
| 1028 | |
---|
| 1029 | if (p->proj_str != pcs->proj_str) { |
---|
| 1030 | if (!p->proj_str || !pcs->proj_str || |
---|
| 1031 | strcmp(p->proj_str, pcs->proj_str) != 0) { |
---|
| 1032 | invalidate_pj_cached(); |
---|
| 1033 | } |
---|
| 1034 | /* free proj_str if not used by parent */ |
---|
[ae917b96] | 1035 | free(p->proj_str); |
---|
[37d6b84] | 1036 | } |
---|
[cb3d1e2] | 1037 | |
---|
[37d6b84] | 1038 | /* don't free default ordering or ordering used by parent */ |
---|
| 1039 | if (p->ordering != default_order && p->ordering != pcs->ordering) |
---|
[ae917b96] | 1040 | free((reading*)p->ordering); |
---|
[647407d] | 1041 | |
---|
[37d6b84] | 1042 | /* free Translate if not used by parent */ |
---|
| 1043 | if (p->Translate != pcs->Translate) |
---|
[ae917b96] | 1044 | free(p->Translate - 1); |
---|
[b5a3219] | 1045 | |
---|
[37d6b84] | 1046 | /* free meta if not used by parent, or in this block */ |
---|
| 1047 | if (p->meta && p->meta != pcs->meta && p->meta->ref_count == 0) |
---|
[ae917b96] | 1048 | free(p->meta); |
---|
[37d6b84] | 1049 | |
---|
[ae917b96] | 1050 | free(p); |
---|
[d1b1380] | 1051 | } |
---|
| 1052 | |
---|
[4be360f] | 1053 | static void |
---|
[eb18f4d] | 1054 | cmd_end(void) |
---|
[a420b49] | 1055 | { |
---|
[9f55538] | 1056 | filepos fp; |
---|
[4be360f] | 1057 | |
---|
[94aeeaf] | 1058 | int begin_lineno = pcs->begin_lineno; |
---|
| 1059 | if (begin_lineno == 0) { |
---|
[da9163b] | 1060 | if (pcs->next == NULL) { |
---|
[4be360f] | 1061 | /* more ENDs than BEGINs */ |
---|
[725d3b1] | 1062 | /* TRANSLATORS: %s is replaced with e.g. BEGIN or .BOOK or #[ */ |
---|
| 1063 | compile_diagnostic(DIAG_ERR|DIAG_SKIP, /*No matching %s*/192, "BEGIN"); |
---|
[4be360f] | 1064 | } else { |
---|
[944153c] | 1065 | /* TRANSLATORS: %s and %s are replaced with e.g. BEGIN and END |
---|
| 1066 | * or END and BEGIN or #[ and #] */ |
---|
| 1067 | compile_diagnostic(DIAG_ERR|DIAG_SKIP, |
---|
| 1068 | /*%s with no matching %s in this file*/23, "END", "BEGIN"); |
---|
[4be360f] | 1069 | } |
---|
| 1070 | return; |
---|
| 1071 | } |
---|
| 1072 | |
---|
[94aeeaf] | 1073 | prefix *begin_survey = pcs->begin_survey; |
---|
| 1074 | long begin_lpos = pcs->begin_lpos; |
---|
| 1075 | int begin_col = pcs->begin_col; |
---|
[647407d] | 1076 | |
---|
[da9163b] | 1077 | pop_settings(); |
---|
[d1b1380] | 1078 | |
---|
[84c60fc] | 1079 | /* note need to read using root *before* BEGIN */ |
---|
[94aeeaf] | 1080 | prefix *survey = NULL; |
---|
[3916384] | 1081 | skipblanks(); |
---|
[94aeeaf] | 1082 | if (!isEol(ch) && !isComm(ch)) { |
---|
[9f55538] | 1083 | get_pos(&fp); |
---|
[3916384] | 1084 | survey = read_prefix(PFX_SURVEY|PFX_ALLOW_ROOT); |
---|
| 1085 | } |
---|
| 1086 | |
---|
[613028c] | 1087 | if (survey != begin_survey) { |
---|
[94aeeaf] | 1088 | filepos fp_save; |
---|
| 1089 | get_pos(&fp_save); |
---|
[613028c] | 1090 | if (survey) { |
---|
[9f55538] | 1091 | set_pos(&fp); |
---|
[613028c] | 1092 | if (!begin_survey) { |
---|
| 1093 | /* TRANSLATORS: Used when a BEGIN command has no survey, but the |
---|
| 1094 | * END command does, e.g.: |
---|
[736f7df] | 1095 | * |
---|
| 1096 | * *begin |
---|
| 1097 | * 1 2 10.00 178 -01 |
---|
| 1098 | * *end entrance <--[Message given here] */ |
---|
[d0be687d] | 1099 | compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Matching BEGIN command has no survey name*/36); |
---|
[647407d] | 1100 | } else { |
---|
[613028c] | 1101 | /* TRANSLATORS: *BEGIN <survey> and *END <survey> should have the |
---|
| 1102 | * same <survey> if it’s given at all */ |
---|
[d0be687d] | 1103 | compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Survey name doesn’t match BEGIN*/193); |
---|
[647407d] | 1104 | } |
---|
[a420b49] | 1105 | } else { |
---|
[613028c] | 1106 | /* TRANSLATORS: Used when a BEGIN command has a survey name, but the |
---|
| 1107 | * END command omits it, e.g.: |
---|
| 1108 | * |
---|
| 1109 | * *begin entrance |
---|
| 1110 | * 1 2 10.00 178 -01 |
---|
| 1111 | * *end <--[Message given here] */ |
---|
[cab0f26] | 1112 | compile_diagnostic(DIAG_WARN|DIAG_COL, /*Survey name omitted from END*/194); |
---|
[a420b49] | 1113 | } |
---|
[94aeeaf] | 1114 | parse file_save = file; |
---|
| 1115 | file.line = begin_lineno; |
---|
| 1116 | file.lpos = begin_lpos; |
---|
| 1117 | int word_flag = 0; |
---|
| 1118 | if (begin_col) { |
---|
| 1119 | word_flag = DIAG_WORD; |
---|
| 1120 | fseek(file.fh, begin_lpos + begin_col - 1, SEEK_SET); |
---|
| 1121 | nextch(); |
---|
| 1122 | } |
---|
| 1123 | compile_diagnostic(DIAG_INFO|word_flag, /*Corresponding %s was here*/22, "BEGIN"); |
---|
| 1124 | file = file_save; |
---|
| 1125 | set_pos(&fp_save); |
---|
[5f1e194] | 1126 | } |
---|
| 1127 | } |
---|
[d1b1380] | 1128 | |
---|
[dfb4240] | 1129 | static void |
---|
| 1130 | cmd_entrance(void) |
---|
| 1131 | { |
---|
[c458cf7] | 1132 | prefix *pfx = read_prefix(PFX_STATION); |
---|
[e315359] | 1133 | pfx->sflags |= BIT(SFLAGS_ENTRANCE); |
---|
| 1134 | pfx->sflags &= ~BIT(SFLAGS_UNUSED_FIXED_POINT); |
---|
[dfb4240] | 1135 | } |
---|
| 1136 | |
---|
[56db37f] | 1137 | static const prefix * first_fix_name = NULL; |
---|
| 1138 | static const char * first_fix_filename; |
---|
| 1139 | static unsigned first_fix_line; |
---|
[216ada0] | 1140 | |
---|
[a420b49] | 1141 | static void |
---|
[eb18f4d] | 1142 | cmd_fix(void) |
---|
[a420b49] | 1143 | { |
---|
[216ada0] | 1144 | static prefix *name_omit_already = NULL; |
---|
[31699b54] | 1145 | static const char * name_omit_already_filename = NULL; |
---|
| 1146 | static unsigned int name_omit_already_line; |
---|
[725d3b1] | 1147 | PJ_COORD coord; |
---|
[58edecc] | 1148 | filepos fp_stn, fp; |
---|
[5f1e194] | 1149 | |
---|
[58edecc] | 1150 | get_pos(&fp_stn); |
---|
[725d3b1] | 1151 | prefix *fix_name = read_prefix(PFX_STATION|PFX_ALLOW_ROOT); |
---|
[5f1e194] | 1152 | |
---|
[c80bd34] | 1153 | get_pos(&fp); |
---|
[e1cbc0d] | 1154 | get_token_legacy(); |
---|
[72f617b] | 1155 | bool reference = S_EQ(&uctoken, "REFERENCE"); |
---|
| 1156 | if (reference) { |
---|
[05b9de76] | 1157 | do_legacy_token_warning(); |
---|
[c80bd34] | 1158 | } else { |
---|
[0532954] | 1159 | if (!s_empty(&uctoken)) set_pos(&fp); |
---|
[647407d] | 1160 | } |
---|
[c80bd34] | 1161 | |
---|
[72f617b] | 1162 | // If `REFERENCE` is specified the coordinates can't be omitted. |
---|
| 1163 | coord.v[0] = read_numeric(!reference); |
---|
[725d3b1] | 1164 | if (coord.v[0] == HUGE_REAL) { |
---|
[2aa2f3f] | 1165 | /* If the end of the line isn't blank, read a number after all to |
---|
| 1166 | * get a more helpful error message */ |
---|
[725d3b1] | 1167 | if (!isEol(ch) && !isComm(ch)) coord.v[0] = read_numeric(false); |
---|
[2aa2f3f] | 1168 | } |
---|
[725d3b1] | 1169 | if (coord.v[0] == HUGE_REAL) { |
---|
[b39e24a] | 1170 | if (pcs->proj_str || proj_str_out) { |
---|
[301a7d4] | 1171 | compile_diagnostic(DIAG_ERR|DIAG_COL|DIAG_SKIP, /*Coordinates can't be omitted when coordinate system has been specified*/439); |
---|
[a4f1d96] | 1172 | return; |
---|
| 1173 | } |
---|
| 1174 | |
---|
[6727d64] | 1175 | if (fix_name == name_omit_already) { |
---|
[301a7d4] | 1176 | compile_diagnostic(DIAG_WARN|DIAG_COL, /*Same station fixed twice with no coordinates*/61); |
---|
[5f1e194] | 1177 | return; |
---|
| 1178 | } |
---|
[6727d64] | 1179 | |
---|
| 1180 | if (name_omit_already) { |
---|
[0ef9ce4] | 1181 | /* TRANSLATORS: Emitted after second and subsequent "FIX" command |
---|
| 1182 | * with no coordinates. |
---|
[6727d64] | 1183 | */ |
---|
[501dd27] | 1184 | compile_diagnostic_at(DIAG_ERR, |
---|
[cab0f26] | 1185 | name_omit_already_filename, |
---|
| 1186 | name_omit_already_line, |
---|
| 1187 | /*Already had FIX command with no coordinates for station “%s”*/441, |
---|
| 1188 | sprint_prefix(name_omit_already)); |
---|
[6727d64] | 1189 | } else { |
---|
[0ef9ce4] | 1190 | /* TRANSLATORS: " *fix a " gives this message: */ |
---|
| 1191 | compile_diagnostic(DIAG_INFO|DIAG_COL, /*FIX command with no coordinates - fixing at (0,0,0)*/54); |
---|
| 1192 | |
---|
[6727d64] | 1193 | name_omit_already = fix_name; |
---|
| 1194 | name_omit_already_filename = file.filename; |
---|
| 1195 | name_omit_already_line = file.line; |
---|
| 1196 | } |
---|
| 1197 | |
---|
[725d3b1] | 1198 | coord.v[0] = coord.v[1] = coord.v[2] = (real)0.0; |
---|
[5f1e194] | 1199 | } else { |
---|
[725d3b1] | 1200 | coord.v[1] = read_numeric(false); |
---|
| 1201 | coord.v[2] = read_numeric(false); |
---|
[702be52] | 1202 | |
---|
[b39e24a] | 1203 | if (pcs->proj_str && proj_str_out) { |
---|
[da9163b] | 1204 | PJ *transform = pj_cached; |
---|
| 1205 | if (!transform) { |
---|
| 1206 | transform = proj_create_crs_to_crs(PJ_DEFAULT_CTX, |
---|
[b39e24a] | 1207 | pcs->proj_str, |
---|
| 1208 | proj_str_out, |
---|
| 1209 | NULL); |
---|
[da9163b] | 1210 | if (transform) { |
---|
[998388e] | 1211 | /* Normalise the output order so x is longitude and y latitude - by |
---|
| 1212 | * default new PROJ has them switched for EPSG:4326 which just seems |
---|
| 1213 | * confusing. |
---|
| 1214 | */ |
---|
[da9163b] | 1215 | PJ* pj_norm = proj_normalize_for_visualization(PJ_DEFAULT_CTX, |
---|
| 1216 | transform); |
---|
| 1217 | proj_destroy(transform); |
---|
| 1218 | transform = pj_norm; |
---|
| 1219 | } |
---|
| 1220 | |
---|
| 1221 | pj_cached = transform; |
---|
[b39e24a] | 1222 | } |
---|
| 1223 | |
---|
| 1224 | if (proj_angular_input(transform, PJ_FWD)) { |
---|
| 1225 | /* Input coordinate system expects radians. */ |
---|
[725d3b1] | 1226 | coord.v[0] = rad(coord.v[0]); |
---|
| 1227 | coord.v[1] = rad(coord.v[1]); |
---|
[702be52] | 1228 | } |
---|
[b39e24a] | 1229 | |
---|
[1a6fddc] | 1230 | coord.v[3] = HUGE_VAL; |
---|
[b39e24a] | 1231 | coord = proj_trans(transform, PJ_FWD, coord); |
---|
| 1232 | |
---|
[725d3b1] | 1233 | if (coord.v[0] == HUGE_VAL || |
---|
| 1234 | coord.v[1] == HUGE_VAL || |
---|
| 1235 | coord.v[2] == HUGE_VAL) { |
---|
[b39e24a] | 1236 | compile_diagnostic(DIAG_ERR, /*Failed to convert coordinates: %s*/436, |
---|
[d9d8f21] | 1237 | proj_context_errno_string(PJ_DEFAULT_CTX, |
---|
| 1238 | proj_errno(transform))); |
---|
[998388e] | 1239 | /* Set dummy values which are finite. */ |
---|
[725d3b1] | 1240 | coord.v[0] = coord.v[1] = coord.v[2] = 0; |
---|
[702be52] | 1241 | } |
---|
[b39e24a] | 1242 | } else if (pcs->proj_str) { |
---|
[cab0f26] | 1243 | compile_diagnostic(DIAG_ERR, /*The input projection is set but the output projection isn't*/437); |
---|
[b39e24a] | 1244 | } else if (proj_str_out) { |
---|
[cab0f26] | 1245 | compile_diagnostic(DIAG_ERR, /*The output projection is set but the input projection isn't*/438); |
---|
[702be52] | 1246 | } |
---|
| 1247 | |
---|
[c270761] | 1248 | get_pos(&fp); |
---|
[725d3b1] | 1249 | real sdx = read_numeric(true); |
---|
[2cfcb32] | 1250 | if (sdx <= 0) { |
---|
[c270761] | 1251 | set_pos(&fp); |
---|
| 1252 | compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_NUM, /*Standard deviation must be positive*/48); |
---|
[2cfcb32] | 1253 | return; |
---|
| 1254 | } |
---|
[647407d] | 1255 | if (sdx != HUGE_REAL) { |
---|
| 1256 | real sdy, sdz; |
---|
[c80bd34] | 1257 | real cxy = 0, cyz = 0, czx = 0; |
---|
[c270761] | 1258 | get_pos(&fp); |
---|
[63d4f07] | 1259 | sdy = read_numeric(true); |
---|
[647407d] | 1260 | if (sdy == HUGE_REAL) { |
---|
| 1261 | /* only one variance given */ |
---|
| 1262 | sdy = sdz = sdx; |
---|
| 1263 | } else { |
---|
[2cfcb32] | 1264 | if (sdy <= 0) { |
---|
[c270761] | 1265 | set_pos(&fp); |
---|
| 1266 | compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_NUM, /*Standard deviation must be positive*/48); |
---|
[2cfcb32] | 1267 | return; |
---|
| 1268 | } |
---|
[c270761] | 1269 | get_pos(&fp); |
---|
[63d4f07] | 1270 | sdz = read_numeric(true); |
---|
[647407d] | 1271 | if (sdz == HUGE_REAL) { |
---|
| 1272 | /* two variances given - horizontal & vertical */ |
---|
| 1273 | sdz = sdy; |
---|
| 1274 | sdy = sdx; |
---|
[c80bd34] | 1275 | } else { |
---|
[2cfcb32] | 1276 | if (sdz <= 0) { |
---|
[c270761] | 1277 | set_pos(&fp); |
---|
| 1278 | compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_NUM, /*Standard deviation must be positive*/48); |
---|
[2cfcb32] | 1279 | return; |
---|
| 1280 | } |
---|
[63d4f07] | 1281 | cxy = read_numeric(true); |
---|
[c80bd34] | 1282 | if (cxy != HUGE_REAL) { |
---|
| 1283 | /* covariances given */ |
---|
[63d4f07] | 1284 | cyz = read_numeric(false); |
---|
| 1285 | czx = read_numeric(false); |
---|
[c2211a5] | 1286 | } else { |
---|
| 1287 | cxy = 0; |
---|
[c80bd34] | 1288 | } |
---|
[647407d] | 1289 | } |
---|
| 1290 | } |
---|
[725d3b1] | 1291 | fix_station_with_variance(fix_name, coord.v, |
---|
| 1292 | sdx * sdx, sdy * sdy, sdz * sdz |
---|
[647407d] | 1293 | #ifndef NO_COVARIANCES |
---|
[725d3b1] | 1294 | , cxy, cyz, czx |
---|
[647407d] | 1295 | #endif |
---|
[725d3b1] | 1296 | ); |
---|
[a9c640c] | 1297 | |
---|
[e315359] | 1298 | if (reference) { |
---|
| 1299 | // `*fix reference` so suppress "unused fixed point" warning. |
---|
| 1300 | fix_name->sflags &= ~BIT(SFLAGS_UNUSED_FIXED_POINT); |
---|
| 1301 | } |
---|
[a9c640c] | 1302 | if (!first_fix_name) { |
---|
| 1303 | /* We track if we've fixed a station yet, and if so what the name |
---|
| 1304 | * of the first fix was, so that we can issue an error if the |
---|
| 1305 | * output coordinate system is set after fixing a station. */ |
---|
| 1306 | first_fix_name = fix_name; |
---|
| 1307 | first_fix_filename = file.filename; |
---|
| 1308 | first_fix_line = file.line; |
---|
| 1309 | } |
---|
| 1310 | |
---|
[cb3d1e2] | 1311 | return; |
---|
[647407d] | 1312 | } |
---|
[5f1e194] | 1313 | } |
---|
| 1314 | |
---|
[56db37f] | 1315 | if (!first_fix_name) { |
---|
| 1316 | /* We track if we've fixed a station yet, and if so what the name of the |
---|
| 1317 | * first fix was, so that we can issue an error if the output coordinate |
---|
| 1318 | * system is set after fixing a station. */ |
---|
| 1319 | first_fix_name = fix_name; |
---|
| 1320 | first_fix_filename = file.filename; |
---|
| 1321 | first_fix_line = file.line; |
---|
| 1322 | } |
---|
| 1323 | |
---|
[725d3b1] | 1324 | int fix_result = fix_station(fix_name, coord.v); |
---|
[e315359] | 1325 | if (reference) { |
---|
| 1326 | // `*fix reference` so suppress "unused fixed point" warning. |
---|
| 1327 | fix_name->sflags &= ~BIT(SFLAGS_UNUSED_FIXED_POINT); |
---|
| 1328 | } |
---|
[725d3b1] | 1329 | if (fix_result == 0) { |
---|
[5f1e194] | 1330 | return; |
---|
| 1331 | } |
---|
[d1b1380] | 1332 | |
---|
[58edecc] | 1333 | get_pos(&fp); |
---|
| 1334 | set_pos(&fp_stn); |
---|
[725d3b1] | 1335 | if (fix_result < 0) { |
---|
[58edecc] | 1336 | compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Station already fixed or equated to a fixed point*/46); |
---|
| 1337 | } else { |
---|
| 1338 | /* TRANSLATORS: *fix a 1 2 3 / *fix a 1 2 3 */ |
---|
| 1339 | compile_diagnostic(DIAG_WARN|DIAG_WORD, /*Station already fixed at the same coordinates*/55); |
---|
[5f1e194] | 1340 | } |
---|
[bf669f6] | 1341 | compile_diagnostic_pfx(DIAG_INFO, fix_name, /*Previously fixed or equated here*/493); |
---|
[58edecc] | 1342 | set_pos(&fp); |
---|
[d1b1380] | 1343 | } |
---|
| 1344 | |
---|
[5c3c61a] | 1345 | static void |
---|
| 1346 | cmd_flags(void) |
---|
| 1347 | { |
---|
[82919e07] | 1348 | static const sztok flagtab[] = { |
---|
[5c3c61a] | 1349 | {"DUPLICATE", FLAGS_DUPLICATE }, |
---|
| 1350 | {"NOT", FLAGS_NOT }, |
---|
[95c3272] | 1351 | {"SPLAY", FLAGS_SPLAY }, |
---|
[5c3c61a] | 1352 | {"SURFACE", FLAGS_SURFACE }, |
---|
[a4ae909] | 1353 | {NULL, FLAGS_UNKNOWN } |
---|
[5c3c61a] | 1354 | }; |
---|
[63d4f07] | 1355 | bool fNot = false; |
---|
| 1356 | bool fEmpty = true; |
---|
[5c3c61a] | 1357 | while (1) { |
---|
| 1358 | int flag; |
---|
| 1359 | get_token(); |
---|
[0532954] | 1360 | /* If token is empty, it could mean end of line, or maybe |
---|
[e1cbc0d] | 1361 | * some non-alphanumeric junk which is better reported later */ |
---|
[0532954] | 1362 | if (s_empty(&token)) break; |
---|
[62bb4d3] | 1363 | |
---|
[63d4f07] | 1364 | fEmpty = false; |
---|
[5c3c61a] | 1365 | flag = match_tok(flagtab, TABSIZE(flagtab)); |
---|
| 1366 | /* treat the second NOT in "NOT NOT" as an unknown flag */ |
---|
| 1367 | if (flag == FLAGS_UNKNOWN || (fNot && flag == FLAGS_NOT)) { |
---|
[caae6cd] | 1368 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*FLAG “%s” unknown*/68, |
---|
[0532954] | 1369 | s_str(&token)); |
---|
[0804fbe] | 1370 | /* Recover from “*FLAGS NOT BOGUS SURFACE” by ignoring "NOT BOGUS" */ |
---|
[63d4f07] | 1371 | fNot = false; |
---|
[62bb4d3] | 1372 | } else if (flag == FLAGS_NOT) { |
---|
[63d4f07] | 1373 | fNot = true; |
---|
[5c3c61a] | 1374 | } else if (fNot) { |
---|
[421b7d2] | 1375 | pcs->flags &= ~BIT(flag); |
---|
[63d4f07] | 1376 | fNot = false; |
---|
[5c3c61a] | 1377 | } else { |
---|
[421b7d2] | 1378 | pcs->flags |= BIT(flag); |
---|
[5c3c61a] | 1379 | } |
---|
| 1380 | } |
---|
[9881759] | 1381 | |
---|
| 1382 | if (fNot) { |
---|
[725d3b1] | 1383 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN, |
---|
| 1384 | /*Expecting “%s”, “%s”, or “%s”*/188, |
---|
| 1385 | "DUPLICATE", "SPLAY", "SURFACE"); |
---|
[9881759] | 1386 | } else if (fEmpty) { |
---|
[725d3b1] | 1387 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN, |
---|
| 1388 | /*Expecting “%s”, “%s”, “%s”, or “%s”*/189, |
---|
| 1389 | "NOT", "DUPLICATE", "SPLAY", "SURFACE"); |
---|
[9881759] | 1390 | } |
---|
[5c3c61a] | 1391 | } |
---|
| 1392 | |
---|
[a420b49] | 1393 | static void |
---|
[eb18f4d] | 1394 | cmd_equate(void) |
---|
[a420b49] | 1395 | { |
---|
[c270761] | 1396 | filepos fp; |
---|
| 1397 | get_pos(&fp); |
---|
[bf669f6] | 1398 | prefix *prev_name = NULL; |
---|
| 1399 | prefix *name = read_prefix(PFX_STATION|PFX_ALLOW_ROOT|PFX_SUSPECT_TYPO); |
---|
[63d4f07] | 1400 | while (true) { |
---|
[bf669f6] | 1401 | if (!name->stn || !fixed(name->stn)) { |
---|
| 1402 | // If the station isn't already fixed, make its file:line location |
---|
| 1403 | // reflect this *equate. |
---|
| 1404 | name->filename = file.filename; |
---|
| 1405 | name->line = file.line; |
---|
| 1406 | } |
---|
[b9c82c2] | 1407 | skipblanks(); |
---|
| 1408 | if (isEol(ch) || isComm(ch)) { |
---|
[bf669f6] | 1409 | if (prev_name == NULL) { |
---|
| 1410 | /* E.g. *equate entrance.6 */ |
---|
[c270761] | 1411 | set_pos(&fp); |
---|
[736f7df] | 1412 | /* TRANSLATORS: EQUATE is a command name, so shouldn’t be |
---|
[0b8c321] | 1413 | * translated. |
---|
| 1414 | * |
---|
| 1415 | * Here "station" is a survey station, not a train station. |
---|
| 1416 | */ |
---|
[d0be687d] | 1417 | compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_WORD, /*Only one station in EQUATE command*/33); |
---|
[11f9067] | 1418 | } |
---|
[5f1e194] | 1419 | return; |
---|
[d1b1380] | 1420 | } |
---|
[cb3d1e2] | 1421 | |
---|
[bf669f6] | 1422 | prev_name = name; |
---|
| 1423 | name = read_prefix(PFX_STATION|PFX_ALLOW_ROOT|PFX_SUSPECT_TYPO); |
---|
[dad5d599] | 1424 | process_equate(prev_name, name); |
---|
[5f1e194] | 1425 | } |
---|
[d1b1380] | 1426 | } |
---|
| 1427 | |
---|
[84c60fc] | 1428 | static void |
---|
| 1429 | report_missing_export(prefix *pfx, int depth) |
---|
| 1430 | { |
---|
| 1431 | char *s; |
---|
[93e3492] | 1432 | const char *p; |
---|
| 1433 | prefix *survey = pfx; |
---|
[84c60fc] | 1434 | int i; |
---|
| 1435 | for (i = depth + 1; i; i--) { |
---|
| 1436 | survey = survey->up; |
---|
[4c07c51] | 1437 | SVX_ASSERT(survey); |
---|
[84c60fc] | 1438 | } |
---|
| 1439 | s = osstrdup(sprint_prefix(survey)); |
---|
[93e3492] | 1440 | p = sprint_prefix(pfx); |
---|
[84c60fc] | 1441 | if (survey->filename) { |
---|
[93e3492] | 1442 | /* TRANSLATORS: A station must be exported out of each level it is in, so |
---|
| 1443 | * this would give "Station “\outer.inner.1” not exported from survey |
---|
| 1444 | * “\outer”)": |
---|
| 1445 | * |
---|
| 1446 | * *equate entrance outer.inner.1 |
---|
| 1447 | * *begin outer |
---|
| 1448 | * *begin inner |
---|
| 1449 | * *export 1 |
---|
| 1450 | * 1 2 1.23 045 -6 |
---|
| 1451 | * *end inner |
---|
[0b8c321] | 1452 | * *end outer |
---|
| 1453 | * |
---|
| 1454 | * Here "survey" is a "cave map" rather than list of questions - it should be |
---|
| 1455 | * translated to the terminology that cavers using the language would use. |
---|
| 1456 | */ |
---|
[cab0f26] | 1457 | compile_diagnostic_pfx(DIAG_ERR, survey, |
---|
| 1458 | /*Station “%s” not exported from survey “%s”*/26, p, s); |
---|
[93e3492] | 1459 | } else { |
---|
[cab0f26] | 1460 | compile_diagnostic(DIAG_ERR, /*Station “%s” not exported from survey “%s”*/26, p, s); |
---|
[84c60fc] | 1461 | } |
---|
[ae917b96] | 1462 | free(s); |
---|
[84c60fc] | 1463 | } |
---|
| 1464 | |
---|
[fb2e93c] | 1465 | static void |
---|
[d1878c51] | 1466 | cmd_export(void) |
---|
[fb2e93c] | 1467 | { |
---|
[d1878c51] | 1468 | prefix *pfx; |
---|
| 1469 | |
---|
[63d4f07] | 1470 | fExportUsed = true; |
---|
[932f7e9] | 1471 | do { |
---|
[fb2e93c] | 1472 | int depth = 0; |
---|
[6327dcf] | 1473 | pfx = read_prefix(PFX_STATION); |
---|
| 1474 | { |
---|
[421b7d2] | 1475 | prefix *p = pfx; |
---|
[fb2e93c] | 1476 | while (p != NULL && p != pcs->Prefix) { |
---|
| 1477 | depth++; |
---|
| 1478 | p = p->up; |
---|
| 1479 | } |
---|
[84c60fc] | 1480 | /* Something like: *export \foo, but we've excluded use of root */ |
---|
[4c07c51] | 1481 | SVX_ASSERT(p); |
---|
[fb2e93c] | 1482 | } |
---|
[84c60fc] | 1483 | /* *export \ or similar bogus stuff */ |
---|
[4c07c51] | 1484 | SVX_ASSERT(depth); |
---|
[932f7e9] | 1485 | #if 0 |
---|
| 1486 | printf("C min %d max %d depth %d pfx %s\n", |
---|
| 1487 | pfx->min_export, pfx->max_export, depth, sprint_prefix(pfx)); |
---|
| 1488 | #endif |
---|
| 1489 | if (pfx->min_export == 0) { |
---|
[421b7d2] | 1490 | /* not encountered *export for this name before */ |
---|
| 1491 | if (pfx->max_export > depth) report_missing_export(pfx, depth); |
---|
| 1492 | pfx->min_export = pfx->max_export = depth; |
---|
[c00c74a9] | 1493 | } else if (pfx->min_export != USHRT_MAX) { |
---|
| 1494 | /* FIXME: what to do if a station is marked for inferred exports |
---|
| 1495 | * but is then explicitly exported? Currently we just ignore the |
---|
| 1496 | * explicit export... */ |
---|
[421b7d2] | 1497 | if (pfx->min_export - 1 > depth) { |
---|
[84c60fc] | 1498 | report_missing_export(pfx, depth); |
---|
[421b7d2] | 1499 | } else if (pfx->min_export - 1 < depth) { |
---|
[736f7df] | 1500 | /* TRANSLATORS: Here "station" is a survey station, not a train station. |
---|
[b49ac56] | 1501 | * |
---|
[736f7df] | 1502 | * Exporting a station twice gives this error: |
---|
| 1503 | * |
---|
| 1504 | * *begin example |
---|
| 1505 | * *export 1 |
---|
| 1506 | * *export 1 |
---|
| 1507 | * 1 2 1.24 045 -6 |
---|
| 1508 | * *end example */ |
---|
[cab0f26] | 1509 | compile_diagnostic(DIAG_ERR, /*Station “%s” already exported*/66, |
---|
| 1510 | sprint_prefix(pfx)); |
---|
[421b7d2] | 1511 | } |
---|
| 1512 | pfx->min_export = depth; |
---|
[fb2e93c] | 1513 | } |
---|
[b9c82c2] | 1514 | skipblanks(); |
---|
| 1515 | } while (!isEol(ch) && !isComm(ch)); |
---|
[fb2e93c] | 1516 | } |
---|
| 1517 | |
---|
[a420b49] | 1518 | static void |
---|
[eb18f4d] | 1519 | cmd_data(void) |
---|
[a420b49] | 1520 | { |
---|
[82919e07] | 1521 | static const sztok dtab[] = { |
---|
[9b5d785] | 1522 | {"ALTITUDE", Dz }, |
---|
[5b7c1b7] | 1523 | {"BACKBEARING", BackComp }, |
---|
| 1524 | {"BACKCLINO", BackClino }, /* alternative name */ |
---|
| 1525 | {"BACKCOMPASS", BackComp }, /* alternative name */ |
---|
| 1526 | {"BACKGRADIENT", BackClino }, |
---|
[4f38f94] | 1527 | {"BACKLENGTH", BackTape }, |
---|
| 1528 | {"BACKTAPE", BackTape }, /* alternative name */ |
---|
[5f1e194] | 1529 | {"BEARING", Comp }, |
---|
[ee05463] | 1530 | {"CEILING", Up }, /* alternative name */ |
---|
[a4ae909] | 1531 | {"CLINO", Clino }, /* alternative name */ |
---|
[a420b49] | 1532 | {"COMPASS", Comp }, /* alternative name */ |
---|
[a4ae909] | 1533 | {"COUNT", Count }, /* FrCount&ToCount in multiline */ |
---|
| 1534 | {"DEPTH", Depth }, /* FrDepth&ToDepth in multiline */ |
---|
[6114207] | 1535 | {"DEPTHCHANGE", DepthChange }, |
---|
[421b7d2] | 1536 | {"DIRECTION", Dir }, |
---|
[ee05463] | 1537 | {"DOWN", Down }, |
---|
[647407d] | 1538 | {"DX", Dx }, |
---|
| 1539 | {"DY", Dy }, |
---|
| 1540 | {"DZ", Dz }, |
---|
[9b5d785] | 1541 | {"EASTING", Dx }, |
---|
[ee05463] | 1542 | {"FLOOR", Down }, /* alternative name */ |
---|
[a4ae909] | 1543 | {"FROM", Fr }, |
---|
[647407d] | 1544 | {"FROMCOUNT", FrCount }, |
---|
[5f1e194] | 1545 | {"FROMDEPTH", FrDepth }, |
---|
| 1546 | {"GRADIENT", Clino }, |
---|
| 1547 | {"IGNORE", Ignore }, |
---|
| 1548 | {"IGNOREALL", IgnoreAll }, |
---|
[ee05463] | 1549 | {"LEFT", Left }, |
---|
[5f1e194] | 1550 | {"LENGTH", Tape }, |
---|
[a0027a2] | 1551 | {"NEWLINE", Newline }, |
---|
[9b5d785] | 1552 | {"NORTHING", Dy }, |
---|
[ee05463] | 1553 | {"RIGHT", Right }, |
---|
[421b7d2] | 1554 | {"STATION", Station }, /* Fr&To in multiline */ |
---|
[a4ae909] | 1555 | {"TAPE", Tape }, /* alternative name */ |
---|
| 1556 | {"TO", To }, |
---|
[647407d] | 1557 | {"TOCOUNT", ToCount }, |
---|
[5f1e194] | 1558 | {"TODEPTH", ToDepth }, |
---|
[ee05463] | 1559 | {"UP", Up }, |
---|
[a4ae909] | 1560 | {NULL, End } |
---|
[5f1e194] | 1561 | }; |
---|
| 1562 | |
---|
[107b8bd] | 1563 | #define MASK_stns BIT(Fr) | BIT(To) | BIT(Station) |
---|
[4f38f94] | 1564 | #define MASK_tape BIT(Tape) | BIT(BackTape) | BIT(FrCount) | BIT(ToCount) | BIT(Count) |
---|
[107b8bd] | 1565 | #define MASK_dpth BIT(FrDepth) | BIT(ToDepth) | BIT(Depth) | BIT(DepthChange) |
---|
[5b7c1b7] | 1566 | #define MASK_comp BIT(Comp) | BIT(BackComp) |
---|
| 1567 | #define MASK_clin BIT(Clino) | BIT(BackClino) |
---|
[5f1e194] | 1568 | |
---|
[5b7c1b7] | 1569 | #define MASK_NORMAL MASK_stns | BIT(Dir) | MASK_tape | MASK_comp | MASK_clin |
---|
[bd263b36] | 1570 | #define MASK_DIVING MASK_NORMAL | MASK_dpth |
---|
[107b8bd] | 1571 | #define MASK_CARTESIAN MASK_stns | BIT(Dx) | BIT(Dy) | BIT(Dz) |
---|
[5b7c1b7] | 1572 | #define MASK_CYLPOLAR MASK_stns | BIT(Dir) | MASK_tape | MASK_comp | MASK_dpth |
---|
[107b8bd] | 1573 | #define MASK_NOSURVEY MASK_stns |
---|
[19bb4772] | 1574 | #define MASK_PASSAGE BIT(Station) | BIT(Left) | BIT(Right) | BIT(Up) | BIT(Down) |
---|
[63ae487] | 1575 | #define MASK_IGNORE 0 // No readings in this style. |
---|
[421b7d2] | 1576 | |
---|
[63ae487] | 1577 | // readings which may be given for each style (index is STYLE_*) |
---|
[6114207] | 1578 | static const unsigned long mask[] = { |
---|
[200a12c] | 1579 | MASK_NORMAL, MASK_DIVING, MASK_CARTESIAN, MASK_CYLPOLAR, MASK_NOSURVEY, |
---|
[63ae487] | 1580 | MASK_PASSAGE, MASK_IGNORE |
---|
[5f1e194] | 1581 | }; |
---|
| 1582 | |
---|
[63ae487] | 1583 | // readings which may be omitted for each style (index is STYLE_*) |
---|
[6114207] | 1584 | static const unsigned long mask_optional[] = { |
---|
[5b7c1b7] | 1585 | BIT(Dir) | BIT(Clino) | BIT(BackClino), |
---|
[bd263b36] | 1586 | BIT(Dir) | BIT(Clino) | BIT(BackClino), |
---|
[86f26e2] | 1587 | 0, |
---|
[54c4612] | 1588 | BIT(Dir), |
---|
[200a12c] | 1589 | 0, |
---|
[63ae487] | 1590 | 0, /* BIT(Left) | BIT(Right) | BIT(Up) | BIT(Down), */ |
---|
| 1591 | 0 |
---|
[5f1e194] | 1592 | }; |
---|
| 1593 | |
---|
[6114207] | 1594 | /* all valid readings */ |
---|
| 1595 | static const unsigned long mask_all[] = { |
---|
| 1596 | MASK_NORMAL | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End), |
---|
| 1597 | MASK_DIVING | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End), |
---|
| 1598 | MASK_CARTESIAN | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End), |
---|
| 1599 | MASK_CYLPOLAR | BIT(Newline) | BIT(Ignore) | BIT(IgnoreAll) | BIT(End), |
---|
[200a12c] | 1600 | MASK_NOSURVEY | BIT(Ignore) | BIT(IgnoreAll) | BIT(End), |
---|
[63ae487] | 1601 | MASK_PASSAGE | BIT(Ignore) | BIT(IgnoreAll) | BIT(End), |
---|
| 1602 | MASK_IGNORE |
---|
[6114207] | 1603 | }; |
---|
[54c4612] | 1604 | #define STYLE_DEFAULT -2 |
---|
| 1605 | #define STYLE_UNKNOWN -1 |
---|
| 1606 | |
---|
[82919e07] | 1607 | static const sztok styletab[] = { |
---|
[647407d] | 1608 | {"CARTESIAN", STYLE_CARTESIAN }, |
---|
[54c4612] | 1609 | {"CYLPOLAR", STYLE_CYLPOLAR }, |
---|
[a420b49] | 1610 | {"DEFAULT", STYLE_DEFAULT }, |
---|
[5f1e194] | 1611 | {"DIVING", STYLE_DIVING }, |
---|
[63ae487] | 1612 | {"IGNORE", STYLE_IGNORE }, |
---|
[5f1e194] | 1613 | {"NORMAL", STYLE_NORMAL }, |
---|
[647407d] | 1614 | {"NOSURVEY", STYLE_NOSURVEY }, |
---|
[ee05463] | 1615 | {"PASSAGE", STYLE_PASSAGE }, |
---|
[107b8bd] | 1616 | {"TOPOFIL", STYLE_NORMAL }, |
---|
[a4ae909] | 1617 | {NULL, STYLE_UNKNOWN } |
---|
[5f1e194] | 1618 | }; |
---|
| 1619 | |
---|
[ee3a4ed] | 1620 | #define m_multi (BIT(Station) | BIT(Count) | BIT(Depth)) |
---|
| 1621 | |
---|
[dbe783b] | 1622 | int style, k = 0; |
---|
| 1623 | reading d; |
---|
[6114207] | 1624 | unsigned long mUsed = 0; |
---|
[667e803c] | 1625 | int old_style = pcs->style; |
---|
[5f1e194] | 1626 | |
---|
[50f6901] | 1627 | /* after a bad *data command ignore survey data until the next |
---|
| 1628 | * *data command to avoid an avalanche of errors */ |
---|
[fdffa7d] | 1629 | pcs->recorded_style = pcs->style = STYLE_IGNORE; |
---|
[289b7a8] | 1630 | |
---|
[a420b49] | 1631 | get_token(); |
---|
[5f1e194] | 1632 | style = match_tok(styletab, TABSIZE(styletab)); |
---|
| 1633 | |
---|
[a420b49] | 1634 | if (style == STYLE_DEFAULT) { |
---|
| 1635 | default_style(pcs); |
---|
| 1636 | return; |
---|
| 1637 | } |
---|
| 1638 | |
---|
[63ae487] | 1639 | if (style == STYLE_IGNORE) { |
---|
| 1640 | return; |
---|
| 1641 | } |
---|
| 1642 | |
---|
[5f1e194] | 1643 | if (style == STYLE_UNKNOWN) { |
---|
[0532954] | 1644 | if (s_empty(&token)) { |
---|
[b9122e1] | 1645 | /* "*data" without arguments reinitialises the current style - useful |
---|
| 1646 | * when using *data passage as it provides a way to break the passage |
---|
| 1647 | * tube without having to repeat the full *data passage command. |
---|
[667e803c] | 1648 | */ |
---|
[fdffa7d] | 1649 | pcs->recorded_style = pcs->style = style = old_style; |
---|
[667e803c] | 1650 | goto reinit_style; |
---|
| 1651 | } |
---|
[736f7df] | 1652 | /* TRANSLATORS: e.g. trying to refer to an invalid FNORD data style */ |
---|
[caae6cd] | 1653 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Data style “%s” unknown*/65, s_str(&token)); |
---|
[5f1e194] | 1654 | return; |
---|
| 1655 | } |
---|
[a420b49] | 1656 | |
---|
[5f1e194] | 1657 | skipblanks(); |
---|
[d6d3576] | 1658 | #ifndef NO_DEPRECATED |
---|
| 1659 | /* Olde syntax had optional field for survey grade, so allow an omit |
---|
| 1660 | * but issue a warning about it */ |
---|
| 1661 | if (isOmit(ch)) { |
---|
| 1662 | static int data_depr_count = 0; |
---|
| 1663 | if (data_depr_count < 5) { |
---|
[caae6cd] | 1664 | compile_diagnostic(DIAG_WARN|DIAG_TOKEN, /*“*data %s %c …” is deprecated - use “*data %s …” instead*/104, |
---|
[0532954] | 1665 | s_str(&token), ch, s_str(&token)); |
---|
[d6d3576] | 1666 | if (++data_depr_count == 5) |
---|
[64544daf] | 1667 | compile_diagnostic(DIAG_INFO, /*Further uses of this deprecated feature will not be reported*/95); |
---|
[d6d3576] | 1668 | } |
---|
| 1669 | nextch(); |
---|
| 1670 | } |
---|
| 1671 | #endif |
---|
[d1b1380] | 1672 | |
---|
[dbe783b] | 1673 | int kMac = 6; /* minimum for NORMAL style */ |
---|
| 1674 | reading *new_order = osmalloc(kMac * sizeof(reading)); |
---|
[0532954] | 1675 | char *style_name = s_steal(&token); |
---|
[421b7d2] | 1676 | do { |
---|
[c80bd34] | 1677 | filepos fp; |
---|
| 1678 | get_pos(&fp); |
---|
[a420b49] | 1679 | get_token(); |
---|
[5f1e194] | 1680 | d = match_tok(dtab, TABSIZE(dtab)); |
---|
[30c81bb] | 1681 | if (d == End && !s_empty(&token)) { |
---|
| 1682 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, |
---|
| 1683 | /*Reading “%s” not allowed in data style “%s”*/63, |
---|
| 1684 | s_str(&token), style_name); |
---|
[ae917b96] | 1685 | free(style_name); |
---|
| 1686 | free(new_order); |
---|
[30c81bb] | 1687 | return; |
---|
| 1688 | } |
---|
| 1689 | |
---|
[90bb053f] | 1690 | /* only token allowed after IGNOREALL is NEWLINE */ |
---|
| 1691 | if (k && new_order[k - 1] == IgnoreAll && d != Newline) { |
---|
[c80bd34] | 1692 | set_pos(&fp); |
---|
[90bb053f] | 1693 | break; |
---|
| 1694 | } |
---|
[0395657] | 1695 | /* Note: an unknown token is reported as trailing garbage */ |
---|
[6114207] | 1696 | if (!TSTBIT(mask_all[style], d)) { |
---|
[736f7df] | 1697 | /* TRANSLATORS: a data "style" is something like NORMAL, DIVING, etc. |
---|
| 1698 | * a "reading" is one of FROM, TO, TAPE, COMPASS, CLINO for NORMAL |
---|
[88f302c] | 1699 | * style. Neither "style" nor "reading" is a keyword in the program. |
---|
| 1700 | * |
---|
| 1701 | * This error complains about a "DEPTH" gauge reading in "NORMAL" |
---|
| 1702 | * style, for example. |
---|
[736f7df] | 1703 | */ |
---|
[caae6cd] | 1704 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, |
---|
[cab0f26] | 1705 | /*Reading “%s” not allowed in data style “%s”*/63, |
---|
[0532954] | 1706 | s_str(&token), style_name); |
---|
[ae917b96] | 1707 | free(style_name); |
---|
| 1708 | free(new_order); |
---|
[ee3a4ed] | 1709 | return; |
---|
| 1710 | } |
---|
[6114207] | 1711 | if (TSTBIT(mUsed, Newline) && TSTBIT(m_multi, d)) { |
---|
[736f7df] | 1712 | /* TRANSLATORS: caused by e.g. |
---|
| 1713 | * |
---|
| 1714 | * *data diving station newline depth tape compass |
---|
| 1715 | * |
---|
| 1716 | * ("depth" needs to occur before "newline"). */ |
---|
[caae6cd] | 1717 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, |
---|
[0532954] | 1718 | /*Reading “%s” must occur before NEWLINE*/225, s_str(&token)); |
---|
[ae917b96] | 1719 | free(style_name); |
---|
| 1720 | free(new_order); |
---|
[5f1e194] | 1721 | return; |
---|
| 1722 | } |
---|
[0395657] | 1723 | /* Check for duplicates unless it's a special reading: |
---|
[ee3a4ed] | 1724 | * IGNOREALL,IGNORE (duplicates allowed) ; END (not possible) |
---|
[5f1e194] | 1725 | */ |
---|
[ee3a4ed] | 1726 | if (!((BIT(Ignore) | BIT(End) | BIT(IgnoreAll)) & BIT(d))) { |
---|
[95c3272] | 1727 | if (TSTBIT(mUsed, d)) { |
---|
[736f7df] | 1728 | /* TRANSLATORS: complains about a situation like trying to define |
---|
| 1729 | * two from stations per leg */ |
---|
[caae6cd] | 1730 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Duplicate reading “%s”*/67, s_str(&token)); |
---|
[ae917b96] | 1731 | free(style_name); |
---|
| 1732 | free(new_order); |
---|
[90bb053f] | 1733 | return; |
---|
[62bb4d3] | 1734 | } else { |
---|
[5b7c1b7] | 1735 | /* Check for previously listed readings which are incompatible |
---|
| 1736 | * with this one - e.g. Count vs FrCount */ |
---|
[63d4f07] | 1737 | bool fBad = false; |
---|
[90bb053f] | 1738 | switch (d) { |
---|
| 1739 | case Station: |
---|
[63d4f07] | 1740 | if (mUsed & (BIT(Fr) | BIT(To))) fBad = true; |
---|
[90bb053f] | 1741 | break; |
---|
| 1742 | case Fr: case To: |
---|
[63d4f07] | 1743 | if (TSTBIT(mUsed, Station)) fBad = true; |
---|
[90bb053f] | 1744 | break; |
---|
| 1745 | case Count: |
---|
[107b8bd] | 1746 | if (mUsed & (BIT(FrCount) | BIT(ToCount) | BIT(Tape))) |
---|
[63d4f07] | 1747 | fBad = true; |
---|
[90bb053f] | 1748 | break; |
---|
| 1749 | case FrCount: case ToCount: |
---|
[107b8bd] | 1750 | if (mUsed & (BIT(Count) | BIT(Tape))) |
---|
[63d4f07] | 1751 | fBad = true; |
---|
[90bb053f] | 1752 | break; |
---|
| 1753 | case Depth: |
---|
[6114207] | 1754 | if (mUsed & (BIT(FrDepth) | BIT(ToDepth) | BIT(DepthChange))) |
---|
[63d4f07] | 1755 | fBad = true; |
---|
[90bb053f] | 1756 | break; |
---|
| 1757 | case FrDepth: case ToDepth: |
---|
[63d4f07] | 1758 | if (mUsed & (BIT(Depth) | BIT(DepthChange))) fBad = true; |
---|
[a186573] | 1759 | break; |
---|
[6114207] | 1760 | case DepthChange: |
---|
[a186573] | 1761 | if (mUsed & (BIT(FrDepth) | BIT(ToDepth) | BIT(Depth))) |
---|
[63d4f07] | 1762 | fBad = true; |
---|
[90bb053f] | 1763 | break; |
---|
[ee3a4ed] | 1764 | case Newline: |
---|
| 1765 | if (mUsed & ~m_multi) { |
---|
[736f7df] | 1766 | /* TRANSLATORS: e.g. |
---|
| 1767 | * |
---|
| 1768 | * *data normal from to tape newline compass clino */ |
---|
[caae6cd] | 1769 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*NEWLINE can only be preceded by STATION, DEPTH, and COUNT*/226); |
---|
[ae917b96] | 1770 | free(style_name); |
---|
| 1771 | free(new_order); |
---|
[ee3a4ed] | 1772 | return; |
---|
| 1773 | } |
---|
[6114207] | 1774 | if (k == 0) { |
---|
[736f7df] | 1775 | /* TRANSLATORS: error from: |
---|
| 1776 | * |
---|
| 1777 | * *data normal newline from to tape compass clino */ |
---|
[caae6cd] | 1778 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*NEWLINE can’t be the first reading*/222); |
---|
[ae917b96] | 1779 | free(style_name); |
---|
| 1780 | free(new_order); |
---|
[6114207] | 1781 | return; |
---|
| 1782 | } |
---|
[ee3a4ed] | 1783 | break; |
---|
[90bb053f] | 1784 | default: /* avoid compiler warnings about unhandled enums */ |
---|
| 1785 | break; |
---|
[421b7d2] | 1786 | } |
---|
[90bb053f] | 1787 | if (fBad) { |
---|
[46cb98f] | 1788 | /* Not entirely happy with phrasing this... */ |
---|
[36efb03] | 1789 | /* TRANSLATORS: This is an error from the *DATA command. It |
---|
| 1790 | * means that a reading (which will appear where %s is isn't |
---|
| 1791 | * valid as the list of readings has already included the same |
---|
| 1792 | * reading, or an equivalent one (e.g. you can't have both |
---|
| 1793 | * DEPTH and DEPTHCHANGE together). */ |
---|
[caae6cd] | 1794 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Reading “%s” duplicates previous reading(s)*/77, |
---|
[0532954] | 1795 | s_str(&token)); |
---|
[ae917b96] | 1796 | free(style_name); |
---|
| 1797 | free(new_order); |
---|
[90bb053f] | 1798 | return; |
---|
| 1799 | } |
---|
[62bb4d3] | 1800 | mUsed |= BIT(d); /* used to catch duplicates */ |
---|
[a420b49] | 1801 | } |
---|
[d1b1380] | 1802 | } |
---|
[90bb053f] | 1803 | if (k && new_order[k - 1] == IgnoreAll) { |
---|
[4c07c51] | 1804 | SVX_ASSERT(d == Newline); |
---|
[90bb053f] | 1805 | k--; |
---|
| 1806 | d = IgnoreAllAndNewLine; |
---|
| 1807 | } |
---|
[a420b49] | 1808 | if (k >= kMac) { |
---|
| 1809 | kMac = kMac * 2; |
---|
[0395657] | 1810 | new_order = osrealloc(new_order, kMac * sizeof(reading)); |
---|
[a420b49] | 1811 | } |
---|
| 1812 | new_order[k++] = d; |
---|
[90bb053f] | 1813 | } while (d != End); |
---|
| 1814 | |
---|
[6114207] | 1815 | if (k >= 2 && new_order[k - 2] == Newline) { |
---|
[736f7df] | 1816 | /* TRANSLATORS: error from: |
---|
| 1817 | * |
---|
| 1818 | * *data normal from to tape compass clino newline */ |
---|
[caae6cd] | 1819 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*NEWLINE can’t be the last reading*/223); |
---|
[ae917b96] | 1820 | free(style_name); |
---|
| 1821 | free(new_order); |
---|
[6114207] | 1822 | return; |
---|
| 1823 | } |
---|
[421b7d2] | 1824 | |
---|
[6114207] | 1825 | if (style == STYLE_NOSURVEY) { |
---|
| 1826 | if (TSTBIT(mUsed, Station)) { |
---|
| 1827 | if (k >= kMac) { |
---|
| 1828 | kMac = kMac * 2; |
---|
| 1829 | new_order = osrealloc(new_order, kMac * sizeof(reading)); |
---|
| 1830 | } |
---|
| 1831 | new_order[k - 1] = Newline; |
---|
| 1832 | new_order[k++] = End; |
---|
| 1833 | } |
---|
[ee05463] | 1834 | } else if (style == STYLE_PASSAGE) { |
---|
| 1835 | /* Station doesn't mean "multiline" for STYLE_PASSAGE. */ |
---|
[6114207] | 1836 | } else if (!TSTBIT(mUsed, Newline) && (m_multi & mUsed)) { |
---|
[736f7df] | 1837 | /* TRANSLATORS: Error given by something like: |
---|
| 1838 | * |
---|
[6114207] | 1839 | * *data normal station tape compass clino |
---|
[736f7df] | 1840 | * |
---|
| 1841 | * ("station" signifies interleaved data). */ |
---|
[cab0f26] | 1842 | compile_diagnostic(DIAG_ERR|DIAG_SKIP, /*Interleaved readings, but no NEWLINE*/224); |
---|
[ae917b96] | 1843 | free(style_name); |
---|
| 1844 | free(new_order); |
---|
[6114207] | 1845 | return; |
---|
| 1846 | } |
---|
| 1847 | |
---|
[a6d094f] | 1848 | #if 0 |
---|
| 1849 | printf("mUsed = 0x%x\n", mUsed); |
---|
| 1850 | #endif |
---|
[90bb053f] | 1851 | |
---|
[5b7c1b7] | 1852 | /* Check the supplied readings form a sufficient set. */ |
---|
[ee05463] | 1853 | if (style != STYLE_PASSAGE) { |
---|
[4f38f94] | 1854 | if ((mUsed & (BIT(Fr) | BIT(To))) == (BIT(Fr) | BIT(To))) |
---|
[ee05463] | 1855 | mUsed |= BIT(Station); |
---|
| 1856 | else if (TSTBIT(mUsed, Station)) |
---|
| 1857 | mUsed |= BIT(Fr) | BIT(To); |
---|
| 1858 | } |
---|
[a186573] | 1859 | |
---|
[5b7c1b7] | 1860 | if (mUsed & (BIT(Comp) | BIT(BackComp))) |
---|
| 1861 | mUsed |= BIT(Comp) | BIT(BackComp); |
---|
| 1862 | |
---|
| 1863 | if (mUsed & (BIT(Clino) | BIT(BackClino))) |
---|
| 1864 | mUsed |= BIT(Clino) | BIT(BackClino); |
---|
| 1865 | |
---|
[4f38f94] | 1866 | if ((mUsed & (BIT(FrDepth) | BIT(ToDepth))) == (BIT(FrDepth) | BIT(ToDepth))) |
---|
[6114207] | 1867 | mUsed |= BIT(Depth) | BIT(DepthChange); |
---|
[4f38f94] | 1868 | else if (mUsed & (BIT(Depth) | BIT(DepthChange))) |
---|
| 1869 | mUsed |= BIT(FrDepth) | BIT(ToDepth) | BIT(Depth) | BIT(DepthChange); |
---|
| 1870 | |
---|
| 1871 | if ((mUsed & (BIT(FrCount) | BIT(ToCount))) == (BIT(FrCount) | BIT(ToCount))) |
---|
| 1872 | mUsed |= BIT(Count) | BIT(Tape) | BIT(BackTape); |
---|
| 1873 | else if (mUsed & (BIT(Count) | BIT(Tape) | BIT(BackTape))) |
---|
| 1874 | mUsed |= BIT(FrCount) | BIT(ToCount) | BIT(Count) | BIT(Tape) | BIT(BackTape); |
---|
[90bb053f] | 1875 | |
---|
[a6d094f] | 1876 | #if 0 |
---|
| 1877 | printf("mUsed = 0x%x, opt = 0x%x, mask = 0x%x\n", mUsed, |
---|
| 1878 | mask_optional[style], mask[style]); |
---|
| 1879 | #endif |
---|
[cb3d1e2] | 1880 | |
---|
[6114207] | 1881 | if (((mUsed &~ BIT(Newline)) | mask_optional[style]) != mask[style]) { |
---|
| 1882 | /* Test should only fail with too few bits set, not too many */ |
---|
[4c07c51] | 1883 | SVX_ASSERT((((mUsed &~ BIT(Newline)) | mask_optional[style]) |
---|
[6114207] | 1884 | &~ mask[style]) == 0); |
---|
[736f7df] | 1885 | /* TRANSLATORS: i.e. not enough readings for the style. */ |
---|
[cab0f26] | 1886 | compile_diagnostic(DIAG_ERR|DIAG_SKIP, /*Too few readings for data style “%s”*/64, style_name); |
---|
[ae917b96] | 1887 | free(style_name); |
---|
| 1888 | free(new_order); |
---|
[a420b49] | 1889 | return; |
---|
| 1890 | } |
---|
[d1b1380] | 1891 | |
---|
[a420b49] | 1892 | /* don't free default ordering or ordering used by parent */ |
---|
| 1893 | if (pcs->ordering != default_order && |
---|
| 1894 | !(pcs->next && pcs->next->ordering == pcs->ordering)) |
---|
[ae917b96] | 1895 | free((reading*)pcs->ordering); |
---|
[cb3d1e2] | 1896 | |
---|
[fdffa7d] | 1897 | pcs->recorded_style = pcs->style = style; |
---|
[a420b49] | 1898 | pcs->ordering = new_order; |
---|
[1b34062] | 1899 | |
---|
[ae917b96] | 1900 | free(style_name); |
---|
[ee05463] | 1901 | |
---|
[667e803c] | 1902 | reinit_style: |
---|
[ee05463] | 1903 | if (style == STYLE_PASSAGE) { |
---|
| 1904 | lrudlist * new_psg = osnew(lrudlist); |
---|
| 1905 | new_psg->tube = NULL; |
---|
| 1906 | new_psg->next = model; |
---|
| 1907 | model = new_psg; |
---|
| 1908 | next_lrud = &(new_psg->tube); |
---|
| 1909 | } |
---|
[d1b1380] | 1910 | } |
---|
| 1911 | |
---|
[a420b49] | 1912 | static void |
---|
[eb18f4d] | 1913 | cmd_units(void) |
---|
[a420b49] | 1914 | { |
---|
[5f1e194] | 1915 | int units, quantity; |
---|
[6114207] | 1916 | unsigned long qmask; |
---|
| 1917 | unsigned long m; /* mask with bit x set to indicate quantity x specified */ |
---|
[5f1e194] | 1918 | real factor; |
---|
[9f55538] | 1919 | filepos fp; |
---|
[a420b49] | 1920 | |
---|
[699bf50] | 1921 | qmask = get_qlist(BIT(Q_POS)|BIT(Q_PLUMB)|BIT(Q_LEVEL)); |
---|
| 1922 | |
---|
[647407d] | 1923 | if (!qmask) return; |
---|
[a420b49] | 1924 | if (qmask == BIT(Q_DEFAULT)) { |
---|
| 1925 | default_units(pcs); |
---|
[5f1e194] | 1926 | return; |
---|
| 1927 | } |
---|
[a420b49] | 1928 | |
---|
[9f55538] | 1929 | get_pos(&fp); |
---|
[63d4f07] | 1930 | factor = read_numeric(true); |
---|
[fa42426] | 1931 | if (factor == 0.0) { |
---|
[9f55538] | 1932 | set_pos(&fp); |
---|
[736f7df] | 1933 | /* TRANSLATORS: error message given by "*units tape 0 feet" - it’s |
---|
| 1934 | * meaningless to say your tape is marked in "0 feet" (but you might |
---|
| 1935 | * measure distance by counting knots on a diving line, and tie them |
---|
| 1936 | * every "2 feet"). */ |
---|
[d0be687d] | 1937 | compile_diagnostic(DIAG_ERR|DIAG_WORD, /**UNITS factor must be non-zero*/200); |
---|
[9f55538] | 1938 | skipline(); |
---|
[fa42426] | 1939 | return; |
---|
| 1940 | } |
---|
[a420b49] | 1941 | |
---|
[63d4f07] | 1942 | units = get_units(qmask, true); |
---|
[647407d] | 1943 | if (units == UNITS_NULL) return; |
---|
[fa42426] | 1944 | if (TSTBIT(qmask, Q_GRADIENT)) |
---|
| 1945 | pcs->f_clino_percent = (units == UNITS_PERCENT); |
---|
| 1946 | if (TSTBIT(qmask, Q_BACKGRADIENT)) |
---|
| 1947 | pcs->f_backclino_percent = (units == UNITS_PERCENT); |
---|
| 1948 | |
---|
[00b10c1] | 1949 | if (TSTBIT(qmask, Q_BEARING)) { |
---|
| 1950 | pcs->f_bearing_quadrants = (units == UNITS_QUADRANTS); |
---|
| 1951 | } |
---|
| 1952 | if (TSTBIT(qmask, Q_BACKBEARING)) { |
---|
| 1953 | pcs->f_backbearing_quadrants = (units == UNITS_QUADRANTS); |
---|
| 1954 | } |
---|
| 1955 | |
---|
[6eb50ab] | 1956 | if (factor == HUGE_REAL) { |
---|
| 1957 | factor = factor_tab[units]; |
---|
| 1958 | } else { |
---|
| 1959 | factor *= factor_tab[units]; |
---|
| 1960 | } |
---|
[5f1e194] | 1961 | |
---|
| 1962 | for (quantity = 0, m = BIT(quantity); m <= qmask; quantity++, m <<= 1) |
---|
| 1963 | if (qmask & m) pcs->units[quantity] = factor; |
---|
[d1b1380] | 1964 | } |
---|
| 1965 | |
---|
[a420b49] | 1966 | static void |
---|
[eb18f4d] | 1967 | cmd_calibrate(void) |
---|
[a420b49] | 1968 | { |
---|
[d6d3576] | 1969 | real sc, z; |
---|
[67508f0] | 1970 | unsigned long qmask, m; |
---|
[a420b49] | 1971 | int quantity; |
---|
[9f55538] | 1972 | filepos fp; |
---|
[46cb98f] | 1973 | |
---|
[da96015] | 1974 | qmask = get_qlist(BIT(Q_POS)|BIT(Q_PLUMB)|BIT(Q_LEVEL)); |
---|
[46cb98f] | 1975 | if (!qmask) return; /* error already reported */ |
---|
| 1976 | |
---|
[a420b49] | 1977 | if (qmask == BIT(Q_DEFAULT)) { |
---|
| 1978 | default_calib(pcs); |
---|
| 1979 | return; |
---|
| 1980 | } |
---|
[46cb98f] | 1981 | |
---|
[a420b49] | 1982 | if (((qmask & LEN_QMASK)) && ((qmask & ANG_QMASK))) { |
---|
[0b8c321] | 1983 | /* TRANSLATORS: e.g. |
---|
| 1984 | * |
---|
| 1985 | * *calibrate tape compass 1 1 |
---|
| 1986 | */ |
---|
[cab0f26] | 1987 | compile_diagnostic(DIAG_ERR|DIAG_SKIP, /*Can’t calibrate angular and length quantities together*/227); |
---|
[647407d] | 1988 | return; |
---|
[a420b49] | 1989 | } |
---|
[46cb98f] | 1990 | |
---|
[63d4f07] | 1991 | z = read_numeric(false); |
---|
[9f55538] | 1992 | get_pos(&fp); |
---|
[63d4f07] | 1993 | sc = read_numeric(true); |
---|
[88569fe] | 1994 | if (sc == HUGE_REAL) { |
---|
| 1995 | if (isalpha(ch)) { |
---|
[63d4f07] | 1996 | int units = get_units(qmask, false); |
---|
[88569fe] | 1997 | if (units == UNITS_NULL) { |
---|
| 1998 | return; |
---|
| 1999 | } |
---|
| 2000 | z *= factor_tab[units]; |
---|
[63d4f07] | 2001 | sc = read_numeric(true); |
---|
[88569fe] | 2002 | if (sc == HUGE_REAL) { |
---|
| 2003 | sc = (real)1.0; |
---|
| 2004 | } else { |
---|
| 2005 | /* Adjustment applied is: (reading - z) * sc |
---|
| 2006 | * We want: reading * sc - z |
---|
| 2007 | * So divide z by sc so the applied adjustment does what we want. |
---|
| 2008 | */ |
---|
| 2009 | z /= sc; |
---|
| 2010 | } |
---|
| 2011 | } else { |
---|
| 2012 | sc = (real)1.0; |
---|
| 2013 | } |
---|
| 2014 | } |
---|
| 2015 | |
---|
[a420b49] | 2016 | if (sc == HUGE_REAL) sc = (real)1.0; |
---|
[647407d] | 2017 | /* check for declination scale */ |
---|
[95c3272] | 2018 | if (TSTBIT(qmask, Q_DECLINATION) && sc != 1.0) { |
---|
[9f55538] | 2019 | set_pos(&fp); |
---|
[736f7df] | 2020 | /* TRANSLATORS: DECLINATION is a built-in keyword, so best not to |
---|
| 2021 | * translate */ |
---|
[d0be687d] | 2022 | compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Scale factor must be 1.0 for DECLINATION*/40); |
---|
[9f55538] | 2023 | skipline(); |
---|
[647407d] | 2024 | return; |
---|
| 2025 | } |
---|
[4b14118] | 2026 | if (sc == 0.0) { |
---|
[9f55538] | 2027 | set_pos(&fp); |
---|
[736f7df] | 2028 | /* TRANSLATORS: If the scale factor for an instrument is zero, then any |
---|
| 2029 | * reading would be mapped to zero, which doesn't make sense. */ |
---|
[d0be687d] | 2030 | compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Scale factor must be non-zero*/391); |
---|
[9f55538] | 2031 | skipline(); |
---|
[4b14118] | 2032 | return; |
---|
| 2033 | } |
---|
[647407d] | 2034 | for (quantity = 0, m = BIT(quantity); m <= qmask; quantity++, m <<= 1) { |
---|
[a420b49] | 2035 | if (qmask & m) { |
---|
[647407d] | 2036 | pcs->z[quantity] = pcs->units[quantity] * z; |
---|
[a420b49] | 2037 | pcs->sc[quantity] = sc; |
---|
| 2038 | } |
---|
[647407d] | 2039 | } |
---|
[d1b1380] | 2040 | } |
---|
| 2041 | |
---|
[abe7192] | 2042 | static const sztok north_tab[] = { |
---|
| 2043 | { "GRID", GRID_NORTH }, |
---|
| 2044 | { "MAGNETIC", MAGNETIC_NORTH }, |
---|
| 2045 | { "TRUE", TRUE_NORTH }, |
---|
| 2046 | { NULL, -1 } |
---|
| 2047 | }; |
---|
| 2048 | |
---|
| 2049 | static void |
---|
| 2050 | cmd_cartesian(void) |
---|
| 2051 | { |
---|
| 2052 | get_token(); |
---|
| 2053 | int north = match_tok(north_tab, TABSIZE(north_tab)); |
---|
| 2054 | if (north < 0) { |
---|
| 2055 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, |
---|
| 2056 | /*Expecting “%s”, “%s”, or “%s”*/188, |
---|
| 2057 | "GRID", "MAGNETIC", "TRUE"); |
---|
| 2058 | return; |
---|
| 2059 | } |
---|
| 2060 | pcs->cartesian_north = north; |
---|
| 2061 | pcs->cartesian_rotation = 0.0; |
---|
| 2062 | |
---|
| 2063 | skipblanks(); |
---|
| 2064 | if (!isEol(ch) && !isComm(ch)) { |
---|
| 2065 | real rotation = read_numeric(false); |
---|
| 2066 | // Accept the same units as *declination does. |
---|
| 2067 | int units = get_units(BIT(Q_DECLINATION), false); |
---|
| 2068 | if (units == UNITS_NULL) { |
---|
| 2069 | return; |
---|
| 2070 | } |
---|
| 2071 | pcs->cartesian_rotation = rotation * factor_tab[units]; |
---|
| 2072 | } |
---|
| 2073 | } |
---|
| 2074 | |
---|
[58c7b459] | 2075 | static void |
---|
| 2076 | cmd_declination(void) |
---|
| 2077 | { |
---|
[63d4f07] | 2078 | real v = read_numeric(true); |
---|
[58c7b459] | 2079 | if (v == HUGE_REAL) { |
---|
[e1cbc0d] | 2080 | get_token_legacy_no_blanks(); |
---|
[0532954] | 2081 | if (!S_EQ(&uctoken, "AUTO")) { |
---|
[d72396e] | 2082 | compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_COL, /*Expected number or “AUTO”*/309); |
---|
[58c7b459] | 2083 | return; |
---|
| 2084 | } |
---|
[d655496] | 2085 | do_legacy_token_warning(); |
---|
[b39e24a] | 2086 | if (!pcs->proj_str) { |
---|
[abe7192] | 2087 | // TRANSLATORS: %s is replaced by the command that requires it, e.g. |
---|
| 2088 | // *DECLINATION AUTO |
---|
[d655496] | 2089 | compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_TOKEN, |
---|
| 2090 | /*Input coordinate system must be specified for “%s”*/301, |
---|
[abe7192] | 2091 | "*DECLINATION AUTO"); |
---|
[58c7b459] | 2092 | return; |
---|
| 2093 | } |
---|
[d655496] | 2094 | |
---|
| 2095 | /* *declination auto X Y Z */ |
---|
| 2096 | real x = read_numeric(false); |
---|
| 2097 | real y = read_numeric(false); |
---|
| 2098 | real z = read_numeric(false); |
---|
[69a9e01] | 2099 | set_declination_location(x, y, z, pcs->proj_str); |
---|
[58c7b459] | 2100 | } else { |
---|
| 2101 | /* *declination D UNITS */ |
---|
[63d4f07] | 2102 | int units = get_units(BIT(Q_DECLINATION), false); |
---|
[58c7b459] | 2103 | if (units == UNITS_NULL) { |
---|
| 2104 | return; |
---|
| 2105 | } |
---|
[85d8e0c] | 2106 | pcs->z[Q_DECLINATION] = -v * factor_tab[units]; |
---|
[2c17123e] | 2107 | pcs->convergence = 0; |
---|
[58c7b459] | 2108 | } |
---|
| 2109 | } |
---|
| 2110 | |
---|
[7f1ab95] | 2111 | #ifndef NO_DEPRECATED |
---|
[a420b49] | 2112 | static void |
---|
[eb18f4d] | 2113 | cmd_default(void) |
---|
[a420b49] | 2114 | { |
---|
[82919e07] | 2115 | static const sztok defaulttab[] = { |
---|
[c0563da] | 2116 | { "CALIBRATE", CMD_CALIBRATE }, |
---|
| 2117 | { "DATA", CMD_DATA }, |
---|
| 2118 | { "UNITS", CMD_UNITS }, |
---|
[a4ae909] | 2119 | { NULL, CMD_NULL } |
---|
[c0563da] | 2120 | }; |
---|
[c86cc71] | 2121 | static int default_depr_count = 0; |
---|
| 2122 | |
---|
| 2123 | if (default_depr_count < 5) { |
---|
[736f7df] | 2124 | /* TRANSLATORS: If you're unsure what "deprecated" means, see: |
---|
[2c1c52e] | 2125 | * https://en.wikipedia.org/wiki/Deprecation */ |
---|
[68f7ba4] | 2126 | compile_diagnostic(DIAG_WARN|DIAG_TOKEN, /**DEFAULT is deprecated - use *CALIBRATE/DATA/SD/UNITS with argument DEFAULT instead*/20); |
---|
[c86cc71] | 2127 | if (++default_depr_count == 5) |
---|
[64544daf] | 2128 | compile_diagnostic(DIAG_INFO, /*Further uses of this deprecated feature will not be reported*/95); |
---|
[c86cc71] | 2129 | } |
---|
| 2130 | |
---|
[cb3d1e2] | 2131 | get_token(); |
---|
[c0563da] | 2132 | switch (match_tok(defaulttab, TABSIZE(defaulttab))) { |
---|
| 2133 | case CMD_CALIBRATE: |
---|
[5f1e194] | 2134 | default_calib(pcs); |
---|
[c0563da] | 2135 | break; |
---|
| 2136 | case CMD_DATA: |
---|
[5f1e194] | 2137 | default_style(pcs); |
---|
| 2138 | default_grade(pcs); |
---|
[abe7192] | 2139 | pcs->cartesian_north = TRUE_NORTH; |
---|
| 2140 | pcs->cartesian_rotation = 0.0; |
---|
[c0563da] | 2141 | break; |
---|
| 2142 | case CMD_UNITS: |
---|
[5f1e194] | 2143 | default_units(pcs); |
---|
[c0563da] | 2144 | break; |
---|
| 2145 | default: |
---|
[caae6cd] | 2146 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Unknown setting “%s”*/41, |
---|
[0532954] | 2147 | s_str(&token)); |
---|
[5f1e194] | 2148 | } |
---|
[d1b1380] | 2149 | } |
---|
[7f1ab95] | 2150 | #endif |
---|
[d1b1380] | 2151 | |
---|
[a420b49] | 2152 | static void |
---|
[eb18f4d] | 2153 | cmd_include(void) |
---|
[a420b49] | 2154 | { |
---|
[0532954] | 2155 | char *pth = NULL; |
---|
| 2156 | string fnm = S_INIT; |
---|
[a882316] | 2157 | #ifndef NO_DEPRECATED |
---|
[5f1e194] | 2158 | prefix *root_store; |
---|
[a882316] | 2159 | #endif |
---|
[5f1e194] | 2160 | int ch_store; |
---|
| 2161 | |
---|
[f97076a] | 2162 | pth = path_from_fnm(file.filename); |
---|
| 2163 | |
---|
[0532954] | 2164 | read_string(&fnm); |
---|
[d1b1380] | 2165 | |
---|
[a882316] | 2166 | #ifndef NO_DEPRECATED |
---|
| 2167 | /* Since *begin / *end nesting cannot cross file boundaries we only |
---|
| 2168 | * need to preserve the prefix if the deprecated *prefix command |
---|
| 2169 | * can be used */ |
---|
[5f1e194] | 2170 | root_store = root; |
---|
| 2171 | root = pcs->Prefix; /* Root for include file is current prefix */ |
---|
[a882316] | 2172 | #endif |
---|
[5f1e194] | 2173 | ch_store = ch; |
---|
[cb3d1e2] | 2174 | |
---|
[0532954] | 2175 | data_file(pth, s_str(&fnm)); |
---|
[a420b49] | 2176 | |
---|
[a882316] | 2177 | #ifndef NO_DEPRECATED |
---|
[5f1e194] | 2178 | root = root_store; /* and restore root */ |
---|
[647407d] | 2179 | #endif |
---|
[5f1e194] | 2180 | ch = ch_store; |
---|
[d1b1380] | 2181 | |
---|
[a420b49] | 2182 | s_free(&fnm); |
---|
[ae917b96] | 2183 | free(pth); |
---|
[d1b1380] | 2184 | } |
---|
| 2185 | |
---|
[a420b49] | 2186 | static void |
---|
[eb18f4d] | 2187 | cmd_sd(void) |
---|
[a420b49] | 2188 | { |
---|
[5f1e194] | 2189 | real sd, variance; |
---|
| 2190 | int units; |
---|
[67508f0] | 2191 | unsigned long qmask, m; |
---|
[5f1e194] | 2192 | int quantity; |
---|
[b14f44f] | 2193 | qmask = get_qlist(BIT(Q_DECLINATION)); |
---|
[46cb98f] | 2194 | if (!qmask) return; /* no quantities found - error already reported */ |
---|
| 2195 | |
---|
[a420b49] | 2196 | if (qmask == BIT(Q_DEFAULT)) { |
---|
| 2197 | default_grade(pcs); |
---|
[5f1e194] | 2198 | return; |
---|
| 2199 | } |
---|
[63d4f07] | 2200 | sd = read_numeric(false); |
---|
[d6d3576] | 2201 | if (sd <= (real)0.0) { |
---|
[cab0f26] | 2202 | compile_diagnostic(DIAG_ERR|DIAG_SKIP|DIAG_COL, /*Standard deviation must be positive*/48); |
---|
[d6d3576] | 2203 | return; |
---|
[5f1e194] | 2204 | } |
---|
[63d4f07] | 2205 | units = get_units(qmask, false); |
---|
[647407d] | 2206 | if (units == UNITS_NULL) return; |
---|
[5f1e194] | 2207 | |
---|
| 2208 | sd *= factor_tab[units]; |
---|
| 2209 | variance = sqrd(sd); |
---|
| 2210 | |
---|
| 2211 | for (quantity = 0, m = BIT(quantity); m <= qmask; quantity++, m <<= 1) |
---|
| 2212 | if (qmask & m) pcs->Var[quantity] = variance; |
---|
[d1b1380] | 2213 | } |
---|
[5f1e194] | 2214 | |
---|
[0f8216c] | 2215 | enum { |
---|
| 2216 | ROLE_BACKTAPE, |
---|
| 2217 | ROLE_BACKCOMPASS, |
---|
| 2218 | ROLE_BACKCLINO, |
---|
| 2219 | ROLE_TAPE, |
---|
| 2220 | ROLE_COMPASS, |
---|
| 2221 | ROLE_CLINO, |
---|
| 2222 | ROLE_COUNTER, |
---|
| 2223 | ROLE_DEPTH, |
---|
| 2224 | ROLE_STATION, |
---|
| 2225 | ROLE_POSITION, |
---|
| 2226 | ROLE_NOTES, |
---|
| 2227 | ROLE_PICTURES, |
---|
| 2228 | ROLE_INSTRUMENTS, |
---|
| 2229 | ROLE_ASSISTANT, |
---|
| 2230 | ROLE_ALTITUDE, |
---|
| 2231 | ROLE_DIMENSIONS, |
---|
| 2232 | ROLE_LEFT, |
---|
| 2233 | ROLE_RIGHT, |
---|
| 2234 | ROLE_UP, |
---|
| 2235 | ROLE_DOWN, |
---|
| 2236 | ROLE_EXPLORER |
---|
| 2237 | }; |
---|
| 2238 | |
---|
| 2239 | static const sztok role_tab[] = { |
---|
| 2240 | {"ALTITUDE", ROLE_ALTITUDE}, |
---|
| 2241 | {"ASSISTANT", ROLE_ASSISTANT}, |
---|
| 2242 | {"BACKBEARING", ROLE_BACKCOMPASS}, |
---|
| 2243 | {"BACKCLINO", ROLE_BACKCLINO}, |
---|
| 2244 | {"BACKCOMPASS", ROLE_BACKCOMPASS}, |
---|
| 2245 | {"BACKGRADIENT", ROLE_BACKCLINO}, |
---|
| 2246 | {"BACKLENGTH", ROLE_BACKTAPE}, |
---|
| 2247 | {"BACKTAPE", ROLE_BACKTAPE}, |
---|
| 2248 | {"BEARING", ROLE_COMPASS}, |
---|
| 2249 | {"CEILING", ROLE_UP}, |
---|
| 2250 | {"CLINO", ROLE_CLINO}, |
---|
| 2251 | {"COMPASS", ROLE_COMPASS}, |
---|
| 2252 | {"COUNT", ROLE_COUNTER}, |
---|
| 2253 | {"COUNTER", ROLE_COUNTER}, |
---|
| 2254 | {"DEPTH", ROLE_DEPTH}, |
---|
| 2255 | {"DIMENSIONS", ROLE_DIMENSIONS}, |
---|
| 2256 | {"DOG", ROLE_ASSISTANT}, |
---|
| 2257 | {"DOWN", ROLE_DOWN}, |
---|
| 2258 | {"DZ", ROLE_ALTITUDE}, |
---|
| 2259 | {"EXPLORER", ROLE_EXPLORER}, |
---|
| 2260 | {"FLOOR", ROLE_DOWN}, |
---|
| 2261 | {"GRADIENT", ROLE_CLINO}, |
---|
| 2262 | {"INSTRUMENTS", ROLE_INSTRUMENTS}, |
---|
| 2263 | {"INSTS", ROLE_INSTRUMENTS}, |
---|
| 2264 | {"LEFT", ROLE_LEFT}, |
---|
| 2265 | {"LENGTH", ROLE_TAPE}, |
---|
| 2266 | {"NOTEBOOK", ROLE_NOTES}, |
---|
| 2267 | {"NOTES", ROLE_NOTES}, |
---|
| 2268 | {"PICS", ROLE_PICTURES}, |
---|
| 2269 | {"PICTURES", ROLE_PICTURES}, |
---|
| 2270 | {"POSITION", ROLE_POSITION}, |
---|
| 2271 | {"RIGHT", ROLE_RIGHT}, |
---|
| 2272 | {"STATION", ROLE_STATION}, |
---|
| 2273 | {"TAPE", ROLE_TAPE}, |
---|
| 2274 | {"UP", ROLE_UP}, |
---|
| 2275 | {NULL, -1} |
---|
| 2276 | }; |
---|
| 2277 | |
---|
| 2278 | static void |
---|
| 2279 | cmd_team(void) |
---|
| 2280 | { |
---|
| 2281 | string name = S_INIT; |
---|
[62be3ee] | 2282 | if (!read_string_warning(&name)) { |
---|
| 2283 | skipline(); |
---|
| 2284 | return; |
---|
| 2285 | } |
---|
[0f8216c] | 2286 | s_free(&name); |
---|
| 2287 | |
---|
| 2288 | while (true) { |
---|
| 2289 | skipblanks(); |
---|
| 2290 | if (isComm(ch) || isEol(ch)) return; |
---|
| 2291 | get_token(); |
---|
| 2292 | int role = match_tok(role_tab, TABSIZE(role_tab)); |
---|
| 2293 | if (role < 0) { |
---|
| 2294 | // Skip after a bad role to avoid triggering multiple warnings for |
---|
| 2295 | // one *team command in existing data from before this check was |
---|
| 2296 | // implemented. |
---|
| 2297 | compile_diagnostic(DIAG_WARN|DIAG_TOKEN|DIAG_SKIP, /*Unknown team role “%s”*/532, |
---|
| 2298 | s_str(&token)); |
---|
[6b4d2e9] | 2299 | return; |
---|
[0f8216c] | 2300 | } |
---|
| 2301 | } |
---|
| 2302 | } |
---|
| 2303 | |
---|
[a420b49] | 2304 | static void |
---|
[eb18f4d] | 2305 | cmd_title(void) |
---|
[a420b49] | 2306 | { |
---|
[1925d66] | 2307 | if (!fExplicitTitle && pcs->Prefix == root) { |
---|
| 2308 | /* If we don't have an explicit title yet, and we're currently in the |
---|
| 2309 | * root prefix, use this title explicitly. */ |
---|
[63d4f07] | 2310 | fExplicitTitle = true; |
---|
[0532954] | 2311 | read_string(&survey_title); |
---|
[a420b49] | 2312 | } else { |
---|
| 2313 | /* parse and throw away this title (but still check rest of line) */ |
---|
[0532954] | 2314 | string s = S_INIT; |
---|
| 2315 | read_string(&s); |
---|
[a420b49] | 2316 | s_free(&s); |
---|
| 2317 | } |
---|
| 2318 | } |
---|
| 2319 | |
---|
[82919e07] | 2320 | static const sztok case_tab[] = { |
---|
[a420b49] | 2321 | {"PRESERVE", OFF}, |
---|
[c57e9da] | 2322 | {"TOLOWER", LOWER}, |
---|
| 2323 | {"TOUPPER", UPPER}, |
---|
[a420b49] | 2324 | {NULL, -1} |
---|
| 2325 | }; |
---|
[cb3d1e2] | 2326 | |
---|
[a420b49] | 2327 | static void |
---|
[eb18f4d] | 2328 | cmd_case(void) |
---|
[a420b49] | 2329 | { |
---|
| 2330 | int setting; |
---|
| 2331 | get_token(); |
---|
| 2332 | setting = match_tok(case_tab, TABSIZE(case_tab)); |
---|
| 2333 | if (setting != -1) { |
---|
| 2334 | pcs->Case = setting; |
---|
| 2335 | } else { |
---|
[caae6cd] | 2336 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Found “%s”, expecting “PRESERVE”, “TOUPPER”, or “TOLOWER”*/10, s_str(&token)); |
---|
[a420b49] | 2337 | } |
---|
| 2338 | } |
---|
| 2339 | |
---|
[f15c53d9] | 2340 | static void |
---|
| 2341 | cmd_copyright(void) |
---|
| 2342 | { |
---|
| 2343 | skipblanks(); |
---|
| 2344 | filepos fp; |
---|
| 2345 | get_pos(&fp); |
---|
| 2346 | unsigned y1 = read_uint_raw(DIAG_WARN|DIAG_UINT, /*Invalid year*/534, NULL); |
---|
| 2347 | if (y1 < 1000) { |
---|
| 2348 | set_pos(&fp); |
---|
| 2349 | compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid year*/534, y1); |
---|
| 2350 | } |
---|
| 2351 | if (ch == '-') { |
---|
| 2352 | nextch(); |
---|
| 2353 | get_pos(&fp); |
---|
| 2354 | unsigned y2 = read_uint_raw(DIAG_WARN|DIAG_UINT, /*Invalid year*/534, NULL); |
---|
| 2355 | if (y2 < 1000) { |
---|
| 2356 | set_pos(&fp); |
---|
| 2357 | compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid year*/534, y2); |
---|
| 2358 | } else if (y2 < y1) { |
---|
| 2359 | set_pos(&fp); |
---|
| 2360 | compile_diagnostic(DIAG_WARN|DIAG_UINT, /*End of date range is before the start*/81); |
---|
| 2361 | } |
---|
| 2362 | } |
---|
| 2363 | |
---|
| 2364 | string text = S_INIT; |
---|
| 2365 | if (!read_string_warning(&text)) { |
---|
| 2366 | skipline(); |
---|
| 2367 | return; |
---|
| 2368 | } |
---|
| 2369 | s_free(&text); |
---|
| 2370 | |
---|
| 2371 | skipblanks(); |
---|
| 2372 | if (!isComm(ch) && !isEol(ch)) |
---|
| 2373 | compile_diagnostic(DIAG_WARN|DIAG_TAIL, /*End of line not blank*/15); |
---|
| 2374 | } |
---|
| 2375 | |
---|
[abd0310] | 2376 | typedef enum { |
---|
| 2377 | CS_NONE = -1, |
---|
| 2378 | CS_CUSTOM, |
---|
| 2379 | CS_EPSG, |
---|
| 2380 | CS_ESRI, |
---|
[b8fb5ac] | 2381 | CS_EUR79Z30, |
---|
[abd0310] | 2382 | CS_IJTSK, |
---|
[b8fb5ac] | 2383 | CS_IJTSK03, |
---|
[abd0310] | 2384 | CS_JTSK, |
---|
[b8fb5ac] | 2385 | CS_JTSK03, |
---|
[abd0310] | 2386 | CS_LAT, |
---|
| 2387 | CS_LOCAL, |
---|
| 2388 | CS_LONG, |
---|
| 2389 | CS_OSGB, |
---|
| 2390 | CS_S_MERC, |
---|
| 2391 | CS_UTM |
---|
| 2392 | } cs_class; |
---|
| 2393 | |
---|
[82919e07] | 2394 | static const sztok cs_tab[] = { |
---|
[b8fb5ac] | 2395 | {"CUSTOM", CS_CUSTOM}, |
---|
| 2396 | {"EPSG", CS_EPSG}, /* EPSG:<number> */ |
---|
| 2397 | {"ESRI", CS_ESRI}, /* ESRI:<number> */ |
---|
| 2398 | {"EUR79Z30", CS_EUR79Z30}, |
---|
| 2399 | {"IJTSK", CS_IJTSK}, |
---|
| 2400 | {"IJTSK03", CS_IJTSK03}, |
---|
| 2401 | {"JTSK", CS_JTSK}, |
---|
| 2402 | {"JTSK03", CS_JTSK03}, |
---|
| 2403 | {"LAT", CS_LAT}, /* LAT-LONG */ |
---|
| 2404 | {"LOCAL", CS_LOCAL}, |
---|
| 2405 | {"LONG", CS_LONG}, /* LONG-LAT */ |
---|
| 2406 | {"OSGB", CS_OSGB}, /* OSGB:<H, N, O, S or T><A-Z except I> */ |
---|
| 2407 | {"S", CS_S_MERC}, /* S-MERC */ |
---|
| 2408 | // UTM<zone><N or S or nothing> is handled separately to avoid needing 180 |
---|
| 2409 | // entries in this lookup table. |
---|
| 2410 | {NULL, CS_NONE} |
---|
[abd0310] | 2411 | }; |
---|
| 2412 | |
---|
| 2413 | static void |
---|
| 2414 | cmd_cs(void) |
---|
| 2415 | { |
---|
[0532954] | 2416 | char *proj_str = NULL; |
---|
[abd0310] | 2417 | cs_class cs; |
---|
| 2418 | int cs_sub = INT_MIN; |
---|
| 2419 | filepos fp; |
---|
[63d4f07] | 2420 | bool output = false; |
---|
[c092d72] | 2421 | enum { YES, NO, MAYBE } ok_for_output = YES; |
---|
[63d4f07] | 2422 | static bool had_cs = false; |
---|
[56db37f] | 2423 | |
---|
| 2424 | if (!had_cs) { |
---|
[63d4f07] | 2425 | had_cs = true; |
---|
[56db37f] | 2426 | if (first_fix_name) { |
---|
[cab0f26] | 2427 | compile_diagnostic_at(DIAG_ERR, |
---|
| 2428 | first_fix_filename, first_fix_line, |
---|
| 2429 | /*Station “%s” fixed before CS command first used*/442, |
---|
| 2430 | sprint_prefix(first_fix_name)); |
---|
[56db37f] | 2431 | } |
---|
| 2432 | } |
---|
[abd0310] | 2433 | |
---|
[b8fb5ac] | 2434 | skipblanks(); |
---|
[abd0310] | 2435 | get_pos(&fp); |
---|
[b8fb5ac] | 2436 | get_token_no_blanks(); |
---|
[0532954] | 2437 | if (S_EQ(&uctoken, "OUT")) { |
---|
[63d4f07] | 2438 | output = true; |
---|
[b8fb5ac] | 2439 | skipblanks(); |
---|
[9f55538] | 2440 | get_pos(&fp); |
---|
[b8fb5ac] | 2441 | get_token_no_blanks(); |
---|
| 2442 | } |
---|
| 2443 | |
---|
| 2444 | if (s_len(&uctoken) > 3 && |
---|
| 2445 | memcmp(s_str(&uctoken), "UTM", 3) == 0 && |
---|
| 2446 | isdigit((unsigned char)s_str(&uctoken)[3])) { |
---|
| 2447 | // The token starts "UTM" followed by a digit so handle that separately |
---|
| 2448 | // to avoid needing 180 entries for UTM zones in the cs_tab lookup |
---|
| 2449 | // table. |
---|
| 2450 | cs = CS_UTM; |
---|
| 2451 | // Reposition on the digit after "UTM". |
---|
| 2452 | set_pos(&fp); |
---|
| 2453 | nextch(); |
---|
| 2454 | nextch(); |
---|
| 2455 | nextch(); |
---|
| 2456 | unsigned n = read_uint(); |
---|
| 2457 | if (n >= 1 && n <= 60) { |
---|
| 2458 | int uch = toupper(ch); |
---|
| 2459 | cs_sub = (int)n; |
---|
| 2460 | if (uch == 'S') { |
---|
[abd0310] | 2461 | nextch(); |
---|
[b8fb5ac] | 2462 | cs_sub = -cs_sub; |
---|
| 2463 | } else if (uch == 'N') { |
---|
| 2464 | nextch(); |
---|
| 2465 | } |
---|
| 2466 | } |
---|
| 2467 | } else { |
---|
| 2468 | cs = match_tok(cs_tab, TABSIZE(cs_tab)); |
---|
| 2469 | switch (cs) { |
---|
| 2470 | case CS_NONE: |
---|
| 2471 | break; |
---|
| 2472 | case CS_CUSTOM: |
---|
| 2473 | ok_for_output = MAYBE; |
---|
| 2474 | get_pos(&fp); |
---|
| 2475 | string str = S_INIT; |
---|
| 2476 | read_string(&str); |
---|
| 2477 | proj_str = s_steal(&str); |
---|
| 2478 | cs_sub = 0; |
---|
| 2479 | break; |
---|
| 2480 | case CS_EPSG: case CS_ESRI: |
---|
| 2481 | ok_for_output = MAYBE; |
---|
| 2482 | if (ch == ':' && isdigit(nextch())) { |
---|
| 2483 | unsigned n = read_uint(); |
---|
| 2484 | if (n < 1000000) { |
---|
| 2485 | cs_sub = (int)n; |
---|
[abd0310] | 2486 | } |
---|
[b8fb5ac] | 2487 | } |
---|
| 2488 | break; |
---|
| 2489 | case CS_EUR79Z30: |
---|
| 2490 | cs_sub = 0; |
---|
| 2491 | break; |
---|
| 2492 | case CS_JTSK: |
---|
| 2493 | case CS_JTSK03: |
---|
| 2494 | ok_for_output = NO; |
---|
| 2495 | cs_sub = 0; |
---|
| 2496 | break; |
---|
| 2497 | case CS_IJTSK: |
---|
| 2498 | case CS_IJTSK03: |
---|
| 2499 | cs_sub = 0; |
---|
| 2500 | break; |
---|
| 2501 | case CS_LAT: case CS_LONG: |
---|
| 2502 | ok_for_output = NO; |
---|
| 2503 | if (ch == '-') { |
---|
| 2504 | nextch(); |
---|
| 2505 | get_token_no_blanks(); |
---|
| 2506 | cs_class cs2 = match_tok(cs_tab, TABSIZE(cs_tab)); |
---|
| 2507 | if ((cs ^ cs2) == (CS_LAT ^ CS_LONG)) { |
---|
| 2508 | cs_sub = 0; |
---|
[abd0310] | 2509 | } |
---|
[b8fb5ac] | 2510 | } |
---|
| 2511 | break; |
---|
| 2512 | case CS_LOCAL: |
---|
| 2513 | cs_sub = 0; |
---|
| 2514 | break; |
---|
| 2515 | case CS_OSGB: |
---|
| 2516 | if (ch == ':') { |
---|
| 2517 | int uch1 = toupper(nextch()); |
---|
| 2518 | if (strchr("HNOST", uch1)) { |
---|
| 2519 | int uch2 = toupper(nextch()); |
---|
| 2520 | if (uch2 >= 'A' && uch2 <= 'Z' && uch2 != 'I') { |
---|
| 2521 | int x, y; |
---|
| 2522 | nextch(); |
---|
| 2523 | if (uch1 > 'I') --uch1; |
---|
| 2524 | uch1 -= 'A'; |
---|
| 2525 | if (uch2 > 'I') --uch2; |
---|
| 2526 | uch2 -= 'A'; |
---|
| 2527 | x = uch1 % 5; |
---|
| 2528 | y = uch1 / 5; |
---|
| 2529 | x = (x * 5) + uch2 % 5; |
---|
| 2530 | y = (y * 5) + uch2 / 5; |
---|
| 2531 | cs_sub = y * 25 + x; |
---|
| 2532 | } |
---|
| 2533 | } |
---|
| 2534 | } |
---|
| 2535 | break; |
---|
| 2536 | case CS_S_MERC: |
---|
| 2537 | if (ch == '-') { |
---|
| 2538 | nextch(); |
---|
| 2539 | get_token_no_blanks(); |
---|
| 2540 | if (S_EQ(&uctoken, "MERC")) { |
---|
| 2541 | cs_sub = 0; |
---|
| 2542 | } |
---|
| 2543 | } |
---|
| 2544 | break; |
---|
| 2545 | case CS_UTM: |
---|
| 2546 | // Handled outside of this switch, but avoid compiler warning about |
---|
| 2547 | // unhandled enumeration value. |
---|
| 2548 | break; |
---|
| 2549 | } |
---|
[abd0310] | 2550 | } |
---|
| 2551 | if (cs_sub == INT_MIN || isalnum(ch)) { |
---|
| 2552 | set_pos(&fp); |
---|
[b8fb5ac] | 2553 | compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Unknown coordinate system*/434); |
---|
[9f55538] | 2554 | skipline(); |
---|
[c092d72] | 2555 | return; |
---|
[abd0310] | 2556 | } |
---|
| 2557 | /* Actually handle the cs */ |
---|
[c092d72] | 2558 | switch (cs) { |
---|
[5d36f97] | 2559 | case CS_NONE: |
---|
| 2560 | break; |
---|
| 2561 | case CS_CUSTOM: |
---|
| 2562 | /* proj_str already set */ |
---|
| 2563 | break; |
---|
[ddd24f28] | 2564 | case CS_EPSG: |
---|
| 2565 | proj_str = osmalloc(32); |
---|
[657fcee] | 2566 | snprintf(proj_str, 32, "EPSG:%d", cs_sub); |
---|
[ddd24f28] | 2567 | break; |
---|
| 2568 | case CS_ESRI: |
---|
| 2569 | proj_str = osmalloc(32); |
---|
[657fcee] | 2570 | snprintf(proj_str, 32, "ESRI:%d", cs_sub); |
---|
[ddd24f28] | 2571 | break; |
---|
[b8fb5ac] | 2572 | case CS_EUR79Z30: |
---|
[ddd24f28] | 2573 | proj_str = osstrdup("+proj=utm +zone=30 +ellps=intl +towgs84=-86,-98,-119,0,0,0,0 +no_defs"); |
---|
| 2574 | break; |
---|
[5598e2c] | 2575 | case CS_IJTSK: |
---|
[b8fb5ac] | 2576 | proj_str = osstrdup("+proj=krovak +ellps=bessel +towgs84=570.8285,85.6769,462.842,4.9984,1.5867,5.2611,3.5623 +no_defs"); |
---|
| 2577 | break; |
---|
| 2578 | case CS_IJTSK03: |
---|
| 2579 | proj_str = osstrdup("+proj=krovak +ellps=bessel +towgs84=485.021,169.465,483.839,7.786342,4.397554,4.102655,0 +no_defs"); |
---|
[5598e2c] | 2580 | break; |
---|
[10af28e] | 2581 | case CS_JTSK: |
---|
[b8fb5ac] | 2582 | 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"); |
---|
| 2583 | break; |
---|
| 2584 | case CS_JTSK03: |
---|
| 2585 | proj_str = osstrdup("+proj=krovak +czech +ellps=bessel +towgs84=485.021,169.465,483.839,7.786342,4.397554,4.102655,0 +no_defs"); |
---|
[10af28e] | 2586 | break; |
---|
[16734b2] | 2587 | case CS_LAT: |
---|
| 2588 | /* FIXME: Requires PROJ >= 4.8.0 for +axis, and the SDs will be |
---|
[10af28e] | 2589 | * misapplied, so we may want to swap ourselves. Also, while |
---|
| 2590 | * therion supports lat-long, I'm not totally convinced that it is |
---|
| 2591 | * sensible to do so - people often say "lat-long", but probably |
---|
| 2592 | * don't think that that's actually "Northing, Easting". This |
---|
| 2593 | * seems like it'll result in people accidentally getting X and Y |
---|
| 2594 | * swapped in their fixed points... |
---|
| 2595 | */ |
---|
[5d36f97] | 2596 | #if 0 |
---|
[cb0a137] | 2597 | proj_str = osstrdup("+proj=longlat +ellps=WGS84 +datum=WGS84 +axis=neu +no_defs"); |
---|
[5d36f97] | 2598 | #endif |
---|
[16734b2] | 2599 | break; |
---|
[10af28e] | 2600 | case CS_LOCAL: |
---|
| 2601 | /* FIXME: Is it useful to be able to explicitly specify this? */ |
---|
| 2602 | break; |
---|
[16734b2] | 2603 | case CS_LONG: |
---|
[b39e24a] | 2604 | proj_str = osstrdup("EPSG:4326"); |
---|
[16734b2] | 2605 | break; |
---|
[a4cd4eea] | 2606 | case CS_OSGB: { |
---|
| 2607 | int x = 14 - (cs_sub % 25); |
---|
| 2608 | int y = (cs_sub / 25) - 20; |
---|
| 2609 | proj_str = osmalloc(160); |
---|
[657fcee] | 2610 | snprintf(proj_str, 160, |
---|
| 2611 | "+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 " |
---|
| 2612 | "+x_0=%d +y_0=%d +ellps=airy +datum=OSGB36 +units=m +no_defs", |
---|
| 2613 | x * 100000, y * 100000); |
---|
[a4cd4eea] | 2614 | break; |
---|
| 2615 | } |
---|
[2076d59] | 2616 | case CS_S_MERC: |
---|
[b39e24a] | 2617 | proj_str = osstrdup("EPSG:3857"); |
---|
[2076d59] | 2618 | break; |
---|
[c092d72] | 2619 | case CS_UTM: |
---|
[b39e24a] | 2620 | proj_str = osmalloc(32); |
---|
[c092d72] | 2621 | if (cs_sub > 0) { |
---|
[657fcee] | 2622 | snprintf(proj_str, 32, "EPSG:%d", 32600 + cs_sub); |
---|
[c092d72] | 2623 | } else { |
---|
[657fcee] | 2624 | snprintf(proj_str, 32, "EPSG:%d", 32700 - cs_sub); |
---|
[c092d72] | 2625 | } |
---|
| 2626 | break; |
---|
| 2627 | } |
---|
| 2628 | |
---|
[10af28e] | 2629 | if (!proj_str) { |
---|
| 2630 | /* printf("CS %d:%d\n", (int)cs, cs_sub); */ |
---|
| 2631 | set_pos(&fp); |
---|
[88ba3b1] | 2632 | compile_diagnostic(DIAG_ERR|DIAG_STRING, /*Unknown coordinate system*/434); |
---|
[9f55538] | 2633 | skipline(); |
---|
[10af28e] | 2634 | return; |
---|
| 2635 | } |
---|
| 2636 | |
---|
[c092d72] | 2637 | if (output) { |
---|
| 2638 | if (ok_for_output == NO) { |
---|
| 2639 | set_pos(&fp); |
---|
[88ba3b1] | 2640 | compile_diagnostic(DIAG_ERR|DIAG_STRING, /*Coordinate system unsuitable for output*/435); |
---|
[9f55538] | 2641 | skipline(); |
---|
[c092d72] | 2642 | return; |
---|
| 2643 | } |
---|
| 2644 | |
---|
[da9163b] | 2645 | if (proj_str_out && strcmp(proj_str, proj_str_out) == 0) { |
---|
| 2646 | /* Same as the output cs that's already set, so nothing to do. */ |
---|
[ae917b96] | 2647 | free(proj_str); |
---|
[da9163b] | 2648 | return; |
---|
| 2649 | } |
---|
| 2650 | |
---|
| 2651 | if (ok_for_output == MAYBE) { |
---|
| 2652 | /* We only actually create the transformation from input to output when |
---|
| 2653 | * we need it, but for a custom proj string or EPSG/ESRI code we need |
---|
| 2654 | * to check that the specified coordinate system is valid and also if |
---|
| 2655 | * it's suitable for output so we need to test creating it here. |
---|
| 2656 | */ |
---|
[df1d579] | 2657 | proj_errno_reset(NULL); |
---|
[da9163b] | 2658 | PJ* pj = proj_create(PJ_DEFAULT_CTX, proj_str); |
---|
| 2659 | if (!pj) { |
---|
| 2660 | set_pos(&fp); |
---|
[88ba3b1] | 2661 | compile_diagnostic(DIAG_ERR|DIAG_STRING, /*Invalid coordinate system: %s*/443, |
---|
[d9d8f21] | 2662 | proj_context_errno_string(PJ_DEFAULT_CTX, |
---|
| 2663 | proj_context_errno(PJ_DEFAULT_CTX))); |
---|
[da9163b] | 2664 | skipline(); |
---|
[ae917b96] | 2665 | free(proj_str); |
---|
[da9163b] | 2666 | return; |
---|
| 2667 | } |
---|
| 2668 | int type = proj_get_type(pj); |
---|
| 2669 | if (type == PJ_TYPE_GEOGRAPHIC_2D_CRS || |
---|
| 2670 | type == PJ_TYPE_GEOGRAPHIC_3D_CRS) { |
---|
| 2671 | set_pos(&fp); |
---|
[88ba3b1] | 2672 | compile_diagnostic(DIAG_ERR|DIAG_STRING, /*Coordinate system unsuitable for output*/435); |
---|
[da9163b] | 2673 | skipline(); |
---|
[ae917b96] | 2674 | free(proj_str); |
---|
[da9163b] | 2675 | return; |
---|
| 2676 | } |
---|
| 2677 | } |
---|
| 2678 | |
---|
| 2679 | if (proj_str_out) { |
---|
| 2680 | /* If the output cs is already set, subsequent attempts to set it |
---|
| 2681 | * are silently ignored (so you can combine two datasets and set |
---|
| 2682 | * the output cs to use before you include either). |
---|
| 2683 | */ |
---|
[ae917b96] | 2684 | free(proj_str); |
---|
[da9163b] | 2685 | } else { |
---|
| 2686 | proj_str_out = proj_str; |
---|
[c092d72] | 2687 | } |
---|
| 2688 | } else { |
---|
| 2689 | if (proj_str_out && strcmp(proj_str, proj_str_out) == 0) { |
---|
[da9163b] | 2690 | /* Same as the current output projection, so valid for input. */ |
---|
| 2691 | } else if (pcs->proj_str && strcmp(proj_str, pcs->proj_str) == 0) { |
---|
| 2692 | /* Same as the current input projection, so nothing to do! */ |
---|
| 2693 | return; |
---|
| 2694 | } else if (ok_for_output == MAYBE) { |
---|
| 2695 | /* (ok_for_output == MAYBE) also happens to indicate whether we need |
---|
| 2696 | * to check that the coordinate system is valid for input. |
---|
| 2697 | */ |
---|
[df1d579] | 2698 | proj_errno_reset(NULL); |
---|
[b39e24a] | 2699 | PJ* pj = proj_create(PJ_DEFAULT_CTX, proj_str); |
---|
[c092d72] | 2700 | if (!pj) { |
---|
| 2701 | set_pos(&fp); |
---|
[88ba3b1] | 2702 | compile_diagnostic(DIAG_ERR|DIAG_STRING, /*Invalid coordinate system: %s*/443, |
---|
[d9d8f21] | 2703 | proj_context_errno_string(PJ_DEFAULT_CTX, |
---|
| 2704 | proj_context_errno(PJ_DEFAULT_CTX))); |
---|
[9f55538] | 2705 | skipline(); |
---|
[c092d72] | 2706 | return; |
---|
| 2707 | } |
---|
[b39e24a] | 2708 | proj_destroy(pj); |
---|
[c092d72] | 2709 | } |
---|
| 2710 | |
---|
[da9163b] | 2711 | /* Free current input proj_str if not used by parent. */ |
---|
[c092d72] | 2712 | settings * p = pcs; |
---|
[da9163b] | 2713 | if (!p->next || p->proj_str != p->next->proj_str) |
---|
[ae917b96] | 2714 | free(p->proj_str); |
---|
[b39e24a] | 2715 | p->proj_str = proj_str; |
---|
[abe7192] | 2716 | p->input_convergence = HUGE_REAL; |
---|
[da9163b] | 2717 | invalidate_pj_cached(); |
---|
[c092d72] | 2718 | } |
---|
[abd0310] | 2719 | } |
---|
| 2720 | |
---|
[82919e07] | 2721 | static const sztok infer_tab[] = { |
---|
[27b8b59] | 2722 | { "EQUATES", INFER_EQUATES }, |
---|
| 2723 | { "EXPORTS", INFER_EXPORTS }, |
---|
| 2724 | { "PLUMBS", INFER_PLUMBS }, |
---|
| 2725 | #if 0 /* FIXME */ |
---|
| 2726 | { "SUBSURVEYS", INFER_SUBSURVEYS }, |
---|
| 2727 | #endif |
---|
| 2728 | { NULL, INFER_NULL } |
---|
[a420b49] | 2729 | }; |
---|
[cb3d1e2] | 2730 | |
---|
[82919e07] | 2731 | static const sztok onoff_tab[] = { |
---|
[27b8b59] | 2732 | { "OFF", 0 }, |
---|
| 2733 | { "ON", 1 }, |
---|
| 2734 | { NULL, -1 } |
---|
[a420b49] | 2735 | }; |
---|
[cb3d1e2] | 2736 | |
---|
[a420b49] | 2737 | static void |
---|
[eb18f4d] | 2738 | cmd_infer(void) |
---|
[a420b49] | 2739 | { |
---|
[27b8b59] | 2740 | infer_what setting; |
---|
[a420b49] | 2741 | int on; |
---|
| 2742 | get_token(); |
---|
| 2743 | setting = match_tok(infer_tab, TABSIZE(infer_tab)); |
---|
[27b8b59] | 2744 | if (setting == INFER_NULL) { |
---|
[caae6cd] | 2745 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Found “%s”, expecting “EQUATES”, “EXPORTS”, or “PLUMBS”*/31, s_str(&token)); |
---|
[647407d] | 2746 | return; |
---|
[a420b49] | 2747 | } |
---|
| 2748 | get_token(); |
---|
| 2749 | on = match_tok(onoff_tab, TABSIZE(onoff_tab)); |
---|
| 2750 | if (on == -1) { |
---|
[caae6cd] | 2751 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Found “%s”, expecting “ON” or “OFF”*/32, s_str(&token)); |
---|
[647407d] | 2752 | return; |
---|
[a420b49] | 2753 | } |
---|
[cb3d1e2] | 2754 | |
---|
[27b8b59] | 2755 | if (on) { |
---|
| 2756 | pcs->infer |= BIT(setting); |
---|
[63d4f07] | 2757 | if (setting == INFER_EXPORTS) fExportUsed = true; |
---|
[27b8b59] | 2758 | } else { |
---|
| 2759 | pcs->infer &= ~BIT(setting); |
---|
[a420b49] | 2760 | } |
---|
| 2761 | } |
---|
| 2762 | |
---|
| 2763 | static void |
---|
[eb18f4d] | 2764 | cmd_truncate(void) |
---|
[a420b49] | 2765 | { |
---|
[647407d] | 2766 | unsigned int truncate_at = 0; /* default is no truncation */ |
---|
[c80bd34] | 2767 | filepos fp; |
---|
| 2768 | |
---|
| 2769 | get_pos(&fp); |
---|
| 2770 | |
---|
[647407d] | 2771 | get_token(); |
---|
[0532954] | 2772 | if (!S_EQ(&uctoken, "OFF")) { |
---|
| 2773 | if (!s_empty(&uctoken)) set_pos(&fp); |
---|
[647407d] | 2774 | truncate_at = read_uint(); |
---|
| 2775 | } |
---|
| 2776 | /* for backward compatibility, "*truncate 0" means "*truncate off" */ |
---|
| 2777 | pcs->Truncate = (truncate_at == 0) ? INT_MAX : truncate_at; |
---|
| 2778 | } |
---|
| 2779 | |
---|
[e8452e3] | 2780 | static void |
---|
| 2781 | cmd_ref(void) |
---|
| 2782 | { |
---|
| 2783 | /* Just syntax check for now. */ |
---|
[0532954] | 2784 | string ref = S_INIT; |
---|
| 2785 | read_string(&ref); |
---|
[e8452e3] | 2786 | s_free(&ref); |
---|
| 2787 | } |
---|
| 2788 | |
---|
[647407d] | 2789 | static void |
---|
[eb18f4d] | 2790 | cmd_require(void) |
---|
[647407d] | 2791 | { |
---|
[9af1e2c0] | 2792 | // Add extra 0 so `*require 1.4.10.1` fails with cavern version 1.4.10. |
---|
| 2793 | const unsigned version[] = {COMMAVERSION, 0}; |
---|
[c80bd34] | 2794 | |
---|
[9af1e2c0] | 2795 | skipblanks(); |
---|
| 2796 | filepos fp; |
---|
| 2797 | get_pos(&fp); |
---|
| 2798 | |
---|
| 2799 | // Parse the required version number, storing its components in |
---|
| 2800 | // required_version. We only store at most one more component than |
---|
| 2801 | // COMMAVERSION has since more than that can't affect the comparison. |
---|
| 2802 | size_t i = 0; |
---|
| 2803 | int diff = 0; |
---|
| 2804 | while (1) { |
---|
| 2805 | unsigned component = read_uint(); |
---|
| 2806 | if (diff == 0 && i < sizeof(version) / sizeof(version[0])) { |
---|
| 2807 | if (diff == 0) { |
---|
| 2808 | diff = (int)version[i++] - (int)component; |
---|
| 2809 | } |
---|
| 2810 | } |
---|
| 2811 | if (ch != '.' || isBlank(nextch()) || isComm(ch) || isEol(ch)) |
---|
| 2812 | break; |
---|
| 2813 | } |
---|
| 2814 | |
---|
| 2815 | if (diff < 0) { |
---|
| 2816 | // Requirement not satisfied |
---|
| 2817 | size_t len = (size_t)(ftell(file.fh) - fp.offset); |
---|
| 2818 | char *v = osmalloc(len + 1); |
---|
| 2819 | set_pos(&fp); |
---|
| 2820 | for (size_t j = 0; j < len; j++) { |
---|
| 2821 | v[j] = ch; |
---|
[69c920d] | 2822 | nextch(); |
---|
[9af1e2c0] | 2823 | } |
---|
| 2824 | v[len] = '\0'; |
---|
| 2825 | /* TRANSLATORS: Feel free to translate as "or newer" instead of "or |
---|
| 2826 | * greater" if that gives a more natural translation. It's |
---|
| 2827 | * technically not quite right when there are parallel active release |
---|
| 2828 | * series (e.g. Survex 1.0.40 was released *after* 1.2.0), but this |
---|
| 2829 | * seems unlikely to confuse users. "Survex" is the name of the |
---|
| 2830 | * software, so should not be translated. |
---|
| 2831 | * |
---|
| 2832 | * Here "survey" is a "cave map" rather than list of questions - it should be |
---|
| 2833 | * translated to the terminology that cavers using the language would use. |
---|
| 2834 | */ |
---|
[7962c9d] | 2835 | compile_diagnostic(DIAG_FATAL|DIAG_FROM(fp), /*Survex version %s or greater required to process this survey data.*/38, v); |
---|
[9af1e2c0] | 2836 | // Does not return so no point freeing v here. |
---|
| 2837 | } |
---|
[a420b49] | 2838 | } |
---|
| 2839 | |
---|
[b5a3219] | 2840 | /* allocate new meta_data if need be */ |
---|
| 2841 | void |
---|
| 2842 | copy_on_write_meta(settings *s) |
---|
| 2843 | { |
---|
| 2844 | if (!s->meta || s->meta->ref_count != 0) { |
---|
| 2845 | meta_data * meta_new = osnew(meta_data); |
---|
| 2846 | if (!s->meta) { |
---|
[1ee204e] | 2847 | meta_new->days1 = meta_new->days2 = -1; |
---|
[b5a3219] | 2848 | } else { |
---|
| 2849 | *meta_new = *(s->meta); |
---|
| 2850 | } |
---|
| 2851 | meta_new->ref_count = 0; |
---|
| 2852 | s->meta = meta_new; |
---|
| 2853 | } |
---|
| 2854 | } |
---|
| 2855 | |
---|
[c7c0f93] | 2856 | static int |
---|
| 2857 | read_year(filepos *fp_date_ptr) |
---|
| 2858 | { |
---|
[f15c53d9] | 2859 | int y = read_uint_raw(DIAG_ERR, /*Expecting date, found “%s”*/198, fp_date_ptr); |
---|
[c7c0f93] | 2860 | if (y < 100) { |
---|
| 2861 | /* Two digit year is 19xx. */ |
---|
| 2862 | y += 1900; |
---|
| 2863 | filepos fp_save; |
---|
| 2864 | get_pos(&fp_save); |
---|
| 2865 | set_pos(fp_date_ptr); |
---|
| 2866 | /* TRANSLATORS: %d will be replaced by the assumed year, e.g. 1918 */ |
---|
| 2867 | compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Assuming 2 digit year is %d*/76, y); |
---|
| 2868 | set_pos(&fp_save); |
---|
| 2869 | } else if (y < 1900 || y > 2078) { |
---|
| 2870 | set_pos(fp_date_ptr); |
---|
| 2871 | compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid year (< 1900 or > 2078)*/58); |
---|
| 2872 | longjmp(jbSkipLine, 1); |
---|
| 2873 | } |
---|
| 2874 | return y; |
---|
| 2875 | } |
---|
| 2876 | |
---|
[950a829] | 2877 | static void |
---|
| 2878 | cmd_date(void) |
---|
[421b7d2] | 2879 | { |
---|
[c7c0f93] | 2880 | enum { DATE_SURVEYED = 1, DATE_EXPLORED = 2 }; |
---|
| 2881 | unsigned date_flags = 0; |
---|
| 2882 | while (get_token(), !s_empty(&uctoken)) { |
---|
| 2883 | unsigned new_flag = 0; |
---|
| 2884 | if (S_EQ(&uctoken, "SURVEYED")) { |
---|
| 2885 | new_flag = DATE_SURVEYED; |
---|
| 2886 | } else if (S_EQ(&uctoken, "EXPLORED")) { |
---|
| 2887 | new_flag = DATE_EXPLORED; |
---|
| 2888 | } else { |
---|
| 2889 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN, |
---|
| 2890 | /*Expecting “%s” or “%s”*/103, "SURVEYED", "EXPLORED"); |
---|
| 2891 | continue; |
---|
| 2892 | } |
---|
[e0c7cd1] | 2893 | |
---|
[c7c0f93] | 2894 | if ((date_flags & new_flag)) { |
---|
| 2895 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN, |
---|
[0823cb3] | 2896 | /*Duplicate date type “%s”*/416, s_str(&token)); |
---|
[c7c0f93] | 2897 | } |
---|
| 2898 | date_flags |= new_flag; |
---|
| 2899 | } |
---|
[1ee204e] | 2900 | |
---|
[c7c0f93] | 2901 | int date_sep = '-'; |
---|
| 2902 | if (date_flags == 0) { |
---|
| 2903 | // `*date` without qualification sets the surveyed date. |
---|
| 2904 | date_flags = DATE_SURVEYED; |
---|
| 2905 | // Also allow '.' for compatibility. |
---|
| 2906 | date_sep = 0; |
---|
[e0c7cd1] | 2907 | } |
---|
| 2908 | |
---|
[c7c0f93] | 2909 | filepos fp_date1, fp_date2, fp; |
---|
| 2910 | get_pos(&fp_date1); |
---|
| 2911 | int year = read_year(&fp_date1); |
---|
| 2912 | int month = 0, day = 0, year2 = 0, month2 = 0, day2 = 0; |
---|
| 2913 | |
---|
[e0c7cd1] | 2914 | if (ch == '-') { |
---|
[c7c0f93] | 2915 | // Could be ISO-format date (e.g. 2024-10 or 2024-10-21 or 1912-11), or |
---|
| 2916 | // a range of old-format dates (e.g. 1973-1975 or 1911-12.02.03) or an |
---|
| 2917 | // ambiguous case like 1911-12 which we need to warn about in a `*date` |
---|
| 2918 | // command without `surveyed` or `explored` qualifiers. |
---|
| 2919 | nextch(); |
---|
| 2920 | get_pos(&fp_date2); |
---|
[f15c53d9] | 2921 | int v = read_uint_raw(DIAG_ERR, /*Expecting date, found “%s”*/198, &fp_date1); |
---|
[c7c0f93] | 2922 | if (date_sep == '-') { |
---|
| 2923 | // We're only accepting ISO dates. |
---|
| 2924 | } else if (ch == '-') { |
---|
| 2925 | // Two `-` so must be an ISO date, e.g. `2024-10-21`. |
---|
| 2926 | } else if (v >= 1 && v <= 12) { |
---|
| 2927 | // Valid month number so assume it's an ISO date. |
---|
| 2928 | if (year < 1900 + v) { |
---|
| 2929 | // Warn about ambiguous cases. |
---|
| 2930 | compile_diagnostic(DIAG_WARN|DIAG_FROM(fp_date1), |
---|
| 2931 | /*Interpreting as an ISO-format date - use “*date surveyed %d-%02d” to suppress this warning, or “*date %d %d” if you wanted a date range*/158, |
---|
| 2932 | year, v, year, 1900 + v); |
---|
| 2933 | } |
---|
| 2934 | } else { |
---|
| 2935 | date_sep = '.'; |
---|
| 2936 | year2 = v; |
---|
| 2937 | if (year2 < 100) { |
---|
| 2938 | /* Two digit year is 19xx. */ |
---|
| 2939 | year2 += 1900; |
---|
| 2940 | /* TRANSLATORS: %d will be replaced by the assumed year, e.g. 1918 */ |
---|
| 2941 | compile_diagnostic(DIAG_WARN|DIAG_FROM(fp_date2), |
---|
| 2942 | /*Assuming 2 digit year is %d*/76, year2); |
---|
| 2943 | } else if (year2 < 1900 || year2 > 2078) { |
---|
| 2944 | compile_diagnostic(DIAG_WARN|DIAG_FROM(fp_date2), |
---|
| 2945 | /*Invalid year (< 1900 or > 2078)*/58); |
---|
| 2946 | longjmp(jbSkipLine, 1); |
---|
| 2947 | } |
---|
| 2948 | goto process_dates; |
---|
| 2949 | } |
---|
| 2950 | |
---|
| 2951 | date_sep = '-'; |
---|
| 2952 | month = v; |
---|
| 2953 | fp = fp_date2; |
---|
| 2954 | } else if (ch == '.') { |
---|
| 2955 | if (date_sep == '-') { |
---|
| 2956 | char date_sep_string[2] = { date_sep, '\0' }; |
---|
| 2957 | compile_diagnostic(DIAG_ERR|DIAG_COL|DIAG_SKIP, |
---|
| 2958 | /*Expecting “%s”*/497, date_sep_string); |
---|
| 2959 | return; |
---|
| 2960 | } |
---|
| 2961 | date_sep = ch; |
---|
[e0c7cd1] | 2962 | nextch(); |
---|
[c7c0f93] | 2963 | get_pos(&fp); |
---|
[f15c53d9] | 2964 | month = read_uint_raw(DIAG_ERR, /*Expecting date, found “%s”*/198, &fp_date1); |
---|
[e0c7cd1] | 2965 | } else { |
---|
[c7c0f93] | 2966 | // Just a year - might be a ISO date range though. |
---|
| 2967 | date_sep = '-'; |
---|
| 2968 | goto try_date2; |
---|
| 2969 | } |
---|
| 2970 | |
---|
| 2971 | if (month < 1 || month > 12) { |
---|
| 2972 | set_pos(&fp); |
---|
| 2973 | compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid month*/86); |
---|
| 2974 | longjmp(jbSkipLine, 1); |
---|
| 2975 | } |
---|
| 2976 | |
---|
| 2977 | if (ch == date_sep) { |
---|
| 2978 | nextch(); |
---|
| 2979 | get_pos(&fp); |
---|
[f15c53d9] | 2980 | day = read_uint_raw(DIAG_ERR, /*Expecting date, found “%s”*/198, &fp_date1); |
---|
[c7c0f93] | 2981 | if (day < 1 || day > last_day(year, month)) { |
---|
| 2982 | set_pos(&fp); |
---|
| 2983 | /* TRANSLATORS: e.g. 31st of April, or 32nd of any month */ |
---|
| 2984 | compile_diagnostic(DIAG_WARN|DIAG_UINT, |
---|
| 2985 | /*Invalid day of the month*/87); |
---|
| 2986 | longjmp(jbSkipLine, 1); |
---|
[1ee204e] | 2987 | } |
---|
| 2988 | } |
---|
| 2989 | |
---|
[c7c0f93] | 2990 | try_date2: |
---|
[2d4ca34] | 2991 | if (date_sep == '-') { |
---|
| 2992 | skipblanks(); |
---|
| 2993 | if (!isdigit(ch)) goto process_dates; |
---|
| 2994 | } else if (ch == '-') { |
---|
[c7c0f93] | 2995 | nextch(); |
---|
[2d4ca34] | 2996 | } else { |
---|
| 2997 | goto process_dates; |
---|
| 2998 | } |
---|
| 2999 | { |
---|
[c7c0f93] | 3000 | get_pos(&fp_date2); |
---|
| 3001 | year2 = read_year(&fp_date2); |
---|
| 3002 | if (ch == date_sep) { |
---|
| 3003 | nextch(); |
---|
| 3004 | get_pos(&fp); |
---|
[f15c53d9] | 3005 | month2 = read_uint_raw(DIAG_ERR, /*Expecting date, found “%s”*/198, |
---|
[c7c0f93] | 3006 | &fp_date2); |
---|
| 3007 | if (month2 < 1 || month2 > 12) { |
---|
| 3008 | set_pos(&fp); |
---|
| 3009 | compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid month*/86); |
---|
| 3010 | longjmp(jbSkipLine, 1); |
---|
| 3011 | } |
---|
| 3012 | |
---|
| 3013 | if (ch == date_sep) { |
---|
| 3014 | nextch(); |
---|
| 3015 | get_pos(&fp); |
---|
[f15c53d9] | 3016 | day2 = read_uint_raw(DIAG_ERR, /*Expecting date, found “%s”*/198, |
---|
[c7c0f93] | 3017 | &fp_date2); |
---|
| 3018 | if (day2 < 1 || day2 > last_day(year2, month2)) { |
---|
| 3019 | set_pos(&fp); |
---|
| 3020 | /* TRANSLATORS: e.g. 31st of April, or 32nd of any month */ |
---|
| 3021 | compile_diagnostic(DIAG_WARN|DIAG_UINT, |
---|
| 3022 | /*Invalid day of the month*/87); |
---|
| 3023 | longjmp(jbSkipLine, 1); |
---|
| 3024 | } |
---|
| 3025 | } |
---|
| 3026 | } |
---|
| 3027 | } |
---|
[1ee204e] | 3028 | |
---|
[50bfed5] | 3029 | process_dates:; |
---|
[c7c0f93] | 3030 | bool date_range = (year2 != 0); |
---|
| 3031 | if (!date_range) { |
---|
| 3032 | year2 = year; |
---|
| 3033 | month2 = month; |
---|
| 3034 | day2 = day; |
---|
| 3035 | } |
---|
| 3036 | |
---|
| 3037 | int days1 = days_since_1900(year, month ? month : 1, day ? day : 1); |
---|
| 3038 | |
---|
| 3039 | if (days1 > current_days_since_1900) { |
---|
| 3040 | filepos fp_save; |
---|
| 3041 | get_pos(&fp_save); |
---|
| 3042 | set_pos(&fp_date1); |
---|
| 3043 | compile_diagnostic(DIAG_WARN|DIAG_DATE, /*Date is in the future!*/80); |
---|
| 3044 | set_pos(&fp_save); |
---|
| 3045 | } |
---|
| 3046 | |
---|
| 3047 | if (month2 == 0) { |
---|
| 3048 | month2 = 12; |
---|
| 3049 | day2 = 31; |
---|
| 3050 | } else if (day2 == 0) { |
---|
| 3051 | day2 = last_day(year2, month2); |
---|
| 3052 | } |
---|
| 3053 | int days2 = days_since_1900(year2, month2, day2); |
---|
| 3054 | if (date_range && days2 > current_days_since_1900) { |
---|
| 3055 | // If !date_range, either we already emitted this warning when |
---|
| 3056 | // processing the start date, or the date is partial and for the |
---|
| 3057 | // current year or current month, in which case it's not helpful |
---|
| 3058 | // to warn that the end is on the future, but it makes sense to |
---|
| 3059 | // process for the end date so the result doesn't change as time |
---|
| 3060 | // passes. |
---|
| 3061 | filepos fp_save; |
---|
| 3062 | get_pos(&fp_save); |
---|
| 3063 | set_pos(&fp_date2); |
---|
[1d74718] | 3064 | compile_diagnostic(DIAG_WARN|DIAG_DATE, /*Date is in the future!*/80); |
---|
[c7c0f93] | 3065 | set_pos(&fp_save); |
---|
[e0c7cd1] | 3066 | } |
---|
[1ee204e] | 3067 | |
---|
| 3068 | if (days2 < days1) { |
---|
[c7c0f93] | 3069 | filepos fp_save; |
---|
| 3070 | get_pos(&fp_save); |
---|
| 3071 | set_pos(&fp_date1); |
---|
[d0be687d] | 3072 | compile_diagnostic(DIAG_ERR|DIAG_WORD, /*End of date range is before the start*/81); |
---|
[c7c0f93] | 3073 | set_pos(&fp_save); |
---|
| 3074 | // Swap range ends to put us in a consistent state. |
---|
[95b0f1d] | 3075 | int tmp = days1; |
---|
| 3076 | days1 = days2; |
---|
| 3077 | days2 = tmp; |
---|
[1ee204e] | 3078 | } |
---|
[e0c7cd1] | 3079 | |
---|
[c7c0f93] | 3080 | if ((date_flags & DATE_SURVEYED)) { |
---|
| 3081 | if (!pcs->meta || pcs->meta->days1 != days1 || pcs->meta->days2 != days2) { |
---|
| 3082 | copy_on_write_meta(pcs); |
---|
| 3083 | pcs->meta->days1 = days1; |
---|
| 3084 | pcs->meta->days2 = days2; |
---|
| 3085 | /* Invalidate cached declination. */ |
---|
| 3086 | pcs->declination = HUGE_REAL; |
---|
| 3087 | } |
---|
| 3088 | } |
---|
| 3089 | |
---|
| 3090 | if ((date_flags & DATE_EXPLORED)) { |
---|
| 3091 | // FIXME: Need to revise 3d format to allow storing EXPLORED date too. |
---|
[95b0f1d] | 3092 | } |
---|
[950a829] | 3093 | } |
---|
| 3094 | |
---|
[3aafcee] | 3095 | typedef void (*cmd_fn)(void); |
---|
| 3096 | |
---|
[82919e07] | 3097 | static const cmd_fn cmd_funcs[] = { |
---|
[dcbcae0] | 3098 | cmd_alias, |
---|
[3aafcee] | 3099 | cmd_begin, |
---|
| 3100 | cmd_calibrate, |
---|
[abe7192] | 3101 | cmd_cartesian, |
---|
[3aafcee] | 3102 | cmd_case, |
---|
[f15c53d9] | 3103 | cmd_copyright, |
---|
[abd0310] | 3104 | cmd_cs, |
---|
[3aafcee] | 3105 | cmd_data, |
---|
[950a829] | 3106 | cmd_date, |
---|
[58c7b459] | 3107 | cmd_declination, |
---|
[7f1ab95] | 3108 | #ifndef NO_DEPRECATED |
---|
[3aafcee] | 3109 | cmd_default, |
---|
[7f1ab95] | 3110 | #endif |
---|
[3aafcee] | 3111 | cmd_end, |
---|
| 3112 | cmd_entrance, |
---|
| 3113 | cmd_equate, |
---|
| 3114 | cmd_export, |
---|
| 3115 | cmd_fix, |
---|
| 3116 | cmd_flags, |
---|
| 3117 | cmd_include, |
---|
| 3118 | cmd_infer, |
---|
| 3119 | skipline, /*cmd_instrument,*/ |
---|
[7f1ab95] | 3120 | #ifndef NO_DEPRECATED |
---|
[3aafcee] | 3121 | cmd_prefix, |
---|
[7f1ab95] | 3122 | #endif |
---|
[e8452e3] | 3123 | cmd_ref, |
---|
[3aafcee] | 3124 | cmd_require, |
---|
| 3125 | cmd_sd, |
---|
| 3126 | cmd_set, |
---|
| 3127 | solve_network, |
---|
[0f8216c] | 3128 | cmd_team, |
---|
[3aafcee] | 3129 | cmd_title, |
---|
| 3130 | cmd_truncate, |
---|
| 3131 | cmd_units |
---|
| 3132 | }; |
---|
| 3133 | |
---|
[a420b49] | 3134 | extern void |
---|
| 3135 | handle_command(void) |
---|
| 3136 | { |
---|
[05b9de76] | 3137 | filepos fp; |
---|
| 3138 | get_pos(&fp); |
---|
[e1cbc0d] | 3139 | get_token_legacy(); |
---|
[05b9de76] | 3140 | int cmdtok = match_tok(cmd_tab, TABSIZE(cmd_tab)); |
---|
[3aafcee] | 3141 | if (cmdtok < 0 || cmdtok >= (int)(sizeof(cmd_funcs) / sizeof(cmd_fn))) { |
---|
[05b9de76] | 3142 | set_pos(&fp); |
---|
| 3143 | get_token(); |
---|
[caae6cd] | 3144 | compile_diagnostic(DIAG_ERR|DIAG_TOKEN|DIAG_SKIP, /*Unknown command “%s”*/12, s_str(&token)); |
---|
[3aafcee] | 3145 | return; |
---|
[932f7e9] | 3146 | } |
---|
| 3147 | |
---|
[05b9de76] | 3148 | do_legacy_token_warning(); |
---|
| 3149 | |
---|
[932f7e9] | 3150 | switch (cmdtok) { |
---|
[3aafcee] | 3151 | case CMD_EXPORT: |
---|
| 3152 | if (!f_export_ok) |
---|
[736f7df] | 3153 | /* TRANSLATORS: The *EXPORT command is only valid just after *BEGIN |
---|
| 3154 | * <SURVEY>, so this would generate this error: |
---|
| 3155 | * |
---|
| 3156 | * *begin fred |
---|
| 3157 | * 1 2 1.23 045 -6 |
---|
| 3158 | * *export 2 |
---|
| 3159 | * *end fred */ |
---|
[cab0f26] | 3160 | compile_diagnostic(DIAG_ERR, /**EXPORT must immediately follow “*BEGIN <SURVEY>”*/57); |
---|
[3aafcee] | 3161 | break; |
---|
[ce15637] | 3162 | case CMD_ALIAS: |
---|
| 3163 | case CMD_CALIBRATE: |
---|
[abe7192] | 3164 | case CMD_CARTESIAN: |
---|
[ce15637] | 3165 | case CMD_CASE: |
---|
[3aafcee] | 3166 | case CMD_COPYRIGHT: |
---|
[ce15637] | 3167 | case CMD_CS: |
---|
| 3168 | case CMD_DATA: |
---|
[3aafcee] | 3169 | case CMD_DATE: |
---|
[ce15637] | 3170 | case CMD_DECLINATION: |
---|
| 3171 | case CMD_DEFAULT: |
---|
| 3172 | case CMD_FLAGS: |
---|
| 3173 | case CMD_INFER: |
---|
[3aafcee] | 3174 | case CMD_INSTRUMENT: |
---|
[ce15637] | 3175 | case CMD_REF: |
---|
| 3176 | case CMD_REQUIRE: |
---|
| 3177 | case CMD_SD: |
---|
| 3178 | case CMD_SET: |
---|
[3aafcee] | 3179 | case CMD_TEAM: |
---|
| 3180 | case CMD_TITLE: |
---|
[ce15637] | 3181 | case CMD_TRUNCATE: |
---|
| 3182 | case CMD_UNITS: |
---|
[4dcd3af] | 3183 | /* These can occur between *begin and *export */ |
---|
[421b7d2] | 3184 | break; |
---|
[a420b49] | 3185 | default: |
---|
[4dcd3af] | 3186 | /* NB: additional handling for "*begin <survey>" in cmd_begin */ |
---|
[63d4f07] | 3187 | f_export_ok = false; |
---|
[3aafcee] | 3188 | break; |
---|
[647407d] | 3189 | } |
---|
[421b7d2] | 3190 | |
---|
[3aafcee] | 3191 | cmd_funcs[cmdtok](); |
---|
[5f1e194] | 3192 | } |
---|