ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/radcompare.c
Revision: 2.12
Committed: Fri Oct 19 22:15:30 2018 UTC (5 years, 6 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.11: +74 -41 lines
Log Message:
Dynamic input line buffers up to 100 MBytes

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.12 static const char RCSid[] = "$Id: radcompare.c,v 2.11 2018/10/19 20:32:16 greg Exp $";
3 greg 2.1 #endif
4     /*
5     * Compare Radiance files for significant differences
6     *
7     * G. Ward
8     */
9    
10     #include <stdlib.h>
11     #include <math.h>
12     #include <ctype.h>
13     #include "platform.h"
14     #include "rtio.h"
15     #include "resolu.h"
16     #include "color.h"
17     #include "lookup.h"
18     /* Reporting levels */
19     #define REP_QUIET 0 /* no reporting */
20     #define REP_ERROR 1 /* report errors only */
21     #define REP_WARN 2 /* report warnings as well */
22     #define REP_VERBOSE 3 /* verbose reporting */
23    
24     int report = REP_WARN; /* reporting level */
25    
26     int ign_header = 0; /* ignore header differences? */
27    
28     double rel_min = 1e-5; /* positive for relative comparisons */
29    
30     double rms_lim = 0.01; /* RMS difference limit */
31    
32     double max_lim = 0.25; /* difference limit if non-negative */
33    
34     int lin1cnt=0, lin2cnt=0; /* file line position */
35    
36 greg 2.6 const char nsuffix[10][3] = { /* 1st, 2nd, 3rd, etc. */
37     "th","st","nd","rd","th","th","th","th","th","th"
38     };
39     #define num_sfx(n) nsuffix[(n)%10]
40    
41 greg 2.1 /* file types */
42     const char *file_type[] = {
43     "Unrecognized",
44     "TEXT_generic",
45     "ascii",
46     COLRFMT,
47     CIEFMT,
48 greg 2.4 "float",
49     "double",
50 greg 2.1 "BSDF_RBFmesh",
51     "Radiance_octree",
52     "Radiance_tmesh",
53     "BINARY_unknown",
54     NULL /* terminator */
55     };
56 greg 2.6 /* keep consistent with above */
57 greg 2.1 enum {TYP_UNKNOWN, TYP_TEXT, TYP_ASCII, TYP_RGBE, TYP_XYZE, TYP_FLOAT,
58     TYP_DOUBLE, TYP_RBFMESH, TYP_OCTREE, TYP_TMESH, TYP_BINARY};
59    
60     #define has_header(t) (!( 1L<<(t) & (1L<<TYP_TEXT | 1L<<TYP_BINARY) ))
61    
62     /* header variables to always ignore */
63     const char *hdr_ignkey[] = {
64     "SOFTWARE",
65     "CAPDATE",
66     "GMT",
67     NULL /* terminator */
68     };
69     /* header variable settings */
70     LUTAB hdr1 = LU_SINIT(free,free);
71     LUTAB hdr2 = LU_SINIT(free,free);
72    
73     /* advance appropriate file line count */
74     #define adv_linecnt(htp) (lin1cnt += (htp == &hdr1), \
75     lin2cnt += (htp == &hdr2))
76    
77 greg 2.12 typedef struct { /* dynamic line buffer */
78     char *ptr;
79     int len;
80     int siz;
81     } LINEBUF;
82    
83     #define init_line(bp) ((bp)->ptr = NULL, (bp)->siz = 0)
84     /* 100 MByte limit on line buffer */
85     #define MAXBUF (100L<<20)
86    
87 greg 2.1 /* input files */
88     char *progname = NULL;
89     const char stdin_name[] = "<stdin>";
90     const char *f1name=NULL, *f2name=NULL;
91     FILE *f1in=NULL, *f2in=NULL;
92 greg 2.3
93 greg 2.1 /* running real differences */
94     double diff2sum = 0;
95 greg 2.8 long nsum = 0;
96 greg 2.1
97     /* Report usage and exit */
98     static void
99     usage()
100     {
101     fputs("Usage: ", stderr);
102     fputs(progname, stderr);
103 greg 2.4 fputs(" [-h][-s|-w|-v][-rel min_test][-rms epsilon][-max epsilon] reference test\n",
104 greg 2.1 stderr);
105 greg 2.9 exit(2);
106 greg 2.1 }
107    
108 greg 2.12 /* Read a text line, increasing buffer size as necessary */
109     static int
110     read_line(LINEBUF *bp, FILE *fp)
111     {
112     bp->len = 0;
113     if (!bp->ptr) {
114     bp->ptr = (char *)malloc(bp->siz = 512);
115     if (!bp->ptr)
116     goto memerr;
117     }
118     while (fgets(bp->ptr + bp->len, bp->siz - bp->len, fp)) {
119     bp->len += strlen(bp->ptr + bp->len);
120     if (bp->ptr[bp->len-1] == '\n')
121     break; /* found EOL */
122     if (bp->len < bp->siz - 4)
123     continue; /* at EOF? */
124     if (bp->siz >= MAXBUF)
125     break; /* don't go to extremes */
126     if ((bp->siz += bp->siz/2) > MAXBUF)
127     bp->siz = MAXBUF;
128     bp->ptr = (char *)realloc(bp->ptr, bp->siz);
129     if (!bp->ptr)
130     goto memerr;
131     }
132     return(bp->len);
133     memerr:
134     fprintf(stderr,
135     "%s: out of memory in read_line() allocating %d byte buffer\n",
136     progname, bp->siz);
137     exit(2);
138     }
139    
140     /* Free line buffer */
141     static void
142     free_line(LINEBUF *bp)
143     {
144     bp->len = 0;
145     if (!bp->ptr) return;
146     free(bp->ptr);
147     bp->ptr = NULL;
148     }
149    
150 greg 2.1 /* Get type ID from name (or 0 if not found) */
151     static int
152     xlate_type(const char *nm)
153     {
154     int i;
155    
156     if (!nm || !*nm)
157     return(TYP_UNKNOWN);
158     for (i = 1; file_type[i]; i++)
159     if (!strcmp(nm, file_type[i]))
160     return(i);
161     return(TYP_UNKNOWN);
162     }
163    
164     /* Compare real values and keep track of differences */
165     static int
166     real_check(double r1, double r2)
167     {
168     double diff2 = (r1 - r2)*(r1 - r2);
169    
170     if (rel_min > 0) { /* doing relative differences? */
171     double av2 = .25*(r1*r1 + 2.*fabs(r1*r2) + r2*r2);
172     if (av2 > rel_min*rel_min)
173     diff2 /= av2;
174     }
175     if (max_lim >= 0 && diff2 > max_lim*max_lim) {
176     if (report != REP_QUIET)
177 greg 2.4 printf(
178 greg 2.5 "%s: %sdifference between %.8g and %.8g exceeds epsilon of %.8g\n",
179 greg 2.1 progname,
180     (rel_min > 0) ? "relative " : "",
181 greg 2.5 r1, r2, max_lim);
182 greg 2.1 return(0);
183     }
184     diff2sum += diff2;
185     nsum++;
186     return(1);
187     }
188    
189 greg 2.8 /* Compare two color values for equivalence */
190     static int
191     color_check(COLOR c1, COLOR c2)
192     {
193     int p;
194    
195     if (!real_check(colval(c1,RED)+colval(c1,GRN)+colval(c1,BLU)*(1./3.),
196     colval(c2,RED)+colval(c2,GRN)+colval(c2,BLU))*(1./3.))
197     return(0);
198    
199     p = (colval(c1,GRN) > colval(c1,RED)) ? GRN : RED;
200     if (colval(c1,BLU) > colval(c1,p)) p = BLU;
201    
202     return(real_check(colval(c1,p), colval(c2,p)));
203     }
204    
205 greg 2.1 /* Compare two strings for equivalence */
206     static int
207     equiv_string(char *s1, char *s2)
208     {
209     #define CLS_STR 0
210     #define CLS_INT 1
211     #define CLS_FLT 2
212     /* skip whitespace at beginning */
213     while (isspace(*s1)) s1++;
214     while (isspace(*s2)) s2++;
215     while (*s1) { /* check each word */
216 greg 2.2 int inquote;
217     if (!*s2) /* unexpected EOL in s2? */
218     return(0);
219     inquote = *s1;
220 greg 2.1 if ((inquote != '\'') & (inquote != '"'))
221     inquote = 0;
222     if (inquote) { /* quoted text must match exactly */
223     if (*s1++ != *s2++)
224     return(0);
225     while (*s1 != inquote) {
226     if (!*s1)
227     return(0);
228     if (*s1++ != *s2++)
229     return(0);
230     }
231     s1++;
232     if (*s2++ != inquote)
233     return(0);
234     } else { /* else classify word type */
235     char *s1s = s1;
236     char *s2s = s2;
237     int cls = CLS_STR;
238     s1 = sskip(s1);
239     s2 = sskip(s2);
240     if (iskip(s1s) == s1) {
241     if (iskip(s2s) == s2)
242     cls = CLS_INT;
243     else if (fskip(s2s) == s2)
244     cls = CLS_FLT;
245     } else if (fskip(s1s) == s1) {
246     if (fskip(s2s) != s2)
247     return(0);
248     cls = CLS_FLT;
249     }
250     switch (cls) {
251     case CLS_INT: /* strncmp() faster */
252     case CLS_STR:
253     if (s1 - s1s != s2 - s2s)
254     return(0);
255     if (strncmp(s1s, s2s, s1 - s1s))
256     return(0);
257     break;
258     case CLS_FLT:
259     if (!real_check(atof(s1s), atof(s2s)))
260     return(0);
261     break;
262     }
263     }
264     while (isspace(*s1)) s1++;
265     while (isspace(*s2)) s2++;
266     }
267     return(!*s2); /* match if we reached the end of s2, too */
268     #undef CLS_STR
269     #undef CLS_INT
270     #undef CLS_FLT
271     }
272    
273     /* Check if string is var=value pair and set if not in ignore list */
274     static int
275     setheadvar(char *val, void *p)
276     {
277     LUTAB *htp = (LUTAB *)p;
278     LUENT *tep;
279     char *key;
280     int kln, vln;
281     int n;
282    
283 greg 2.3 adv_linecnt(htp); /* side-effect is to count lines */
284 greg 2.1 if (!isalpha(*val)) /* key must start line */
285     return(0);
286     key = val++;
287     while (*val && !isspace(*val) & (*val != '='))
288     val++;
289     kln = val - key;
290     while (isspace(*val)) /* check for value */
291     *val++ = '\0';
292     if (*val != '=')
293     return(0);
294     *val++ = '\0';
295     while (isspace(*val))
296     val++;
297     if (!*val) /* nothing set? */
298     return(0);
299     /* check if key to ignore */
300     for (n = 0; hdr_ignkey[n]; n++)
301     if (!strcmp(key, hdr_ignkey[n]))
302     return(0);
303 greg 2.6 vln = strlen(val); /* eliminate space and newline at end */
304     while (isspace(val[--vln]))
305     ;
306     val[++vln] = '\0';
307 greg 2.1 if (!(tep = lu_find(htp, key)))
308 greg 2.4 return(-1); /* memory allocation error */
309 greg 2.1 if (!tep->key)
310     tep->key = strcpy(malloc(kln+1), key);
311     if (tep->data)
312     free(tep->data);
313     tep->data = strcpy(malloc(vln+1), val);
314     return(1);
315     }
316    
317     /* Lookup correspondent in other header */
318     static int
319     match_val(const LUENT *ep1, void *p2)
320     {
321     const LUENT *ep2 = lu_find((LUTAB *)p2, ep1->key);
322     if (!ep2 || !ep2->data) {
323     if (report != REP_QUIET)
324 greg 2.5 printf("%s: variable '%s' missing in '%s'\n",
325 greg 2.1 progname, ep1->key, f2name);
326     return(-1);
327     }
328     if (!equiv_string((char *)ep1->data, (char *)ep2->data)) {
329 greg 2.4 if (report != REP_QUIET) {
330 greg 2.5 printf("%s: header variable '%s' has different values\n",
331 greg 2.1 progname, ep1->key);
332 greg 2.4 if (report >= REP_VERBOSE) {
333     printf("%s: %s=%s\n", f1name,
334     ep1->key, (char *)ep1->data);
335     printf("%s: %s=%s\n", f2name,
336     ep2->key, (char *)ep2->data);
337     }
338     }
339 greg 2.1 return(-1);
340     }
341     return(1); /* good match */
342     }
343    
344     /* Compare two sets of header variables */
345     static int
346 greg 2.10 headers_match()
347 greg 2.1 {
348 greg 2.10 int ne = lu_doall(&hdr1, match_val, &hdr2);
349 greg 2.1 if (ne < 0)
350     return(0); /* something didn't match! */
351 greg 2.4 /* non-fatal if second header has extra */
352 greg 2.10 if (report >= REP_WARN && (ne = lu_doall(&hdr2, NULL, NULL) - ne))
353 greg 2.5 printf("%s: warning - '%s' has %d extra header setting(s)\n",
354 greg 2.1 progname, f2name, ne);
355     return(1); /* good match */
356     }
357    
358     /* Check generic input to determine if it is binary, -1 on error */
359     static int
360     input_is_binary(FILE *fin)
361     {
362     int n = 0;
363     int c = 0;
364    
365     while ((c = getc(fin)) != EOF) {
366 greg 2.5 ++n;
367 greg 2.1 if (!c | (c > 127))
368     break; /* non-ascii character */
369 greg 2.5 if (n >= 10240)
370 greg 2.1 break; /* enough to be confident */
371     }
372     if (!n)
373     return(-1); /* first read failed */
374     if (fseek(fin, 0L, 0) < 0)
375     return(-1); /* rewind failed */
376     return(!c | (c > 127));
377     }
378    
379     /* Identify and return data type from header (if one) */
380     static int
381     identify_type(const char *name, FILE *fin, LUTAB *htp)
382     {
383     extern const char HDRSTR[];
384     int c;
385     /* check magic header start */
386     if ((c = getc(fin)) != HDRSTR[0]) {
387     if (c == EOF) goto badeof;
388     ungetc(c, fin);
389     c = 0;
390     } else if ((c = getc(fin)) != HDRSTR[1]) {
391     if (c == EOF) goto badeof;
392     ungetc(c, fin); ungetc(HDRSTR[0], fin);
393     c = 0;
394     }
395     if (c) { /* appears to have a header */
396     char sbuf[32];
397     if (!fgets(sbuf, sizeof(sbuf), fin))
398     goto badeof;
399 greg 2.3 adv_linecnt(htp); /* for #?ID string */
400 greg 2.1 if (report >= REP_WARN && strncmp(sbuf, "RADIANCE", 8)) {
401 greg 2.4 fputs(name, stdout);
402     fputs(": warning - unexpected header ID: ", stdout);
403     fputs(sbuf, stdout);
404 greg 2.1 }
405     if (getheader(fin, setheadvar, htp) < 0) {
406     fputs(name, stderr);
407 greg 2.6 fputs(": unknown error reading header\n", stderr);
408 greg 2.1 return(-1);
409     }
410     adv_linecnt(htp); /* for trailing emtpy line */
411     return(xlate_type((const char *)lu_find(htp,"FORMAT")->data));
412     }
413     c = input_is_binary(fin); /* else peek to see if binary */
414     if (c < 0) {
415     fputs(name, stderr);
416     fputs(": read/seek error\n", stderr);
417     return(-1);
418     }
419     if (c)
420     return(TYP_BINARY);
421     SET_FILE_TEXT(fin); /* originally set to binary */
422     return(TYP_TEXT);
423     badeof:
424     if (report != REP_QUIET) {
425 greg 2.4 fputs(name, stdout);
426 greg 2.6 fputs(": unexpected end-of-file\n", stdout);
427 greg 2.1 }
428     return(-1);
429     }
430    
431     /* Check that overall RMS error is below threshold */
432     static int
433     good_RMS()
434     {
435     if (!nsum)
436     return(1);
437     if (diff2sum/(double)nsum > rms_lim*rms_lim) {
438     if (report != REP_QUIET)
439 greg 2.4 printf(
440 greg 2.5 "%s: %sRMS difference between '%s' and '%s' of %.5g exceeds limit of %.5g\n",
441 greg 2.1 progname,
442     (rel_min > 0) ? "relative " : "",
443     f1name, f2name,
444 greg 2.5 sqrt(diff2sum/(double)nsum), rms_lim);
445 greg 2.1 return(0);
446     }
447     if (report >= REP_VERBOSE)
448 greg 2.4 printf("%s: %sRMS difference of reals in '%s' and '%s' is %.5g\n",
449 greg 2.1 progname, (rel_min > 0) ? "relative " : "",
450     f1name, f2name, sqrt(diff2sum/(double)nsum));
451     return(1);
452     }
453    
454     /* Compare two inputs as generic binary files */
455     static int
456     compare_binary()
457     {
458 greg 2.5 int c1=0, c2=0;
459    
460 greg 2.1 if (report >= REP_VERBOSE) {
461 greg 2.4 fputs(progname, stdout);
462     fputs(": comparing inputs as binary\n", stdout);
463 greg 2.1 }
464     for ( ; ; ) { /* exact byte matching */
465 greg 2.5 c1 = getc(f1in);
466     c2 = getc(f2in);
467 greg 2.1 if (c1 == EOF) {
468     if (c2 == EOF)
469     return(1); /* success! */
470     if (report != REP_QUIET) {
471 greg 2.4 fputs(f1name, stdout);
472 greg 2.6 fputs(": unexpected end-of-file\n", stdout);
473 greg 2.1 }
474     return(0);
475     }
476     if (c2 == EOF) {
477     if (report != REP_QUIET) {
478 greg 2.4 fputs(f2name, stdout);
479 greg 2.6 fputs(": unexpected end-of-file\n", stdout);
480 greg 2.1 }
481     return(0);
482     }
483     if (c1 != c2)
484     break; /* quit and report difference */
485     }
486     if (report == REP_QUIET)
487     return(0);
488 greg 2.5 printf("%s: binary files '%s' and '%s' differ at byte offset %ld|%ld\n",
489 greg 2.1 progname, f1name, f2name, ftell(f1in), ftell(f2in));
490 greg 2.5 if (report >= REP_VERBOSE)
491     printf("%s: byte in '%s' is 0x%X, byte in '%s' is 0x%X\n",
492     progname, f1name, c1, f2name, c2);
493 greg 2.1 return(0);
494     }
495    
496     /* Compare two inputs as generic text files */
497     static int
498     compare_text()
499     {
500 greg 2.12 LINEBUF l1buf, l2buf;
501 greg 2.1
502     if (report >= REP_VERBOSE) {
503 greg 2.4 fputs(progname, stdout);
504     fputs(": comparing inputs as ASCII text\n", stdout);
505 greg 2.1 }
506 greg 2.12 init_line(&l1buf); init_line(&l2buf); /* compare a line at a time */
507     while (read_line(&l1buf, f1in)) {
508     lin1cnt++;
509     if (!*sskip2(l1buf.ptr,0))
510 greg 2.1 continue; /* ignore empty lines */
511 greg 2.12
512     while (read_line(&l2buf, f2in)) {
513     lin2cnt++;
514     if (*sskip2(l2buf.ptr,0))
515 greg 2.1 break; /* found other non-empty line */
516     }
517     if (feof(f2in)) {
518     if (report != REP_QUIET) {
519 greg 2.4 fputs(f2name, stdout);
520 greg 2.6 fputs(": unexpected end-of-file\n", stdout);
521 greg 2.1 }
522 greg 2.12 free_line(&l1buf); free_line(&l2buf);
523 greg 2.1 return(0);
524     }
525     /* compare non-empty lines */
526 greg 2.12 if (!equiv_string(l1buf.ptr, l2buf.ptr)) {
527 greg 2.1 if (report != REP_QUIET) {
528 greg 2.4 printf("%s: inputs '%s' and '%s' differ at line %d|%d\n",
529 greg 2.1 progname, f1name, f2name,
530     lin1cnt, lin2cnt);
531 greg 2.12 if ( report >= REP_VERBOSE &&
532     (l1buf.len < 256) &
533     (l2buf.len < 256) ) {
534 greg 2.4 fputs("------------- Mismatch -------------\n", stdout);
535 greg 2.5 printf("%s@%d:\t%s", f1name,
536 greg 2.12 lin1cnt, l1buf.ptr);
537 greg 2.5 printf("%s@%d:\t%s", f2name,
538 greg 2.12 lin2cnt, l2buf.ptr);
539 greg 2.1 }
540     }
541 greg 2.12 free_line(&l1buf); free_line(&l2buf);
542 greg 2.1 return(0);
543     }
544     }
545     /* check for EOF on input 2 */
546 greg 2.12 while (read_line(&l2buf, f2in)) {
547     if (!*sskip2(l2buf.ptr,0))
548 greg 2.1 continue;
549     if (report != REP_QUIET) {
550 greg 2.4 fputs(f1name, stdout);
551 greg 2.6 fputs(": unexpected end-of-file\n", stdout);
552 greg 2.1 }
553 greg 2.12 free_line(&l1buf); free_line(&l2buf);
554 greg 2.1 return(0);
555     }
556 greg 2.12 free_line(&l1buf); free_line(&l2buf);
557 greg 2.1 return(good_RMS()); /* final check for reals */
558     }
559    
560     /* Compare two inputs that are known to be RGBE or XYZE images */
561     static int
562     compare_hdr()
563     {
564     RESOLU rs1, rs2;
565     COLOR *scan1, *scan2;
566     int x, y;
567    
568 greg 2.6 if (report >= REP_VERBOSE) {
569     fputs(progname, stdout);
570     fputs(": comparing inputs as HDR images\n", stdout);
571     }
572 greg 2.1 fgetsresolu(&rs1, f1in);
573     fgetsresolu(&rs2, f2in);
574     if (rs1.rt != rs2.rt) {
575     if (report != REP_QUIET)
576 greg 2.4 printf(
577 greg 2.1 "%s: Images '%s' and '%s' have different pixel ordering\n",
578     progname, f1name, f2name);
579     return(0);
580     }
581     if ((rs1.xr != rs2.xr) | (rs1.yr != rs2.yr)) {
582     if (report != REP_QUIET)
583 greg 2.4 printf(
584 greg 2.1 "%s: Images '%s' and '%s' are different sizes\n",
585     progname, f1name, f2name);
586     return(0);
587     }
588     scan1 = (COLOR *)malloc(scanlen(&rs1)*sizeof(COLOR));
589     scan2 = (COLOR *)malloc(scanlen(&rs2)*sizeof(COLOR));
590     if (!scan1 | !scan2) {
591     fprintf(stderr, "%s: out of memory in compare_hdr()\n", progname);
592     exit(2);
593     }
594     for (y = 0; y < numscans(&rs1); y++) {
595     if ((freadscan(scan1, scanlen(&rs1), f1in) < 0) |
596     (freadscan(scan2, scanlen(&rs2), f2in) < 0)) {
597     if (report != REP_QUIET)
598 greg 2.6 printf("%s: unexpected end-of-file\n",
599 greg 2.1 progname);
600     free(scan1);
601     free(scan2);
602     return(0);
603     }
604 greg 2.5 for (x = 0; x < scanlen(&rs1); x++) {
605 greg 2.8 if (color_check(scan1[x], scan2[x]))
606 greg 2.1 continue;
607     if (report != REP_QUIET) {
608 greg 2.4 printf(
609 greg 2.1 "%s: pixels at scanline %d offset %d differ\n",
610     progname, y, x);
611     if (report >= REP_VERBOSE) {
612 greg 2.4 printf("%s: (R,G,B)=(%g,%g,%g)\n",
613 greg 2.1 f1name, colval(scan1[x],RED),
614     colval(scan1[x],GRN),
615     colval(scan1[x],BLU));
616 greg 2.4 printf("%s: (R,G,B)=(%g,%g,%g)\n",
617 greg 2.1 f2name, colval(scan2[x],RED),
618     colval(scan2[x],GRN),
619     colval(scan2[x],BLU));
620     }
621     }
622     free(scan1);
623     free(scan2);
624     return(0);
625     }
626     }
627     free(scan1);
628     free(scan2);
629     return(good_RMS()); /* final check of RMS */
630     }
631    
632     /* Compare two inputs that are known to be 32-bit floating-point data */
633     static int
634     compare_float()
635     {
636     long nread = 0;
637     float f1, f2;
638    
639 greg 2.6 if (report >= REP_VERBOSE) {
640     fputs(progname, stdout);
641 greg 2.7 fputs(": comparing inputs as 32-bit IEEE floats\n", stdout);
642 greg 2.6 }
643 greg 2.1 while (getbinary(&f1, sizeof(f1), 1, f1in)) {
644     if (!getbinary(&f2, sizeof(f2), 1, f2in))
645     goto badeof;
646     ++nread;
647     if (real_check(f1, f2))
648     continue;
649     if (report != REP_QUIET)
650 greg 2.4 printf("%s: %ld%s float values differ\n",
651 greg 2.6 progname, nread, num_sfx(nread));
652 greg 2.1 return(0);
653     }
654     if (!getbinary(&f2, sizeof(f2), 1, f2in))
655     return(good_RMS()); /* final check of RMS */
656     badeof:
657     if (report != REP_QUIET)
658 greg 2.6 printf("%s: unexpected end-of-file\n", progname);
659 greg 2.1 return(0);
660     }
661    
662     /* Compare two inputs that are known to be 64-bit floating-point data */
663     static int
664     compare_double()
665     {
666     long nread = 0;
667     double f1, f2;
668    
669 greg 2.6 if (report >= REP_VERBOSE) {
670     fputs(progname, stdout);
671 greg 2.7 fputs(": comparing inputs as 64-bit IEEE doubles\n", stdout);
672 greg 2.6 }
673 greg 2.1 while (getbinary(&f1, sizeof(f1), 1, f1in)) {
674     if (!getbinary(&f2, sizeof(f2), 1, f2in))
675     goto badeof;
676     ++nread;
677     if (real_check(f1, f2))
678     continue;
679     if (report != REP_QUIET)
680 greg 2.6 printf("%s: %ld%s double values differ\n",
681     progname, nread, num_sfx(nread));
682 greg 2.1 return(0);
683     }
684     if (!getbinary(&f2, sizeof(f2), 1, f2in))
685     return(good_RMS()); /* final check of RMS */
686     badeof:
687     if (report != REP_QUIET)
688 greg 2.6 printf("%s: unexpected end-of-file\n", progname);
689 greg 2.1 return(0);
690     }
691    
692     /* Compare two Radiance files for equivalence */
693     int
694     main(int argc, char *argv[])
695     {
696     int typ1, typ2;
697     int a;
698    
699     progname = argv[0];
700     for (a = 1; a < argc && argv[a][0] == '-'; a++) {
701     switch (argv[a][1]) {
702     case 'h': /* ignore header info. */
703     ign_header = !ign_header;
704     continue;
705     case 's': /* silent operation */
706     report = REP_QUIET;
707     continue;
708     case 'w': /* turn off warnings */
709     if (report > REP_ERROR)
710     report = REP_ERROR;
711     continue;
712     case 'v': /* turn on verbose mode */
713     report = REP_VERBOSE;
714     continue;
715     case 'm': /* maximum epsilon */
716     max_lim = atof(argv[++a]);
717     continue;
718     case 'r':
719     if (argv[a][2] == 'e') /* relative difference */
720     rel_min = atof(argv[++a]);
721     else if (argv[a][2] == 'm') /* RMS limit */
722     rms_lim = atof(argv[++a]);
723     else
724     usage();
725     continue;
726     case '\0': /* first file from stdin */
727     f1in = stdin;
728     f1name = stdin_name;
729     break;
730     default:
731     usage();
732     }
733     break;
734     }
735 greg 2.7 if (a != argc-2) /* make sure of two inputs */
736 greg 2.1 usage();
737 greg 2.7 if (!strcmp(argv[a], argv[a+1])) { /* inputs are same? */
738 greg 2.1 if (report >= REP_WARN)
739 greg 2.4 printf("%s: warning - identical inputs given\n",
740 greg 2.1 progname);
741     return(0);
742     }
743 greg 2.7 if (!f1name) f1name = argv[a];
744     if (!f2name) f2name = argv[a+1];
745 greg 2.1 /* open inputs */
746     SET_FILE_BINARY(stdin); /* in case we're using it */
747     if (!f1in && !(f1in = fopen(f1name, "rb"))) {
748     fprintf(stderr, "%s: cannot open for reading\n", f1name);
749 greg 2.9 return(2);
750 greg 2.1 }
751     if (!strcmp(f2name, "-")) {
752     f2in = stdin;
753     f2name = stdin_name;
754     } else if (!(f2in = fopen(f2name, "rb"))) {
755     fprintf(stderr, "%s: cannot open for reading\n", f2name);
756 greg 2.9 return(2);
757 greg 2.1 }
758     /* load headers */
759     if ((typ1 = identify_type(f1name, f1in, &hdr1)) < 0)
760 greg 2.9 return(2);
761 greg 2.1 if ((typ2 = identify_type(f2name, f2in, &hdr2)) < 0)
762 greg 2.9 return(2);
763 greg 2.1 if (typ1 != typ2) {
764     if (report != REP_QUIET)
765 greg 2.4 printf("%s: '%s' is %s and '%s' is %s\n",
766 greg 2.1 progname, f1name, file_type[typ1],
767     f2name, file_type[typ2]);
768     return(1);
769     }
770     ign_header |= !has_header(typ1); /* check headers if indicated */
771 greg 2.10 if (!ign_header && !headers_match())
772 greg 2.1 return(1);
773     lu_done(&hdr1); lu_done(&hdr2);
774     if (!ign_header & (report >= REP_WARN)) {
775     if (typ1 == TYP_UNKNOWN)
776 greg 2.4 printf("%s: warning - unrecognized format, comparing as binary\n",
777 greg 2.1 progname);
778     if (lin1cnt != lin2cnt)
779 greg 2.4 printf("%s: warning - headers are different lengths\n",
780 greg 2.1 progname);
781     }
782     if (report >= REP_VERBOSE)
783 greg 2.4 printf("%s: input file type is %s\n",
784 greg 2.1 progname, file_type[typ1]);
785    
786     switch (typ1) { /* compare based on type */
787     case TYP_BINARY:
788     case TYP_TMESH:
789     case TYP_OCTREE:
790     case TYP_RBFMESH:
791     case TYP_UNKNOWN:
792     return( !compare_binary() );
793     case TYP_TEXT:
794     case TYP_ASCII:
795     return( !compare_text() );
796     case TYP_RGBE:
797     case TYP_XYZE:
798     return( !compare_hdr() );
799     case TYP_FLOAT:
800     return( !compare_float() );
801     case TYP_DOUBLE:
802     return( !compare_double() );
803     }
804     return(1);
805     }