source: git/src/cavern.c @ b3b0900

faster-cavernlogwalls-datawalls-data-hanging-as-warning
Last change on this file since b3b0900 was 4c83f84, checked in by Olly Betts <olly@…>, 3 weeks ago

Don't check HAVE_CONFIG_H in most cases

This check is only useful for img.c, which is intended to be usable
outside of Survex (and had fallbacks for functions which may not be
available which will get used if built in a non-autotools project).
For all the other source files it's just useless boilerplate.

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