source: git/src/sorterr.c

Last change on this file was 0b99107, checked in by Olly Betts <olly@…>, 2 weeks ago

Eliminate old FSF addresses

Update GPL/LGPL licence files and boilerplate to direct people who
didn't receive the licence text to the FSF website, as the current
versions of the FSF licence texts now do, rather than giving a postal
address.

  • Property mode set to 100644
File size: 6.7 KB
Line 
1/* sorterr.c */
2/* Sort a survex .err file */
3/* Copyright (C) 2001,2002,2005,2010,2011,2014 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, see
17 * <https://www.gnu.org/licenses/>.
18 */
19
20#include <config.h>
21
22#include <ctype.h>
23#include <math.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27
28#include "cmdline.h"
29#include "filename.h"
30#include "message.h"
31#include "osalloc.h"
32
33static const struct option long_opts[] = {
34   /* const char *name; int has_arg (0 no_argument, 1 required_*, 2 optional_*); int *flag; int val; */
35   {"horizontal", no_argument, 0, 'h'},
36   {"vertical", no_argument, 0, 'v'},
37   {"percentage", no_argument, 0, 'p'},
38   {"per-leg", no_argument, 0, 'l'},
39   {"replace", no_argument, 0, 'r'},
40   {"help", no_argument, 0, HLP_HELP},
41   {"version", no_argument, 0, HLP_VERSION},
42   {0, 0, 0, 0}
43};
44
45#define short_opts "hvplr"
46
47static struct help_msg help[] = {
48/*                              <-- */
49   /* TRANSLATORS: --help output for sorterr --horizontal option */
50   {HLP_ENCODELONG(0),        /*sort by horizontal error factor*/179, 0, 0},
51   /* TRANSLATORS: --help output for sorterr --vertical option */
52   {HLP_ENCODELONG(1),        /*sort by vertical error factor*/180, 0, 0},
53   /* TRANSLATORS: --help output for sorterr --percentage option */
54   {HLP_ENCODELONG(2),        /*sort by percentage error*/181, 0, 0},
55   /* TRANSLATORS: --help output for sorterr --per-leg option */
56   {HLP_ENCODELONG(3),        /*sort by error per leg*/182, 0, 0},
57   /* TRANSLATORS: --help output for sorterr --replace option */
58   {HLP_ENCODELONG(4),        /*replace .err file with re-sorted version*/183, 0, 0},
59   {0, 0, 0, 0}
60};
61
62typedef struct {
63   double err;
64   long fpos;
65} trav;
66
67static void
68skipline(const char *fnm, FILE *fh)
69{
70   int ch;
71   do {
72      ch = GETC(fh);
73   } while (ch != '\n' && ch != EOF);
74
75   if (ch == EOF) {
76      if (FERROR(fh))
77         fatalerror_in_file(fnm, 0, /*Error reading file*/18);
78      fatalerror_in_file(fnm, 0, /*Couldn’t parse .err file*/112);
79   }
80}
81
82static void
83printline(const char *fnm, FILE *fh, FILE *fh_out)
84{
85   int ch;
86   do {
87      ch = GETC(fh);
88      if (ch != EOF && ch != '\r' && ch != '\n') PUTC(ch, fh_out);
89   } while (ch != '\n' && ch != EOF);
90   PUTC('\n', fh_out);
91
92   if (ch == EOF) {
93      if (FERROR(fh))
94         fatalerror_in_file(fnm, 0, /*Error reading file*/18);
95      fatalerror_in_file(fnm, 0, /*Couldn’t parse .err file*/112);
96   }
97}
98
99static int
100cmp_trav(const void *a, const void *b)
101{
102   double diff = ((const trav *)a)->err - ((const trav *)b)->err;
103   if (diff < 0) return -1;
104   if (diff > 0) return 1;
105   return 0;
106}
107
108int
109main(int argc, char **argv)
110{
111   char *fnm;
112   FILE *fh;
113   char sortby = 'A';
114   size_t len = 1024;
115   trav *blk = osmalloc(1024 * sizeof(trav));
116   size_t next = 0;
117   size_t howmany = 0;
118   FILE *fh_out = stdout;
119   char *fnm_out = NULL;
120
121   msg_init(argv);
122
123   /* TRANSLATORS: Part of sorterr --help */
124   cmdline_set_syntax_message(/*ERR_FILE [HOW_MANY]*/268, 0, NULL);
125   cmdline_init(argc, argv, short_opts, long_opts, NULL, help, 1, 2);
126   while (1) {
127      int opt = cmdline_getopt();
128      if (opt == EOF) break;
129      switch (opt) {
130       case 'h': case 'v': case 'p': case 'l':
131         sortby = toupper(opt);
132         break;
133       case 'r':
134         fh_out = NULL;
135         break;
136      }
137   }
138
139   fnm = argv[optind++];
140   if (argv[optind]) howmany = atoi(argv[optind]);
141
142   fh = fopen(fnm, "rb");
143   if (!fh) fatalerror(/*Couldn’t open file “%s”*/1, fnm);
144
145   /* 4 line paragraphs, separated by blank lines...
146    * 041.verhall.12 - 041.verhall.13
147    * Original length   2.97m (  1 legs), moved   0.04m ( 0.04m/leg). Error   1.19%
148    * 0.222332
149    * H: 0.224749 V: 0.215352
150    *
151    */
152   while (1) {
153      int ch;
154      if (next == len) {
155         len += len;
156         blk = osrealloc(blk, len * sizeof(trav));
157      }
158      blk[next].fpos = ftell(fh);
159      ch = GETC(fh);
160      if (ch == EOF) break;
161      skipline(fnm, fh);
162      switch (sortby) {
163       case 'A':
164         skipline(fnm, fh);
165         if (fscanf(fh, "%lf", &blk[next].err) != 1) {
166            baderrfile:
167            fatalerror_in_file(fnm, 0, /*Couldn’t parse .err file*/112);
168         }
169         skipline(fnm, fh);
170         skipline(fnm, fh);
171         break;
172       case 'H': case 'V':
173         skipline(fnm, fh);
174         skipline(fnm, fh);
175         do {
176            ch = GETC(fh);
177            if (ch == '\n' || ch == EOF) goto baderrfile;
178         } while (ch != sortby);
179         if (fscanf(fh, ":%lf", &blk[next].err) != 1) goto baderrfile;
180         skipline(fnm, fh);
181         break;
182       case 'P':
183         do {
184            ch = GETC(fh);
185            if (ch == '\n' || ch == EOF) goto baderrfile;
186         } while (ch != ')');
187         do {
188            ch = GETC(fh);
189            if (ch == '\n' || ch == EOF) goto baderrfile;
190         } while (ch != ')');
191         do {
192            ch = GETC(fh);
193            if (ch == '\n' || ch == EOF) goto baderrfile;
194         } while (!isdigit(ch));
195         ungetc(ch, fh);
196         if (fscanf(fh, "%lf", &blk[next].err) != 1) goto baderrfile;
197         skipline(fnm, fh);
198         skipline(fnm, fh);
199         skipline(fnm, fh);
200         break;
201       case 'L':
202         do {
203            ch = GETC(fh);
204            if (ch == '\n' || ch == EOF) goto baderrfile;
205         } while (ch != ')');
206         do {
207            ch = GETC(fh);
208            if (ch == '\n' || ch == EOF) goto baderrfile;
209         } while (ch != '(');
210         if (fscanf(fh, "%lf", &blk[next].err) != 1) goto baderrfile;
211         skipline(fnm, fh);
212         skipline(fnm, fh);
213         skipline(fnm, fh);
214         break;
215      }
216      skipline(fnm, fh);
217      next++;
218   }
219
220   if (next == 0) {
221      /* no entries - nothing more to do whether -r is specified or not */
222      exit(EXIT_SUCCESS);
223   }
224
225   qsort(blk, next, sizeof(trav), cmp_trav);
226
227   if (fh_out == NULL) {
228      char *base = base_from_fnm(fnm);
229      fnm_out = add_ext(base, "tmp");
230      free(base);
231      fh_out = safe_fopen(fnm_out, "w");
232   }
233
234   do {
235      --next;
236      if (fseek(fh, blk[next].fpos, SEEK_SET) == -1)
237         fatalerror_in_file(fnm, 0, /*Error reading file*/18);
238
239      printline(fnm, fh, fh_out);
240      printline(fnm, fh, fh_out);
241      printline(fnm, fh, fh_out);
242      printline(fnm, fh, fh_out);
243      PUTC('\n', fh_out);
244      if (howmany && --howmany == 0) break;
245   } while (next);
246   fclose(fh);
247
248   if (fnm_out) {
249      safe_fclose(fh_out);
250#ifdef _WIN32
251      /* UNIX rename atomically replaces, so doesn't need this.
252       * WIN32 won't overwrite (from tests) so needs this code.
253       */
254      remove(fnm);
255#endif
256      rename(fnm_out, fnm);
257   }
258
259   return 0;
260}
Note: See TracBrowser for help on using the repository browser.