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