source: git/src/sorterr.c

main
Last change on this file was 0b99107, checked in by Olly Betts <olly@…>, 4 months 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.