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