1/* $Id: tiffcmp.c 276 2010-06-30 12:18:30Z nijtmans $ */
2
3/*
4 * Copyright (c) 1988-1997 Sam Leffler
5 * Copyright (c) 1991-1997 Silicon Graphics, Inc.
6 *
7 * Permission to use, copy, modify, distribute, and sell this software and
8 * its documentation for any purpose is hereby granted without fee, provided
9 * that (i) the above copyright notices and this permission notice appear in
10 * all copies of the software and related documentation, and (ii) the names of
11 * Sam Leffler and Silicon Graphics may not be used in any advertising or
12 * publicity relating to the software without the specific, prior written
13 * permission of Sam Leffler and Silicon Graphics.
14 *
15 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
17 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
18 *
19 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
20 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
21 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
22 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
23 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
24 * OF THIS SOFTWARE.
25 */
26
27#include "tif_config.h"
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <math.h>
33
34#ifdef HAVE_UNISTD_H
35# include <unistd.h>
36#endif
37
38#include "tiffio.h"
39
40#ifndef HAVE_GETOPT
41extern int getopt(int, char**, char*);
42#endif
43
44static	int stopondiff = 1;
45static	int stoponfirsttag = 1;
46static	uint16 bitspersample = 1;
47static	uint16 samplesperpixel = 1;
48static	uint16 sampleformat = SAMPLEFORMAT_UINT;
49static	uint32 imagewidth;
50static	uint32 imagelength;
51
52static	void usage(void);
53static	int tiffcmp(TIFF*, TIFF*);
54static	int cmptags(TIFF*, TIFF*);
55static	int ContigCompare(int, uint32, unsigned char*, unsigned char*, int);
56static	int SeparateCompare(int, int, uint32, unsigned char*, unsigned char*);
57static	void PrintIntDiff(uint32, int, uint32, uint32, uint32);
58static	void PrintFloatDiff(uint32, int, uint32, double, double);
59
60static	void leof(const char*, uint32, int);
61
62int
63main(int argc, char* argv[])
64{
65	TIFF *tif1, *tif2;
66	int c, dirnum;
67	extern int optind;
68	extern char* optarg;
69
70	while ((c = getopt(argc, argv, "ltz:")) != -1)
71		switch (c) {
72		case 'l':
73			stopondiff = 0;
74			break;
75		case 'z':
76			stopondiff = atoi(optarg);
77			break;
78		case 't':
79			stoponfirsttag = 0;
80			break;
81		case '?':
82			usage();
83			/*NOTREACHED*/
84		}
85	if (argc - optind < 2)
86		usage();
87	tif1 = TIFFOpen(argv[optind], "r");
88	if (tif1 == NULL)
89		return (-1);
90	tif2 = TIFFOpen(argv[optind+1], "r");
91	if (tif2 == NULL)
92		return (-2);
93	dirnum = 0;
94	while (tiffcmp(tif1, tif2)) {
95		if (!TIFFReadDirectory(tif1)) {
96			if (!TIFFReadDirectory(tif2))
97				break;
98			printf("No more directories for %s\n",
99			    TIFFFileName(tif1));
100			return (1);
101		} else if (!TIFFReadDirectory(tif2)) {
102			printf("No more directories for %s\n",
103			    TIFFFileName(tif2));
104			return (1);
105		}
106		printf("Directory %d:\n", ++dirnum);
107	}
108
109	TIFFClose(tif1);
110	TIFFClose(tif2);
111	return (0);
112}
113
114char* stuff[] = {
115"usage: tiffcmp [options] file1 file2",
116"where options are:",
117" -l		list each byte of image data that differs between the files",
118" -z #		list specified number of bytes that differs between the files",
119" -t		ignore any differences in directory tags",
120NULL
121};
122
123static void
124usage(void)
125{
126	char buf[BUFSIZ];
127	int i;
128
129	setbuf(stderr, buf);
130        fprintf(stderr, "%s\n\n", TIFFGetVersion());
131	for (i = 0; stuff[i] != NULL; i++)
132		fprintf(stderr, "%s\n", stuff[i]);
133	exit(-1);
134}
135
136#define	checkEOF(tif, row, sample) { \
137	leof(TIFFFileName(tif), row, sample); \
138	goto bad; \
139}
140
141static	int CheckShortTag(TIFF*, TIFF*, int, char*);
142static	int CheckShort2Tag(TIFF*, TIFF*, int, char*);
143static	int CheckShortArrayTag(TIFF*, TIFF*, int, char*);
144static	int CheckLongTag(TIFF*, TIFF*, int, char*);
145static	int CheckFloatTag(TIFF*, TIFF*, int, char*);
146static	int CheckStringTag(TIFF*, TIFF*, int, char*);
147
148static int
149tiffcmp(TIFF* tif1, TIFF* tif2)
150{
151	uint16 config1, config2;
152	tsize_t size1;
153	uint32 row;
154	tsample_t s;
155	unsigned char *buf1, *buf2;
156
157	if (!CheckShortTag(tif1, tif2, TIFFTAG_BITSPERSAMPLE, "BitsPerSample"))
158		return (0);
159	if (!CheckShortTag(tif1, tif2, TIFFTAG_SAMPLESPERPIXEL, "SamplesPerPixel"))
160		return (0);
161	if (!CheckLongTag(tif1, tif2, TIFFTAG_IMAGEWIDTH, "ImageWidth"))
162		return (0);
163	if (!cmptags(tif1, tif2))
164		return (1);
165	(void) TIFFGetField(tif1, TIFFTAG_BITSPERSAMPLE, &bitspersample);
166	(void) TIFFGetField(tif1, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel);
167	(void) TIFFGetField(tif1, TIFFTAG_SAMPLEFORMAT, &sampleformat);
168	(void) TIFFGetField(tif1, TIFFTAG_IMAGEWIDTH, &imagewidth);
169	(void) TIFFGetField(tif1, TIFFTAG_IMAGELENGTH, &imagelength);
170	(void) TIFFGetField(tif1, TIFFTAG_PLANARCONFIG, &config1);
171	(void) TIFFGetField(tif2, TIFFTAG_PLANARCONFIG, &config2);
172	buf1 = (unsigned char *)_TIFFmalloc(size1 = TIFFScanlineSize(tif1));
173	buf2 = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif2));
174	if (buf1 == NULL || buf2 == NULL) {
175		fprintf(stderr, "No space for scanline buffers\n");
176		exit(-1);
177	}
178	if (config1 != config2 && bitspersample != 8 && samplesperpixel > 1) {
179		fprintf(stderr,
180"Can't handle different planar configuration w/ different bits/sample\n");
181		goto bad;
182	}
183#define	pack(a,b)	((a)<<8)|(b)
184	switch (pack(config1, config2)) {
185	case pack(PLANARCONFIG_SEPARATE, PLANARCONFIG_CONTIG):
186		for (row = 0; row < imagelength; row++) {
187			if (TIFFReadScanline(tif2, buf2, row, 0) < 0)
188				checkEOF(tif2, row, -1)
189			for (s = 0; s < samplesperpixel; s++) {
190				if (TIFFReadScanline(tif1, buf1, row, s) < 0)
191					checkEOF(tif1, row, s)
192				if (SeparateCompare(1, s, row, buf2, buf1) < 0)
193					goto bad1;
194			}
195		}
196		break;
197	case pack(PLANARCONFIG_CONTIG, PLANARCONFIG_SEPARATE):
198		for (row = 0; row < imagelength; row++) {
199			if (TIFFReadScanline(tif1, buf1, row, 0) < 0)
200				checkEOF(tif1, row, -1)
201			for (s = 0; s < samplesperpixel; s++) {
202				if (TIFFReadScanline(tif2, buf2, row, s) < 0)
203					checkEOF(tif2, row, s)
204				if (SeparateCompare(0, s, row, buf1, buf2) < 0)
205					goto bad1;
206			}
207		}
208		break;
209	case pack(PLANARCONFIG_SEPARATE, PLANARCONFIG_SEPARATE):
210		for (s = 0; s < samplesperpixel; s++)
211			for (row = 0; row < imagelength; row++) {
212				if (TIFFReadScanline(tif1, buf1, row, s) < 0)
213					checkEOF(tif1, row, s)
214				if (TIFFReadScanline(tif2, buf2, row, s) < 0)
215					checkEOF(tif2, row, s)
216				if (ContigCompare(s, row, buf1, buf2, size1) < 0)
217					goto bad1;
218			}
219		break;
220	case pack(PLANARCONFIG_CONTIG, PLANARCONFIG_CONTIG):
221		for (row = 0; row < imagelength; row++) {
222			if (TIFFReadScanline(tif1, buf1, row, 0) < 0)
223				checkEOF(tif1, row, -1)
224			if (TIFFReadScanline(tif2, buf2, row, 0) < 0)
225				checkEOF(tif2, row, -1)
226			if (ContigCompare(-1, row, buf1, buf2, size1) < 0)
227				goto bad1;
228		}
229		break;
230	}
231	if (buf1) _TIFFfree(buf1);
232	if (buf2) _TIFFfree(buf2);
233	return (1);
234bad:
235	if (stopondiff)
236		exit(1);
237bad1:
238	if (buf1) _TIFFfree(buf1);
239	if (buf2) _TIFFfree(buf2);
240	return (0);
241}
242
243#define	CmpShortField(tag, name) \
244	if (!CheckShortTag(tif1, tif2, tag, name) && stoponfirsttag) return (0)
245#define	CmpShortField2(tag, name) \
246	if (!CheckShort2Tag(tif1, tif2, tag, name) && stoponfirsttag) return (0)
247#define	CmpLongField(tag, name) \
248	if (!CheckLongTag(tif1, tif2, tag, name) && stoponfirsttag) return (0)
249#define	CmpFloatField(tag, name) \
250	if (!CheckFloatTag(tif1, tif2, tag, name) && stoponfirsttag) return (0)
251#define	CmpStringField(tag, name) \
252	if (!CheckStringTag(tif1, tif2, tag, name) && stoponfirsttag) return (0)
253#define	CmpShortArrayField(tag, name) \
254	if (!CheckShortArrayTag(tif1, tif2, tag, name) && stoponfirsttag) return (0)
255
256static int
257cmptags(TIFF* tif1, TIFF* tif2)
258{
259	CmpLongField(TIFFTAG_SUBFILETYPE,	"SubFileType");
260	CmpLongField(TIFFTAG_IMAGEWIDTH,	"ImageWidth");
261	CmpLongField(TIFFTAG_IMAGELENGTH,	"ImageLength");
262	CmpShortField(TIFFTAG_BITSPERSAMPLE,	"BitsPerSample");
263	CmpShortField(TIFFTAG_COMPRESSION,	"Compression");
264	CmpShortField(TIFFTAG_PREDICTOR,	"Predictor");
265	CmpShortField(TIFFTAG_PHOTOMETRIC,	"PhotometricInterpretation");
266	CmpShortField(TIFFTAG_THRESHHOLDING,	"Thresholding");
267	CmpShortField(TIFFTAG_FILLORDER,	"FillOrder");
268	CmpShortField(TIFFTAG_ORIENTATION,	"Orientation");
269	CmpShortField(TIFFTAG_SAMPLESPERPIXEL,	"SamplesPerPixel");
270	CmpShortField(TIFFTAG_MINSAMPLEVALUE,	"MinSampleValue");
271	CmpShortField(TIFFTAG_MAXSAMPLEVALUE,	"MaxSampleValue");
272	CmpShortField(TIFFTAG_SAMPLEFORMAT,	"SampleFormat");
273	CmpFloatField(TIFFTAG_XRESOLUTION,	"XResolution");
274	CmpFloatField(TIFFTAG_YRESOLUTION,	"YResolution");
275	CmpLongField(TIFFTAG_GROUP3OPTIONS,	"Group3Options");
276	CmpLongField(TIFFTAG_GROUP4OPTIONS,	"Group4Options");
277	CmpShortField(TIFFTAG_RESOLUTIONUNIT,	"ResolutionUnit");
278	CmpShortField(TIFFTAG_PLANARCONFIG,	"PlanarConfiguration");
279	CmpLongField(TIFFTAG_ROWSPERSTRIP,	"RowsPerStrip");
280	CmpFloatField(TIFFTAG_XPOSITION,	"XPosition");
281	CmpFloatField(TIFFTAG_YPOSITION,	"YPosition");
282	CmpShortField(TIFFTAG_GRAYRESPONSEUNIT, "GrayResponseUnit");
283	CmpShortField(TIFFTAG_COLORRESPONSEUNIT, "ColorResponseUnit");
284#ifdef notdef
285	{ uint16 *graycurve;
286	  CmpField(TIFFTAG_GRAYRESPONSECURVE, graycurve);
287	}
288	{ uint16 *red, *green, *blue;
289	  CmpField3(TIFFTAG_COLORRESPONSECURVE, red, green, blue);
290	}
291	{ uint16 *red, *green, *blue;
292	  CmpField3(TIFFTAG_COLORMAP, red, green, blue);
293	}
294#endif
295	CmpShortField2(TIFFTAG_PAGENUMBER,	"PageNumber");
296	CmpStringField(TIFFTAG_ARTIST,		"Artist");
297	CmpStringField(TIFFTAG_IMAGEDESCRIPTION,"ImageDescription");
298	CmpStringField(TIFFTAG_MAKE,		"Make");
299	CmpStringField(TIFFTAG_MODEL,		"Model");
300	CmpStringField(TIFFTAG_SOFTWARE,	"Software");
301	CmpStringField(TIFFTAG_DATETIME,	"DateTime");
302	CmpStringField(TIFFTAG_HOSTCOMPUTER,	"HostComputer");
303	CmpStringField(TIFFTAG_PAGENAME,	"PageName");
304	CmpStringField(TIFFTAG_DOCUMENTNAME,	"DocumentName");
305	CmpShortField(TIFFTAG_MATTEING,		"Matteing");
306	CmpShortArrayField(TIFFTAG_EXTRASAMPLES,"ExtraSamples");
307	return (1);
308}
309
310static int
311ContigCompare(int sample, uint32 row,
312	      unsigned char* p1, unsigned char* p2, int size)
313{
314    uint32 pix;
315    int ppb = 8 / bitspersample;
316    int	 samples_to_test;
317
318    if (memcmp(p1, p2, size) == 0)
319        return 0;
320
321    samples_to_test = (sample == -1) ? samplesperpixel : 1;
322
323    switch (bitspersample) {
324      case 1: case 2: case 4: case 8:
325      {
326          unsigned char *pix1 = p1, *pix2 = p2;
327
328          for (pix = 0; pix < imagewidth; pix += ppb) {
329              int		s;
330
331              for(s = 0; s < samples_to_test; s++) {
332                  if (*pix1 != *pix2) {
333                      if( sample == -1 )
334                          PrintIntDiff(row, s, pix, *pix1, *pix2);
335                      else
336                          PrintIntDiff(row, sample, pix, *pix1, *pix2);
337                  }
338
339                  pix1++;
340                  pix2++;
341              }
342          }
343          break;
344      }
345      case 16:
346      {
347          uint16 *pix1 = (uint16 *)p1, *pix2 = (uint16 *)p2;
348
349          for (pix = 0; pix < imagewidth; pix++) {
350              int	s;
351
352              for(s = 0; s < samples_to_test; s++) {
353                  if (*pix1 != *pix2)
354                      PrintIntDiff(row, sample, pix, *pix1, *pix2);
355
356                  pix1++;
357                  pix2++;
358              }
359          }
360          break;
361      }
362      case 32:
363	if (sampleformat == SAMPLEFORMAT_UINT
364	    || sampleformat == SAMPLEFORMAT_INT) {
365		uint32 *pix1 = (uint32 *)p1, *pix2 = (uint32 *)p2;
366
367		for (pix = 0; pix < imagewidth; pix++) {
368			int	s;
369
370			for(s = 0; s < samples_to_test; s++) {
371				if (*pix1 != *pix2) {
372					PrintIntDiff(row, sample, pix,
373						     *pix1, *pix2);
374				}
375
376				pix1++;
377				pix2++;
378			}
379		}
380	} else if (sampleformat == SAMPLEFORMAT_IEEEFP) {
381		float *pix1 = (float *)p1, *pix2 = (float *)p2;
382
383		for (pix = 0; pix < imagewidth; pix++) {
384			int	s;
385
386			for(s = 0; s < samples_to_test; s++) {
387				if (fabs(*pix1 - *pix2) < 0.000000000001) {
388					PrintFloatDiff(row, sample, pix,
389						       *pix1, *pix2);
390				}
391
392				pix1++;
393				pix2++;
394			}
395		}
396	} else {
397		  fprintf(stderr, "Sample format %d is not supported.\n",
398			  sampleformat);
399		  return -1;
400	}
401        break;
402      default:
403	fprintf(stderr, "Bit depth %d is not supported.\n", bitspersample);
404	return -1;
405    }
406
407    return 0;
408}
409
410static void
411PrintIntDiff(uint32 row, int sample, uint32 pix, uint32 w1, uint32 w2)
412{
413	if (sample < 0)
414		sample = 0;
415	switch (bitspersample) {
416	case 1:
417	case 2:
418	case 4:
419	    {
420		int32 mask1, mask2, s;
421
422		mask1 =  ~((-1) << bitspersample);
423		s = (8 - bitspersample);
424		mask2 = mask1 << s;
425		for (; mask2 && pix < imagewidth;
426		     mask2 >>= bitspersample, s -= bitspersample, pix++) {
427			if ((w1 & mask2) ^ (w2 & mask2)) {
428				printf(
429			"Scanline %lu, pixel %lu, sample %d: %01x %01x\n",
430	    				(unsigned long) row,
431					(unsigned long) pix,
432					sample,
433					(unsigned int)((w1 >> s) & mask1),
434					(unsigned int)((w2 >> s) & mask1));
435				if (--stopondiff == 0)
436					exit(1);
437			}
438		}
439		break;
440	    }
441	case 8:
442		printf("Scanline %lu, pixel %lu, sample %d: %02x %02x\n",
443		       (unsigned long) row, (unsigned long) pix, sample,
444		       (unsigned int) w1, (unsigned int) w2);
445		if (--stopondiff == 0)
446			exit(1);
447		break;
448	case 16:
449		printf("Scanline %lu, pixel %lu, sample %d: %04x %04x\n",
450		    (unsigned long) row, (unsigned long) pix, sample,
451		    (unsigned int) w1, (unsigned int) w2);
452		if (--stopondiff == 0)
453			exit(1);
454		break;
455	case 32:
456		printf("Scanline %lu, pixel %lu, sample %d: %08x %08x\n",
457		    (unsigned long) row, (unsigned long) pix, sample,
458		    (unsigned int) w1, (unsigned int) w2);
459		if (--stopondiff == 0)
460			exit(1);
461		break;
462	default:
463		break;
464	}
465}
466
467static void
468PrintFloatDiff(uint32 row, int sample, uint32 pix, double w1, double w2)
469{
470	if (sample < 0)
471		sample = 0;
472	switch (bitspersample) {
473	case 32:
474		printf("Scanline %lu, pixel %lu, sample %d: %g %g\n",
475		    (long) row, (long) pix, sample, w1, w2);
476		if (--stopondiff == 0)
477			exit(1);
478		break;
479	default:
480		break;
481	}
482}
483
484static int
485SeparateCompare(int reversed, int sample, uint32 row,
486		unsigned char* cp1, unsigned char* p2)
487{
488	uint32 npixels = imagewidth;
489	int pixel;
490
491	cp1 += sample;
492	for (pixel = 0; npixels-- > 0; pixel++, cp1 += samplesperpixel, p2++) {
493		if (*cp1 != *p2) {
494			printf("Scanline %lu, pixel %lu, sample %ld: ",
495			    (long) row, (long) pixel, (long) sample);
496			if (reversed)
497				printf("%02x %02x\n", *p2, *cp1);
498			else
499				printf("%02x %02x\n", *cp1, *p2);
500			if (--stopondiff == 0)
501				exit(1);
502		}
503	}
504
505	return 0;
506}
507
508static int
509checkTag(TIFF* tif1, TIFF* tif2, int tag, char* name, void* p1, void* p2)
510{
511
512	if (TIFFGetField(tif1, tag, p1)) {
513		if (!TIFFGetField(tif2, tag, p2)) {
514			printf("%s tag appears only in %s\n",
515			    name, TIFFFileName(tif1));
516			return (0);
517		}
518		return (1);
519	} else if (TIFFGetField(tif2, tag, p2)) {
520		printf("%s tag appears only in %s\n", name, TIFFFileName(tif2));
521		return (0);
522	}
523	return (-1);
524}
525
526#define	CHECK(cmp, fmt) {				\
527	switch (checkTag(tif1,tif2,tag,name,&v1,&v2)) {	\
528	case 1:	if (cmp)				\
529	case -1:	return (1);			\
530		printf(fmt, name, v1, v2);		\
531	}						\
532	return (0);					\
533}
534
535static int
536CheckShortTag(TIFF* tif1, TIFF* tif2, int tag, char* name)
537{
538	uint16 v1, v2;
539	CHECK(v1 == v2, "%s: %u %u\n");
540}
541
542static int
543CheckShort2Tag(TIFF* tif1, TIFF* tif2, int tag, char* name)
544{
545	uint16 v11, v12, v21, v22;
546
547	if (TIFFGetField(tif1, tag, &v11, &v12)) {
548		if (!TIFFGetField(tif2, tag, &v21, &v22)) {
549			printf("%s tag appears only in %s\n",
550			    name, TIFFFileName(tif1));
551			return (0);
552		}
553		if (v11 == v21 && v12 == v22)
554			return (1);
555		printf("%s: <%u,%u> <%u,%u>\n", name, v11, v12, v21, v22);
556	} else if (TIFFGetField(tif2, tag, &v21, &v22))
557		printf("%s tag appears only in %s\n", name, TIFFFileName(tif2));
558	else
559		return (1);
560	return (0);
561}
562
563static int
564CheckShortArrayTag(TIFF* tif1, TIFF* tif2, int tag, char* name)
565{
566	uint16 n1, *a1;
567	uint16 n2, *a2;
568
569	if (TIFFGetField(tif1, tag, &n1, &a1)) {
570		if (!TIFFGetField(tif2, tag, &n2, &a2)) {
571			printf("%s tag appears only in %s\n",
572			    name, TIFFFileName(tif1));
573			return (0);
574		}
575		if (n1 == n2) {
576			char* sep;
577			uint16 i;
578
579			if (memcmp(a1, a2, n1 * sizeof(uint16)) == 0)
580				return (1);
581			printf("%s: value mismatch, <%u:", name, n1);
582			sep = "";
583			for (i = 0; i < n1; i++)
584				printf("%s%u", sep, a1[i]), sep = ",";
585			printf("> and <%u: ", n2);
586			sep = "";
587			for (i = 0; i < n2; i++)
588				printf("%s%u", sep, a2[i]), sep = ",";
589			printf(">\n");
590		} else
591			printf("%s: %u items in %s, %u items in %s", name,
592			    n1, TIFFFileName(tif1),
593			    n2, TIFFFileName(tif2)
594			);
595	} else if (TIFFGetField(tif2, tag, &n2, &a2))
596		printf("%s tag appears only in %s\n", name, TIFFFileName(tif2));
597	else
598		return (1);
599	return (0);
600}
601
602static int
603CheckLongTag(TIFF* tif1, TIFF* tif2, int tag, char* name)
604{
605	uint32 v1, v2;
606	CHECK(v1 == v2, "%s: %u %u\n");
607}
608
609static int
610CheckFloatTag(TIFF* tif1, TIFF* tif2, int tag, char* name)
611{
612	float v1, v2;
613	CHECK(v1 == v2, "%s: %g %g\n");
614}
615
616static int
617CheckStringTag(TIFF* tif1, TIFF* tif2, int tag, char* name)
618{
619	char *v1, *v2;
620	CHECK(strcmp(v1, v2) == 0, "%s: \"%s\" \"%s\"\n");
621}
622
623static void
624leof(const char* name, uint32 row, int s)
625{
626
627	printf("%s: EOF at scanline %lu", name, (unsigned long)row);
628	if (s >= 0)
629		printf(", sample %d", s);
630	printf("\n");
631}
632
633/* vim: set ts=8 sts=8 sw=8 noet: */
634/*
635 * Local Variables:
636 * mode: c
637 * c-basic-offset: 8
638 * fill-column: 78
639 * End:
640 */
641