xref: /libtiff-4.0.7/tools/tiffcmp.c (revision 7d5139e9)
1 /* $Id: tiffcmp.c,v 1.13 2006-06-08 11:52:27 dron Exp $ */
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
41 extern int getopt(int, char**, char*);
42 #endif
43 
44 static	int stopondiff = 1;
45 static	int stoponfirsttag = 1;
46 static	uint16 bitspersample = 1;
47 static	uint16 samplesperpixel = 1;
48 static	uint16 sampleformat = SAMPLEFORMAT_UINT;
49 static	uint32 imagewidth;
50 static	uint32 imagelength;
51 
52 static	void usage(void);
53 static	int tiffcmp(TIFF*, TIFF*);
54 static	int cmptags(TIFF*, TIFF*);
55 static	int ContigCompare(int, uint32, unsigned char*, unsigned char*, int);
56 static	int SeparateCompare(int, int, uint32, unsigned char*, unsigned char*);
57 static	void PrintIntDiff(uint32, int, uint32, uint32, uint32);
58 static	void PrintFloatDiff(uint32, int, uint32, double, double);
59 
60 static	void leof(const char*, uint32, int);
61 
62 int
63 main(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 
114 char* 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",
120 NULL
121 };
122 
123 static void
124 usage(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 
141 static	int CheckShortTag(TIFF*, TIFF*, int, char*);
142 static	int CheckShort2Tag(TIFF*, TIFF*, int, char*);
143 static	int CheckShortArrayTag(TIFF*, TIFF*, int, char*);
144 static	int CheckLongTag(TIFF*, TIFF*, int, char*);
145 static	int CheckFloatTag(TIFF*, TIFF*, int, char*);
146 static	int CheckStringTag(TIFF*, TIFF*, int, char*);
147 
148 static int
149 tiffcmp(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);
234 bad:
235 	if (stopondiff)
236 		exit(1);
237 bad1:
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 
256 static int
257 cmptags(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 
310 static int
311 ContigCompare(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 
410 static void
411 PrintIntDiff(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 
467 static void
468 PrintFloatDiff(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 
484 static int
485 SeparateCompare(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 
508 static int
509 checkTag(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 
535 static int
536 CheckShortTag(TIFF* tif1, TIFF* tif2, int tag, char* name)
537 {
538 	uint16 v1, v2;
539 	CHECK(v1 == v2, "%s: %u %u\n");
540 }
541 
542 static int
543 CheckShort2Tag(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 
563 static int
564 CheckShortArrayTag(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 
602 static int
603 CheckLongTag(TIFF* tif1, TIFF* tif2, int tag, char* name)
604 {
605 	uint32 v1, v2;
606 	CHECK(v1 == v2, "%s: %u %u\n");
607 }
608 
609 static int
610 CheckFloatTag(TIFF* tif1, TIFF* tif2, int tag, char* name)
611 {
612 	float v1, v2;
613 	CHECK(v1 == v2, "%s: %g %g\n");
614 }
615 
616 static int
617 CheckStringTag(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 
623 static void
624 leof(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