source: git/src/cavern.c @ 4fcdd51a

debug-cidebug-ci-sanitisersfaster-cavernloglog-selectstereo-2025walls-datawalls-data-hanging-as-warningwarn-only-for-hanging-survey
Last change on this file since 4fcdd51a was 1a21d04, checked in by Olly Betts <olly@…>, 3 years ago

Improve anon bound reporting

If the bound including anonymous stations is at an anonymous station but
the bound only considering named stations is the same, use the named
station for the anonymous bound too.

This helps to avoid reporting seemingly redundant bounds (though if the
anonymous bound is a fraction of a cm beyond the named bound then it'll
we'll still report what looks like a redundant bound).

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