1 /*
2 Numdiff - compare putatively similar files,
3 ignoring small numeric differences
4 Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Ivano Primi <ivprimi@libero.it>
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include "numdiff.h"
23 #include "xalloc.h"
24
25 extern Real Zero;
26
27 /* Care that internally the field numbers start from zero, not from one */
28 const long FIELDNO_MAX = (8 * FIELDMASK_SIZE);
29 const char separator = ':';
30
31 static const struct numfmt defaults = {CURRENCY,
32 DP,
33 THSEP,
34 GROUPING,
35 POS_SIGN,
36 NEG_SIGN,
37 ECH,
38 IU};
39
40 /*
41 Create a new stack of threshold values, then
42 push onto the stack the element
43
44 {
45 threshold = 0.0, beg1 = 0, end1 = FIELDNO_MAX-1,
46 beg2 = 0, end2 = FIELDNO_MAX-1,
47 double_range_spec = 0
48 }
49
50 and return a pointer to the top of the stack.
51 */
52
thrlist_new(void)53 thrlist thrlist_new (void)
54 {
55 thrlist list;
56
57 list = (thrlist) xmalloc (sizeof (thrlist_node));
58 initR (&list->threshold);
59 /* copyR (&list->threshold, Zero); */
60 list->beg1 = list->beg2 = 0;
61 list->end1 = list->end2 = FIELDNO_MAX - 1;
62 list->double_range_spec = 0;
63 list->next = NULL;
64 return list;
65 }
66
67
68 /*
69 If the string STR begins with a valid range of positive integer values, then
70 set *B to (start value - 1), *E to (end value - 1), and, if TAIL != NULL,
71 set *TAIL so that it points to the first character of STR after the range
72 specification. Finally, return THRLIST_OK (0).
73 For example, if STR == "1-10#abc", then *B will be set to 0, *E to 9, and
74 *TAIL will point to the character '#'.
75 If the string STR does not begin with a valid range of positive integer values,
76 then leave *B, *E and *TAIL unchanged and return the error code THRLIST_INVALID_FORMAT.
77
78 Remarks:
79
80 1. The following abbreviated range specifications are admitted, where N is a positive
81 integer value and M == FIELDNO_MAX :
82
83 N stays for N-N
84 -N stays for 1-N
85 N- stays for N-M .
86
87 2. The extremal values of a valid range are integer numbers between
88 1 and FIELDNO_MAX. In addition, the start value may not be greater than
89 the end value. In case these conditions are not both true
90 the function returns THRLIST_INVALID_FORMAT.
91 */
92
set_interval(const char * str,unsigned long * b,unsigned long * e,char ** tail)93 static int set_interval (const char *str, unsigned long *b, unsigned long *e, char **tail)
94 {
95 long beg, end;
96 char *ptr, *endptr;
97
98 beg = end = -1;
99 if (!str || !*str)
100 return THRLIST_INVALID_FORMAT; /* illegal input */
101
102 /* If we arrive here we are sure that *str != '\0' ! */
103 if ((beg = strtol (str, &endptr, 10)) == 0
104 || beg > FIELDNO_MAX || beg < -FIELDNO_MAX)
105 return THRLIST_INVALID_FORMAT; /* illegal input */
106 else if (beg < 0)
107 {
108 if (*endptr == '\0' || *endptr == separator)
109 {
110 end = -beg;
111 beg = 1;
112 }
113 else
114 return THRLIST_INVALID_FORMAT;
115 }
116 else if (*endptr == '\0' || *endptr == separator)
117 end = beg;
118 else if (*endptr == '-')
119 {
120 ptr = endptr + 1;
121 if (*ptr == '\0' || *ptr == separator)
122 end = FIELDNO_MAX;
123 else
124 {
125 if ((end = strtol (ptr, &endptr, 10)) <= 0
126 || (*endptr != '\0' && *endptr != separator) || end > FIELDNO_MAX)
127 return THRLIST_INVALID_FORMAT; /* illegal input */
128 }
129 }
130 if (beg > end)
131 return THRLIST_INVALID_FORMAT;
132 else
133 {
134 *b = (unsigned long)(beg-1);
135 *e = (unsigned long)(end-1);
136 if (tail != NULL)
137 *tail = endptr;
138 return THRLIST_OK;
139 }
140 }
141
142
143 /*
144 If the string DEF contains a specification of the form
145
146 RANGE1:RANGE2,
147
148 where the ranges RANGE1 and RANGE2 are both in the form accepted by
149 the function set_interval() and have the same length, then
150 set *BEG1, *END1, *BEG2 and *END2 to the extremal
151 values of RANGE1 and RANGE2, respectively, after
152 decrementing them of 1.
153 Then set *DBL_RNG_SPEC to 1 and return THRLIST_OK
154 to the calling function.
155
156 If the string DEF contains a specification of the form
157
158 RANGE,
159
160 where RANGE is in the form accepted by the function
161 set_interval(), then set *BEG1 and *BEG2 to
162 (start_value_of_RANGE - 1), *END1 and *END2
163 (end_value_of_RANGE - 1), and *DBL_RNG_SPEC to zero.
164 Then return THRLIST_OK to the calling function.
165
166 If DEF does not contain a valid specification of the form
167
168 RANGE or RANGE1:RANGE2
169
170 then return THRLIST_INVALID_FORMAT.
171 If the given specification is valid, but
172 RANGE1 and RANGE2 do not have the same length,
173 then return THRLIST_INVALID_RANGES.
174
175 Remark: In the last two cases after returning
176 from the function the values of
177
178 *BEG1, *BEG2, *END1, *END2 and *DBL_RNG_SPEC
179
180 can not be trusted.
181 */
182
set_intervals(const char * def,unsigned long * beg1,unsigned long * beg2,unsigned long * end1,unsigned long * end2,int * dbl_rng_spec)183 static int set_intervals (const char *def,
184 unsigned long *beg1, unsigned long *beg2,
185 unsigned long *end1, unsigned long *end2,
186 int *dbl_rng_spec)
187 {
188 char *endptr;
189 int rv;
190
191 rv = set_interval (def, beg1, end1, &endptr);
192 if (rv != 0)
193 return rv;
194 else if (*endptr == separator)
195 {
196 rv = set_interval (endptr + 1, beg2, end2, &endptr);
197 if ((rv != 0) || (*endptr != '\0'))
198 return THRLIST_INVALID_FORMAT;
199 else
200 {
201 *dbl_rng_spec = 1;
202 return (*end2 + *beg1 == *end1 + *beg2) ? THRLIST_OK : THRLIST_INVALID_RANGES;
203 }
204 }
205 else
206 {
207 /* *endptr == '\0' */
208 *beg2 = *beg1;
209 *end2 = *end1;
210 *dbl_rng_spec = 0;
211 return THRLIST_OK;
212 }
213 }
214
215
216 /*
217 Push onto the stack pointed to by PLIST a new element
218 according to the specification contained in the string
219 DEF.
220
221 This specification must have the form
222
223 1. THRESHOLD or
224
225 2. THRESHOLD:RANGE or
226
227 3. THRESHOLD:RANGE1:RANGE2
228
229 where THRESHOLD is a non-negative real (decimal) number,
230 and RANGE or RANGE1:RANGE2 must be a valid specification for
231 the function set_intervals().
232
233 In case 1. and 2. set the field DOUBLE_RANGE_SPEC of
234 the just pushed element to zero, otherwise set it to 1.
235
236 If DEF does not contain a valid specification, then
237 do nothing but return a suitable error code
238 (either THRLIST_INVALID_FORMAT or THRLIST_INVALID_RANGES).
239 Otherwise, return THRLIST_OK.
240 */
241
thrlist_add(thrlist * plist,const char * def)242 int thrlist_add (thrlist *plist, const char* def)
243 {
244 int rv, dbl_rng_spec;
245 Real r;
246 unsigned long beg1, beg2, end1, end2;
247 thrlist_node *pnode;
248 char *endptr;
249
250 if (!def || !*def)
251 return THRLIST_INVALID_FORMAT; /* no input */
252
253 /* If we arrive here we are sure that *def != '\0' ! */
254 initR (&r);
255 str2R (def, &endptr, ISCALE, &defaults, &r);
256 if (endptr == def || cmp(r, Zero) < 0)
257 {
258 delR (&r);
259 return THRLIST_INVALID_FORMAT; /* no valid positive number */
260 }
261 else
262 {
263 if (*endptr == '\0')
264 {
265 beg1 = beg2 = 0;
266 end1 = end2 = FIELDNO_MAX - 1;
267 dbl_rng_spec = 0;
268 }
269 else if (*endptr == separator)
270 {
271 if ( (rv = set_intervals (endptr + 1, &beg1, &beg2, &end1, &end2, &dbl_rng_spec)) != 0)
272 {
273 delR (&r);
274 return rv;
275 }
276 }
277 else
278 {
279 delR (&r);
280 return THRLIST_INVALID_FORMAT;
281 }
282 pnode = (thrlist_node*) xmalloc (sizeof (thrlist_node));
283 initR (&pnode->threshold);
284 copyR (&pnode->threshold, r);
285 delR (&r);
286 pnode->beg1 = beg1;
287 pnode->beg2 = beg2;
288 pnode->end1 = end1;
289 pnode->end2 = end2;
290 pnode->double_range_spec = dbl_rng_spec;
291 pnode->next = *plist;
292 *plist = pnode;
293 return THRLIST_OK;
294 }
295 }
296
297
298 /*
299 Look in the stack LIST for the first element E from the top such that
300 either E.DOUBLE_RANGE_SPEC == 0 and
301 E.BEG1 <= FIELDNO1 <= E.END1, E.BEG2 <= FIELDNO2 <= E.END2, or
302 E.DOUBLE_RANGE_SPEC == 1 and
303 E.BEG1 <= FIELDNO1 <= E.END1, FIELDNO2 == E.BEG2 + FIELDNO1 - E.BEG1 .
304 Then return the value of the comparison test between R and E.THRESHOLD.
305
306 If there is no element E in LIST satisfying one of the previous conditions
307 (this should never happen if LIST has been created by thrlist_new()),
308 then interrupt the main program via exit(EXIT_TROUBLE) after printing
309 a suitable error message on stdout.
310 */
311
thrlist_cmp(Real r,thrlist list,unsigned long fieldno1,unsigned long fieldno2)312 int thrlist_cmp (Real r, thrlist list, unsigned long fieldno1, unsigned long fieldno2)
313 {
314 thrlist_node *pnode, *pnext;
315
316 pnode = list;
317 while (pnode != NULL)
318 {
319 pnext = pnode->next;
320 if ( (pnode->double_range_spec) )
321 {
322 if (fieldno1 >= pnode->beg1 && fieldno1 <= pnode->end1 &&
323 fieldno2 == pnode->beg2 + fieldno1 - pnode->beg1)
324 {
325 return cmp (r, pnode->threshold); /* found */
326 }
327 }
328 else
329 {
330 if (fieldno1 >= pnode->beg1 && fieldno1 <= pnode->end1 &&
331 fieldno2 >= pnode->beg2 && fieldno2 <= pnode->end2)
332 {
333 return cmp (r, pnode->threshold); /* found */
334 }
335 }
336 pnode = pnext;
337 }
338 /*
339 The code execution should NEVER reach this point.
340 */
341 printf (_("Fatal error occurred during comparison of two numerical fields\n"));
342 exit (EXIT_TROUBLE);
343 }
344
345
346 /*
347 Dispose the stack pointed to by PLIST (free and clean the memory).
348 */
349
thrlist_dispose(thrlist * plist)350 void thrlist_dispose (thrlist *plist)
351 {
352 thrlist_node *pnode, *pnext;
353
354 if (plist != NULL)
355 {
356 pnode = *plist;
357 while (pnode != NULL)
358 {
359 pnext = pnode->next;
360 delR (&pnode->threshold);
361 free ((void*)pnode);
362 pnode = pnext;
363 }
364 *plist = NULL;
365 }
366 }
367