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