source: git/src/cavern.c @ 58dfbe4f

faster-cavernloglog-selectstereo-2025walls-datawalls-data-hanging-as-warningwarn-only-for-hanging-survey
Last change on this file since 58dfbe4f was 0532954, checked in by Olly Betts <olly@…>, 16 months ago

Rework str.h

The previous implementation was O(n²) for a loop appending n characters
to the string. In practice the strings are typically very short for
.svx format, but for other formats they may be longer so it seems silly
to have this known inefficiency.

  • Property mode set to 100644
File size: 14.2 KB
RevLine 
[0156ccfc]1/* cavern.c
[bb90203]2 * SURVEX Cave surveying software: data reduction main and related functions
[4bea0f8]3 * Copyright (C) 1991-2023 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
[ecbc6c18]17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
[bb90203]18 */
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
[9990aab]24#define MSG_SETUP_PROJ_SEARCH_PATH 1
25
[be97baf]26#include <limits.h>
[4833448]27#include <stdlib.h>
[bb90203]28#include <time.h>
29
[5853657]30#include "cavern.h"
31#include "cmdline.h"
32#include "commands.h"
[1ee204e]33#include "date.h"
[bb90203]34#include "datain.h"
35#include "debug.h"
36#include "message.h"
37#include "filename.h"
38#include "filelist.h"
[a405bc1]39#include "img_hosted.h"
[bb90203]40#include "listpos.h"
[5853657]41#include "netbits.h"
42#include "netskel.h"
[bb90203]43#include "out.h"
[8e8057c]44#include "str.h"
[5853657]45#include "validate.h"
[d18e778]46#include "whichos.h"
[bb90203]47
[affaeee]48#if OS_WIN32
49# include <conio.h> /* for _kbhit() and _getch() */
[e8cf429]50#endif
51
[bb90203]52/* For funcs which want to be immune from messing around with different
53 * calling conventions */
54#ifndef CDECL
55# define CDECL
56#endif
57
58/* Globals */
59node *stnlist = NULL;
60settings *pcs;
61prefix *root;
[a2c33ae]62prefix *anon_list = NULL;
[bb90203]63long cLegs, cStns;
64long cComponents;
[63d4f07]65bool fExportUsed = false;
[c092d72]66char * proj_str_out = NULL;
[da9163b]67PJ * pj_cached = NULL;
[bb90203]68
69FILE *fhErrStat = NULL;
[693388e]70img *pimg = NULL;
[63d4f07]71bool fQuiet = false; /* just show brief summary + errors */
72bool fMute = false; /* just show errors */
73bool fSuppress = false; /* only output 3d file */
74static bool fLog = false; /* stdout to .log file */
75static bool f_warnings_are_errors = false; /* turn warnings into errors */
[647407d]76
77nosurveylink *nosurveyhead;
[bb90203]78
79real totadj, total, totplan, totvert;
[dfac588]80real min[6], max[6];
81prefix *pfxHi[6], *pfxLo[6];
[bb90203]82
[0532954]83string survey_title = S_INIT;
[bb90203]84
[63d4f07]85bool fExplicitTitle = false;
[bb90203]86
[8e8057c]87char *fnm_output_base = NULL;
88int fnm_output_base_is_dir = 0;
89
[ee05463]90lrudlist * model = NULL;
91lrud ** next_lrud = NULL;
92
[5d59477]93char output_separator = '.';
94
[06a871f]95static void do_stats(void);
[bb90203]96
97static const struct option long_opts[] = {
98   /* const char *name; int has_arg (0 no_argument, 1 required_*, 2 optional_*); int *flag; int val; */
99   {"percentage", no_argument, 0, 'p'},
[21904d3]100   /* Ignore for compatibility with older versions. */
101   {"no-percentage", no_argument, 0, 0},
[8e8057c]102   {"output", required_argument, 0, 'o'},
[647407d]103   {"quiet", no_argument, 0, 'q'},
104   {"no-auxiliary-files", no_argument, 0, 's'},
[bb9d869]105   {"warnings-are-errors", no_argument, 0, 'w'},
[0dab87a]106   {"log", no_argument, 0, 1},
[4833448]107   {"3d-version", required_argument, 0, 'v'},
[affaeee]108#if OS_WIN32
[0dab87a]109   {"pause", no_argument, 0, 2},
[647407d]110#endif
[bb90203]111   {"help", no_argument, 0, HLP_HELP},
112   {"version", no_argument, 0, HLP_VERSION},
113   {0, 0, 0, 0}
114};
115
[4833448]116#define short_opts "pao:qsv:wz:"
[bb90203]117
118static struct help_msg help[] = {
119/*                              <-- */
[736f7df]120   /* TRANSLATORS: --help output for cavern --output option */
[45af761]121   {HLP_ENCODELONG(2),        /*set location for output files*/162, 0},
[736f7df]122   /* TRANSLATORS: --help output for cavern --quiet option */
[45af761]123   {HLP_ENCODELONG(3),        /*only show brief summary (-qq for errors only)*/163, 0},
[736f7df]124   /* TRANSLATORS: --help output for cavern --no-auxiliary-files option */
[45af761]125   {HLP_ENCODELONG(4),        /*do not create .err file*/164, 0},
[736f7df]126   /* TRANSLATORS: --help output for cavern --warnings-are-errors option */
[45af761]127   {HLP_ENCODELONG(5),        /*turn warnings into errors*/165, 0},
[736f7df]128   /* TRANSLATORS: --help output for cavern --log option */
[45af761]129   {HLP_ENCODELONG(6),        /*log output to .log file*/170, 0},
[736f7df]130   /* TRANSLATORS: --help output for cavern --3d-version option */
[45af761]131   {HLP_ENCODELONG(7),        /*specify the 3d file format version to output*/171, 0},
[a4ae909]132 /*{'z',                        "set optimizations for network reduction"},*/
[45af761]133   {0, 0, 0}
[bb90203]134};
135
[5b68ae1]136/* atexit functions */
[25ab06b]137static void
138delete_output_on_error(void)
139{
[bb9d869]140   if (msg_errors || (f_warnings_are_errors && msg_warnings))
141      filename_delete_output();
[25ab06b]142}
143
[affaeee]144#if OS_WIN32
[5b68ae1]145static void
146pause_on_exit(void)
147{
148   while (_kbhit()) _getch();
149   _getch();
150}
151#endif
152
[1ee204e]153int current_days_since_1900;
[e0c7cd1]154
[b39e24a]155static void discarding_proj_logger(void *ctx, int level, const char *message) {
156    (void)ctx;
157    (void)level;
158    (void)message;
159}
160
[bb90203]161extern CDECL int
162main(int argc, char **argv)
163{
164   int d;
[1ee204e]165   time_t tmUserStart = time(NULL);
166   clock_t tmCPUStart = clock();
167   {
[eafe1a3]168       /* FIXME: localtime? */
[1ee204e]169       struct tm * t = localtime(&tmUserStart);
170       int y = t->tm_year + 1900;
171       current_days_since_1900 = days_since_1900(y, t->tm_mon + 1, t->tm_mday);
172   }
[b88b171]173
174   /* Always buffer by line for aven's benefit. */
175   setvbuf(stdout, NULL, _IOLBF, 0);
176
[b39e24a]177   /* Prevent stderr spew from PROJ. */
178   proj_log_func(PJ_DEFAULT_CTX, NULL, discarding_proj_logger);
179
[bdfe97f]180   msg_init(argv);
[bb90203]181
182   pcs = osnew(settings);
183   pcs->next = NULL;
184   pcs->Translate = ((short*) osmalloc(ossizeof(short) * 257)) + 1;
[b5a3219]185   pcs->meta = NULL;
[b39e24a]186   pcs->proj_str = NULL;
[95b0f1d]187   pcs->declination = HUGE_REAL;
[4bea0f8]188   pcs->convergence = HUGE_REAL;
[37d6b84]189   pcs->dec_filename = NULL;
190   pcs->dec_line = 0;
191   pcs->dec_context = NULL;
192   pcs->dec_lat = HUGE_VAL;
193   pcs->dec_lon = HUGE_VAL;
194   pcs->dec_alt = HUGE_VAL;
195   pcs->min_declination = HUGE_VAL;
196   pcs->max_declination = -HUGE_VAL;
[bb90203]197
198   /* Set up root of prefix hierarchy */
199   root = osnew(prefix);
200   root->up = root->right = root->down = NULL;
201   root->stn = NULL;
[421b7d2]202   root->pos = NULL;
[ff6cfe1]203   root->ident = NULL;
[932f7e9]204   root->min_export = root->max_export = 0;
[95c3272]205   root->sflags = BIT(SFLAGS_SURVEY);
[016068a]206   root->filename = NULL;
[647407d]207
208   nosurveyhead = NULL;
[bb90203]209
210   stnlist = NULL;
211   cLegs = cStns = cComponents = 0;
212   totadj = total = totplan = totvert = 0.0;
213
[0590246]214   for (d = 0; d < 6; d++) {
[fa42426]215      min[d] = HUGE_REAL;
216      max[d] = -HUGE_REAL;
[bb90203]217      pfxHi[d] = pfxLo[d] = NULL;
218   }
219
[d06141c]220   /* at least one argument must be given */
[b85e20f]221   cmdline_init(argc, argv, short_opts, long_opts, NULL, help, 1, -1);
[bb90203]222   while (1) {
[b85e20f]223      int opt = cmdline_getopt();
[bb90203]224      if (opt == EOF) break;
225      switch (opt) {
226       case 'p':
[21904d3]227         /* Ignore for compatibility with older versions. */
[b4fe9fb]228         break;
[8e8057c]229       case 'o': {
[9206096]230         osfree(fnm_output_base); /* in case of multiple -o options */
[8e8057c]231         /* can be a directory (in which case use basename of leaf input)
232          * or a file (in which case just trim the extension off) */
233         if (fDirectory(optarg)) {
234            /* this is a little tricky - we need to note the path here,
235             * and then add the leaf later on (in datain.c) */
236            fnm_output_base = base_from_fnm(optarg);
237            fnm_output_base_is_dir = 1;
238         } else {
239            fnm_output_base = base_from_fnm(optarg);
240         }
241         break;
242       }
[647407d]243       case 'q':
244         if (fQuiet) fMute = 1;
245         fQuiet = 1;
246         break;
247       case 's':
248         fSuppress = 1;
249         break;
[4833448]250       case 'v': {
251         int v = atoi(optarg);
252         if (v < IMG_VERSION_MIN || v > IMG_VERSION_MAX)
253            fatalerror(/*3d file format versions %d to %d supported*/88,
254                       IMG_VERSION_MIN, IMG_VERSION_MAX);
255         img_output_version = v;
256         break;
257       }
[bb9d869]258       case 'w':
259         f_warnings_are_errors = 1;
260         break;
[8e8057c]261       case 'z': {
262         /* Control which network optimisations are used (development tool) */
263         static int first_opt_z = 1;
[eb18f4d]264         char c;
[8e8057c]265         if (first_opt_z) {
[bb90203]266            optimize = 0;
[8e8057c]267            first_opt_z = 0;
[bb90203]268         }
[c50391b8]269         /* Lollipops, Parallel legs, Iterate mx, Delta* */
[eb18f4d]270         while ((c = *optarg++) != '\0')
[0580c6a]271            if (islower((unsigned char)c)) optimize |= BITA(c);
[0dab87a]272         break;
273       case 1:
[63d4f07]274         fLog = true;
[bb90203]275         break;
[affaeee]276#if OS_WIN32
[0dab87a]277       case 2:
[5b68ae1]278         atexit(pause_on_exit);
279         break;
280#endif
[bb90203]281       }
282      }
283   }
284
[0dab87a]285   if (fLog) {
286      char *fnm;
[9887ea01]287      if (!fnm_output_base) {
288         char *p;
289         p = baseleaf_from_fnm(argv[optind]);
[0156ccfc]290         fnm = add_ext(p, EXT_LOG);
[421b7d2]291         osfree(p);
[9887ea01]292      } else if (fnm_output_base_is_dir) {
[09e8f4c]293         char *p;
294         fnm = baseleaf_from_fnm(argv[optind]);
295         p = use_path(fnm_output_base, fnm);
296         osfree(fnm);
[0156ccfc]297         fnm = add_ext(p, EXT_LOG);
[09e8f4c]298         osfree(p);
299      } else {
[0156ccfc]300         fnm = add_ext(fnm_output_base, EXT_LOG);
[09e8f4c]301      }
[421b7d2]302
[0dab87a]303      if (!freopen(fnm, "w", stdout))
[0804fbe]304         fatalerror(/*Failed to open output file “%s”*/47, fnm);
[0dab87a]305
306      osfree(fnm);
307   }
308
[facdf7a]309   if (!fMute) {
310      const char *p = COPYRIGHT_MSG;
311      puts(PRETTYPACKAGE" "VERSION);
312      while (1) {
313          const char *q = p;
314          p = strstr(p, "(C)");
315          if (p == NULL) {
316              puts(q);
317              break;
318          }
319          fwrite(q, 1, p - q, stdout);
[ee7511a]320          fputs(msg(/*©*/0), stdout);
[facdf7a]321          p += 3;
322      }
323   }
[bb90203]324
[25ab06b]325   atexit(delete_output_on_error);
326
[bb90203]327   /* end of options, now process data files */
328   while (argv[optind]) {
329      const char *fnm = argv[optind];
330
331      if (!fExplicitTitle) {
[0532954]332          char *lf = baseleaf_from_fnm(fnm);
333          if (s_empty(&survey_title)) {
334              s_donate(&survey_title, lf);
335          } else {
336              s_catchar(&survey_title, ' ');
337              s_cat(&survey_title, lf);
338              osfree(lf);
339          }
[bb90203]340      }
341
342      /* Select defaults settings */
343      default_all(pcs);
[f4b609d]344      data_file(NULL, fnm); /* first argument is current path */
[cb3d1e2]345
[bb90203]346      optind++;
347   }
[cb3d1e2]348
[bb90203]349   validate();
350
[37d6b84]351   report_declination(pcs);
352
[bb90203]353   solve_network(/*stnlist*/); /* Find coordinates of all points */
354   validate();
[421b7d2]355
[a4ae909]356   /* close .3d file */
357   if (!img_close(pimg)) {
358      char *fnm = add_ext(fnm_output_base, EXT_SVX_3D);
[a405bc1]359      fatalerror(img_error2msg(img_error()), fnm);
[647407d]360   }
[7104f16]361   if (fhErrStat) safe_fclose(fhErrStat);
[bb90203]362
363   out_current_action(msg(/*Calculating statistics*/120));
[2b6eca8]364   if (!fMute) do_stats();
[647407d]365   if (!fQuiet) {
[f03053a7]366      /* clock() typically wraps after 72 minutes, but there doesn't seem
367       * to be a better way.  Still 72 minutes means some cave!
[be97baf]368       * We detect if clock() could have wrapped and suppress CPU time
369       * printing in this case.
[f03053a7]370       */
[be97baf]371      double tmUser = difftime(time(NULL), tmUserStart);
372      double tmCPU;
373      clock_t now = clock();
374#define CLOCK_T_WRAP \
375        (sizeof(clock_t)<sizeof(long)?(1ul << (CHAR_BIT * sizeof(clock_t))):0)
376      tmCPU = (now - (unsigned long)tmCPUStart)
377         / (double)CLOCKS_PER_SEC;
378      if (now < tmCPUStart)
379         tmCPU += CLOCK_T_WRAP / (double)CLOCKS_PER_SEC;
380      if (tmUser >= tmCPU + CLOCK_T_WRAP / (double)CLOCKS_PER_SEC)
381         tmCPU = 0;
[647407d]382
[27b8b59]383      /* tmUser is integer, tmCPU not - equivalent to (ceil(tmCPU) >= tmUser) */
[647407d]384      if (tmCPU + 1 > tmUser) {
[421b7d2]385         printf(msg(/*CPU time used %5.2fs*/140), tmCPU);
[647407d]386      } else if (tmCPU == 0) {
[27b8b59]387         if (tmUser != 0.0) {
[421b7d2]388            printf(msg(/*Time used %5.2fs*/141), tmUser);
[647407d]389         } else {
[421b7d2]390            fputs(msg(/*Time used unavailable*/142), stdout);
[647407d]391         }
[bb90203]392      } else {
[5b68ae1]393         printf(msg(/*Time used %5.2fs (%5.2fs CPU time)*/143), tmUser, tmCPU);
[bb90203]394      }
[5b68ae1]395      putnl();
[647407d]396   }
[25ab06b]397   if (msg_warnings || msg_errors) {
[7ebee5b]398      if (msg_errors || (f_warnings_are_errors && msg_warnings)) {
[5311876]399         printf(msg(/*There were %d warning(s) and %d error(s) - no output files produced.*/113),
[7ebee5b]400                msg_warnings, msg_errors);
401         putnl();
402         return EXIT_FAILURE;
403      }
404      printf(msg(/*There were %d warning(s).*/16), msg_warnings);
[25ab06b]405      putnl();
406   }
[bb9d869]407   return EXIT_SUCCESS;
[bb90203]408}
409
410static void
[85c0078]411do_range(int d, int msgno, real length_factor, const char * units)
[bb90203]412{
[1a21d04]413   if (d < 3) {
414      /* If the bound including anonymous stations is at an anonymous station
415       * but the bound only considering named stations is the same, use the
416       * named station for the anonymous bound too.
417       */
418      if (TSTBIT(pfxHi[d]->sflags, SFLAGS_ANON) && max[d] == max[d + 3]) {
419         pfxHi[d] = pfxHi[d + 3];
420      }
421      if (TSTBIT(pfxLo[d]->sflags, SFLAGS_ANON) && min[d] == min[d + 3]) {
422         pfxLo[d] = pfxLo[d + 3];
423      }
424   }
425
[ccc815d5]426   /* sprint_prefix uses a single buffer, so to report two stations in one
427    * message we need to make a temporary copy of the string for one of them.
428    */
[aecd032]429   char * pfx_hi = osstrdup(sprint_prefix(pfxHi[d]));
430   char * pfx_lo = sprint_prefix(pfxLo[d]);
[85c0078]431   real hi = max[d] * length_factor;
432   real lo = min[d] * length_factor;
433   printf(msg(msgno), hi - lo, units, pfx_hi, hi, units, pfx_lo, lo, units);
[aecd032]434   osfree(pfx_hi);
[2b6eca8]435   putnl();
[dfac588]436
[378112a]437   /* Range without anonymous stations at offset 3. */
[dfac588]438   if (d < 3 && (pfxHi[d] != pfxHi[d + 3] || pfxLo[d] != pfxLo[d + 3])) {
439      do_range(d + 3, msgno, length_factor, units);
440   }
[bb90203]441}
442
443static void
[06a871f]444do_stats(void)
[bb90203]445{
446   long cLoops = cComponents + cLegs - cStns;
[85c0078]447   int length_units = get_length_units(Q_LENGTH);
448   const char * units = get_units_string(length_units);
449   real length_factor = 1.0 / get_units_factor(length_units);
[bb90203]450
[2b6eca8]451   putnl();
[bb90203]452
[a63fdd2a]453   if (cStns == 1) {
[2b6eca8]454      fputs(msg(/*Survey contains 1 survey station,*/172), stdout);
[a63fdd2a]455   } else {
[2b6eca8]456      printf(msg(/*Survey contains %ld survey stations,*/173), cStns);
[a63fdd2a]457   }
[bb90203]458
[a63fdd2a]459   if (cLegs == 1) {
[2b6eca8]460      fputs(msg(/* joined by 1 leg.*/174), stdout);
[a63fdd2a]461   } else {
[2b6eca8]462      printf(msg(/* joined by %ld legs.*/175), cLegs);
[a63fdd2a]463   }
[bb90203]464
[2b6eca8]465   putnl();
[bb90203]466
[a63fdd2a]467   if (cLoops == 1) {
[2b6eca8]468      fputs(msg(/*There is 1 loop.*/138), stdout);
[a63fdd2a]469   } else {
[2b6eca8]470      printf(msg(/*There are %ld loops.*/139), cLoops);
[a63fdd2a]471   }
[bb90203]472
[2b6eca8]473   putnl();
[bb90203]474
475   if (cComponents != 1) {
[736f7df]476      /* TRANSLATORS: "Connected component" in the graph theory sense - it
477       * means there are %ld bits of survey with no connections between them.
478       * This message is only used if there are more than 1. */
[2b6eca8]479      printf(msg(/*Survey has %ld connected components.*/178), cComponents);
[a63fdd2a]480      putnl();
481   }
[bb90203]482
[85c0078]483   printf(msg(/*Total length of survey legs = %7.2f%s (%7.2f%s adjusted)*/132),
484          total * length_factor, units, totadj * length_factor, units);
[2b6eca8]485   putnl();
[85c0078]486   printf(msg(/*Total plan length of survey legs = %7.2f%s*/133),
487          totplan * length_factor, units);
[2b6eca8]488   putnl();
[85c0078]489   printf(msg(/*Total vertical length of survey legs = %7.2f%s*/134),
490          totvert * length_factor, units);
[2b6eca8]491   putnl();
492
[4ae2ea4]493   /* If there's no underground survey, we've no ranges */
494   if (pfxHi[0]) {
[736f7df]495      /* TRANSLATORS: numbers are altitudes of highest and lowest stations */
[85c0078]496      do_range(2, /*Vertical range = %4.2f%s (from %s at %4.2f%s to %s at %4.2f%s)*/135,
497               length_factor, units);
[736f7df]498      /* TRANSLATORS: c.f. previous message */
[85c0078]499      do_range(1, /*North-South range = %4.2f%s (from %s at %4.2f%s to %s at %4.2f%s)*/136,
500               length_factor, units);
[736f7df]501      /* TRANSLATORS: c.f. previous two messages */
[85c0078]502      do_range(0, /*East-West range = %4.2f%s (from %s at %4.2f%s to %s at %4.2f%s)*/137,
503               length_factor, units);
[4ae2ea4]504   }
[bb90203]505
[2b6eca8]506   print_node_stats();
[bb90203]507   /* Also, could give:
508    *  # nodes stations (ie have other than two references or are fixed)
509    *  # fixed stations (list of?)
510    */
511}
Note: See TracBrowser for help on using the repository browser.