1 /*****************************************************************************
2 
3 giftext - dump GIF pixels and metadata as text
4 
5 SPDX-License-Identifier: MIT
6 
7 *****************************************************************************/
8 
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <ctype.h>
12 #include <fcntl.h>
13 #include <stdbool.h>
14 
15 #ifdef _WIN32
16 #include <io.h>
17 #endif /* _WIN32 */
18 
19 #include "gif_lib.h"
20 #include "getarg.h"
21 
22 #define PROGRAM_NAME	"giftext"
23 
24 #define MAKE_PRINTABLE(c)  (isprint(c) ? (c) : ' ')
25 
26 static char
27     *VersionStr =
28 	PROGRAM_NAME
29 	VERSION_COOKIE
30 	"	Gershon Elber,	"
31 	__DATE__ ",   " __TIME__ "\n"
32 	"(C) Copyright 1989 Gershon Elber.\n";
33 static char
34     *CtrlStr =
35 	PROGRAM_NAME
36 	" v%- c%- e%- z%- p%- r%- h%- GifFile!*s";
37 
38 static void PrintCodeBlock(GifFileType *GifFile, GifByteType *CodeBlock, bool Reset);
39 static void PrintPixelBlock(GifByteType *PixelBlock, int Len, bool Reset);
40 static void PrintExtBlock(GifByteType *Extension, bool Reset);
41 static void PrintLZCodes(GifFileType *GifFile);
42 
43 /******************************************************************************
44  Interpret the command line and scan the given GIF file.
45 ******************************************************************************/
main(int argc,char ** argv)46 int main(int argc, char **argv)
47 {
48     int i, j, ExtCode, ErrorCode, CodeSize, NumFiles, Len, ImageNum = 1;
49     bool Error,
50 	ColorMapFlag = false, EncodedFlag = false, LZCodesFlag = false,
51 	PixelFlag = false, HelpFlag = false, RawFlag = false;
52     char *GifFileName, **FileName = NULL;
53     GifPixelType *Line;
54     GifRecordType RecordType;
55     GifByteType *CodeBlock, *Extension;
56     GifFileType *GifFile;
57 
58     if ((Error = GAGetArgs(argc, argv, CtrlStr,
59 		&GifNoisyPrint, &ColorMapFlag, &EncodedFlag,
60 		&LZCodesFlag, &PixelFlag, &RawFlag, &HelpFlag,
61 		&NumFiles, &FileName)) != false ||
62 	(NumFiles > 1 && !HelpFlag)) {
63 	if (Error)
64 	    GAPrintErrMsg(Error);
65 	else if (NumFiles > 1)
66 	    GIF_MESSAGE("Error in command line parsing - one GIF file please.");
67 	GAPrintHowTo(CtrlStr);
68 	exit(EXIT_FAILURE);
69     }
70 
71     if (HelpFlag) {
72 	(void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR);
73 	GAPrintHowTo(CtrlStr);
74 	exit(EXIT_SUCCESS);
75     }
76 
77     if (NumFiles == 1) {
78 	GifFileName = *FileName;
79 	if ((GifFile = DGifOpenFileName(*FileName, &ErrorCode)) == NULL) {
80 	    PrintGifError(ErrorCode);
81 	    exit(EXIT_FAILURE);
82 	}
83     }
84     else {
85 	/* Use stdin instead: */
86 	GifFileName = "Stdin";
87 	if ((GifFile = DGifOpenFileHandle(0, &ErrorCode)) == NULL) {
88 	    PrintGifError(ErrorCode);
89 	    exit(EXIT_FAILURE);
90 	}
91     }
92 
93     /* Because we write binary data - make sure no text will be written. */
94     if (RawFlag) {
95 	ColorMapFlag = EncodedFlag = LZCodesFlag = PixelFlag = false;
96 #ifdef _WIN32
97 	_setmode(1, O_BINARY);             /* Make sure it is in binary mode. */
98 #endif /* _WIN32 */
99     }
100     else {
101 	printf("\n%s:\n\n\tScreen Size - Width = %d, Height = %d.\n",
102 	       GifFileName, GifFile->SWidth, GifFile->SHeight);
103 	printf("\tColorResolution = %d, BitsPerPixel = %d, BackGround = %d, Aspect = %d.\n",
104 	       GifFile->SColorResolution,
105 	       GifFile->SColorMap ? GifFile->SColorMap->BitsPerPixel : 0,
106 	       GifFile->SBackGroundColor,
107 	       GifFile->AspectByte);
108 	if (GifFile->SColorMap)
109 	    printf("\tHas Global Color Map.\n\n");
110 	else
111 	    printf("\tNo Global Color Map.\n\n");
112 	if (ColorMapFlag && GifFile->SColorMap) {
113 	    printf("\tGlobal Color Map:\n");
114 	    Len = GifFile->SColorMap->ColorCount;
115 	    printf("\tSort Flag: %s\n",
116 		   GifFile->SColorMap->SortFlag ? "on":"off");
117 	    for (i = 0; i < Len; i+=4) {
118 		for (j = 0; j < 4 && j < Len; j++) {
119 		    printf("%3d: %02xh %02xh %02xh   ", i + j,
120 			   GifFile->SColorMap->Colors[i + j].Red,
121 			   GifFile->SColorMap->Colors[i + j].Green,
122 			   GifFile->SColorMap->Colors[i + j].Blue);
123 		}
124 		printf("\n");
125 	    }
126 	}
127     }
128 
129     do {
130 	if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
131 	    PrintGifError(GifFile->Error);
132 	    exit(EXIT_FAILURE);
133 	}
134 	switch (RecordType) {
135 	    case IMAGE_DESC_RECORD_TYPE:
136 		if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
137 		    PrintGifError(GifFile->Error);
138 		    exit(EXIT_FAILURE);
139 		}
140 		if (!RawFlag) {
141 		    printf("\nImage #%d:\n\n\tImage Size - Left = %d, Top = %d, Width = %d, Height = %d.\n",
142 			   ImageNum++, GifFile->Image.Left, GifFile->Image.Top,
143 			   GifFile->Image.Width, GifFile->Image.Height);
144 		    printf("\tImage is %s",
145 			   GifFile->Image.Interlace ? "Interlaced" :
146 						    "Non Interlaced");
147 		    if (GifFile->Image.ColorMap != NULL)
148 			printf(", BitsPerPixel = %d.\n",
149 				GifFile->Image.ColorMap->BitsPerPixel);
150 		    else
151 			printf(".\n");
152 		    if (GifFile->Image.ColorMap)
153 			printf("\tImage Has Color Map.\n");
154 		    else
155 			printf("\tNo Image Color Map.\n");
156 		    if (ColorMapFlag && GifFile->Image.ColorMap) {
157 			printf("\tSort Flag: %s\n",
158 			       GifFile->Image.ColorMap->SortFlag ? "on":"off");
159 			Len = 1 << GifFile->Image.ColorMap->BitsPerPixel;
160 			for (i = 0; i < Len; i+=4) {
161 			    for (j = 0; j < 4 && j < Len; j++) {
162 				printf("%3d: %02xh %02xh %02xh   ", i + j,
163 				       GifFile->Image.ColorMap->Colors[i + j].Red,
164 				       GifFile->Image.ColorMap->Colors[i + j].Green,
165 				       GifFile->Image.ColorMap->Colors[i + j].Blue);
166 			    }
167 			    printf("\n");
168 			}
169 		    }
170 		}
171 
172 		if (EncodedFlag) {
173 		    if (DGifGetCode(GifFile, &CodeSize, &CodeBlock) == GIF_ERROR) {
174 			PrintGifError(GifFile->Error);
175 			exit(EXIT_FAILURE);
176 		    }
177 		    printf("\nImage LZ compressed Codes (Code Size = %d):\n",
178 			   CodeSize);
179 		    PrintCodeBlock(GifFile, CodeBlock, true);
180 		    while (CodeBlock != NULL) {
181 			if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR) {
182 			    PrintGifError(GifFile->Error);
183 			    exit(EXIT_FAILURE);
184 			}
185 			PrintCodeBlock(GifFile, CodeBlock, false);
186 		    }
187 		}
188 		else if (LZCodesFlag) {
189 		    PrintLZCodes(GifFile);
190 		}
191 		else if (PixelFlag) {
192 		    Line = (GifPixelType *) malloc(GifFile->Image.Width *
193 						sizeof(GifPixelType));
194 		    for (i = 0; i < GifFile->Image.Height; i++) {
195 			if (DGifGetLine(GifFile, Line, GifFile->Image.Width)
196 			    == GIF_ERROR) {
197 			    PrintGifError(GifFile->Error);
198 			    exit(EXIT_FAILURE);
199 			}
200 			PrintPixelBlock(Line, GifFile->Image.Width, i == 0);
201 		    }
202 		    PrintPixelBlock(NULL, GifFile->Image.Width, false);
203 		    free((char *) Line);
204 		}
205 		else if (RawFlag) {
206 		    Line = (GifPixelType *) malloc(GifFile->Image.Width *
207 						sizeof(GifPixelType));
208 		    for (i = 0; i < GifFile->Image.Height; i++) {
209 			if (DGifGetLine(GifFile, Line, GifFile->Image.Width)
210 			    == GIF_ERROR) {
211 			    PrintGifError(GifFile->Error);
212 			    exit(EXIT_FAILURE);
213 			}
214 			fwrite(Line, 1, GifFile->Image.Width, stdout);
215 		    }
216 		    free((char *) Line);
217 		}
218 		else {
219 		    /* Skip the image: */
220 		    if (DGifGetCode(GifFile, &CodeSize, &CodeBlock) == GIF_ERROR) {
221 			PrintGifError(GifFile->Error);
222 			exit(EXIT_FAILURE);
223 		    }
224 		    while (CodeBlock != NULL) {
225 			if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR) {
226 			    PrintGifError(GifFile->Error);
227 			    exit(EXIT_FAILURE);
228 			}
229 		    }
230 
231 		}
232 		break;
233 	    case EXTENSION_RECORD_TYPE:
234 		if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
235 		    PrintGifError(GifFile->Error);
236 		    exit(EXIT_FAILURE);
237 		}
238 		if (!RawFlag) {
239 		    putchar('\n');
240 		    switch (ExtCode)
241 		    {
242 		    case COMMENT_EXT_FUNC_CODE:
243 			printf("GIF89 comment");
244 			break;
245 		    case GRAPHICS_EXT_FUNC_CODE:
246 			printf("GIF89 graphics control");
247 			break;
248 		    case PLAINTEXT_EXT_FUNC_CODE:
249 			printf("GIF89 plaintext");
250 			break;
251 		    case APPLICATION_EXT_FUNC_CODE:
252 			printf("GIF89 application block");
253 			break;
254 		    default:
255 			printf("Extension record of unknown type");
256 			break;
257 		    }
258 		    printf(" (Ext Code = %d [%c]):\n",
259 			   ExtCode, MAKE_PRINTABLE(ExtCode));
260 		    PrintExtBlock(Extension, true);
261 
262 		    if (ExtCode == GRAPHICS_EXT_FUNC_CODE) {
263 			GraphicsControlBlock gcb;
264 			if (Extension == NULL) {
265 			    printf("Invalid extension block\n");
266 			    GifFile->Error = D_GIF_ERR_IMAGE_DEFECT;
267 			    PrintGifError(GifFile->Error);
268 			    exit(EXIT_FAILURE);
269 			}
270 			if (DGifExtensionToGCB(Extension[0], Extension+1, &gcb) == GIF_ERROR) {
271 			    PrintGifError(GifFile->Error);
272 			    exit(EXIT_FAILURE);
273 			}
274 			printf("\tDisposal Mode: %d\n", gcb.DisposalMode);
275 			printf("\tUser Input Flag: %d\n", gcb.UserInputFlag);
276 			printf("\tTransparency on: %s\n",
277 			       gcb.TransparentColor != -1 ? "yes" : "no");
278 			printf("\tDelayTime: %d\n", gcb.DelayTime);
279 			printf("\tTransparent Index: %d\n", gcb.TransparentColor);
280 		    }
281 		}
282 		for (;;) {
283 		    if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
284 			PrintGifError(GifFile->Error);
285 			exit(EXIT_FAILURE);
286 		    }
287 		    if (Extension == NULL)
288 			break;
289 		    PrintExtBlock(Extension, false);
290 		}
291 		break;
292 	    case TERMINATE_RECORD_TYPE:
293 		break;
294 	    default:	     /* Should be trapped by DGifGetRecordType */
295 		break;
296 	}
297     }
298     while (RecordType != TERMINATE_RECORD_TYPE);
299 
300     if (DGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR) {
301 	PrintGifError(ErrorCode);
302 	exit(EXIT_FAILURE);
303     }
304 
305     if (!RawFlag) printf("\nGIF file terminated normally.\n");
306 
307     return 0;
308 }
309 
310 /******************************************************************************
311  Print the given CodeBlock - a string in pascal notation (size in first
312  place). Save local information so printing can be performed continuously,
313  or reset to start state if Reset. If CodeBlock is NULL, output is flushed
314 ******************************************************************************/
PrintCodeBlock(GifFileType * GifFile,GifByteType * CodeBlock,bool Reset)315 static void PrintCodeBlock(GifFileType *GifFile, GifByteType *CodeBlock, bool Reset)
316 {
317     static int CrntPlace = 0;
318     static long CodeCount = 0;
319     int i, Len;
320 
321     if (Reset || CodeBlock == NULL) {
322 	if (CodeBlock == NULL) {
323 	    long NumBytes = 0;
324 	    if (CrntPlace > 0) {
325 		printf("\n");
326 		CodeCount += CrntPlace - 16;
327 	    }
328 	    if (GifFile->Image.ColorMap)
329 		NumBytes = ((((long) GifFile->Image.Width) * GifFile->Image.Height)
330 				* GifFile->Image.ColorMap->BitsPerPixel) / 8;
331 	    else if (GifFile->SColorMap != NULL)
332 		NumBytes = ((((long) GifFile->Image.Width) * GifFile->Image.Height)
333 				* GifFile->SColorMap->BitsPerPixel) / 8;
334 	    /* FIXME: What should the compression ratio be if no color table? */
335 	    if (NumBytes > 0) {
336 		int Percent = 100 * CodeCount / NumBytes;
337 		printf("\nCompression ratio: %ld/%ld (%d%%).\n",
338 			CodeCount, NumBytes, Percent);
339 	    }
340 	    return;
341 	}
342 	CrntPlace = 0;
343 	CodeCount = 0;
344     }
345 
346     Len = CodeBlock[0];
347     for (i = 1; i <= Len; i++) {
348 	if (CrntPlace == 0) {
349 	    printf("\n%05lxh:  ", CodeCount);
350 	    CodeCount += 16;
351 	}
352 	(void)printf(" %02xh", CodeBlock[i]);
353 	if (++CrntPlace >= 16) CrntPlace = 0;
354     }
355 }
356 
357 /******************************************************************************
358  Print the given Extension - a string in pascal notation (size in first
359  place). Save local information so printing can be performed continuously,
360  or reset to start state if Reset. If Extension is NULL, output is flushed
361 ******************************************************************************/
PrintExtBlock(GifByteType * Extension,bool Reset)362 static void PrintExtBlock(GifByteType *Extension, bool Reset)
363 {
364     static int CrntPlace = 0;
365     static long ExtCount = 0;
366     static char HexForm[49], AsciiForm[17];
367 
368     if (Reset || Extension == NULL) {
369 	if (Extension == NULL) {
370 	    if (CrntPlace > 0) {
371 		HexForm[CrntPlace * 3] = 0;
372 		AsciiForm[CrntPlace] = 0;
373 		printf("\n%05lx: %-49s  %-17s\n", ExtCount, HexForm, AsciiForm);
374 		return;
375 	    }
376 	    else
377 		printf("\n");
378 	}
379 	CrntPlace = 0;
380 	ExtCount = 0;
381     }
382 
383     if (Extension != NULL) {
384 	int i, Len;
385 	Len = Extension[0];
386 	for (i = 1; i <= Len; i++) {
387 	    (void)snprintf(&HexForm[CrntPlace * 3], 3,
388 			   " %02x", Extension[i]);
389 	    (void)snprintf(&AsciiForm[CrntPlace], 3,
390 			   "%c", MAKE_PRINTABLE(Extension[i]));
391 	    if (++CrntPlace == 16) {
392 		HexForm[CrntPlace * 3] = 0;
393 		AsciiForm[CrntPlace] = 0;
394 		printf("\n%05lx: %-49s  %-17s", ExtCount, HexForm, AsciiForm);
395 		ExtCount += 16;
396 		CrntPlace = 0;
397 	    }
398 	}
399     }
400 }
401 
402 /******************************************************************************
403  Print the given PixelBlock of length Len.
404  Save local information so printing can be performed continuously,
405  or reset to start state if Reset. If PixelBlock is NULL, output is flushed
406 ******************************************************************************/
PrintPixelBlock(GifByteType * PixelBlock,int Len,bool Reset)407 static void PrintPixelBlock(GifByteType *PixelBlock, int Len, bool Reset)
408 {
409     static int CrntPlace = 0;
410     static long ExtCount = 0;
411     static char HexForm[49], AsciiForm[17];
412     int i;
413 
414     if (Reset || PixelBlock == NULL) {
415 	if (PixelBlock == NULL) {
416 	    if (CrntPlace > 0) {
417 		HexForm[CrntPlace * 3] = 0;
418 		AsciiForm[CrntPlace] = 0;
419 		printf("\n%05lx: %-49s  %-17s\n", ExtCount, HexForm, AsciiForm);
420 	    }
421 	    else
422 		printf("\n");
423 	}
424 	CrntPlace = 0;
425 	ExtCount = 0;
426 	if (PixelBlock == NULL) return;
427     }
428 
429     for (i = 0; i < Len; i++) {
430 	(void)snprintf(&HexForm[CrntPlace * 3], 3,
431 		       " %02x", PixelBlock[i]);
432 	(void)snprintf(&AsciiForm[CrntPlace], 3,
433 		       "%c", MAKE_PRINTABLE(PixelBlock[i]));
434 	if (++CrntPlace == 16) {
435 	    HexForm[CrntPlace * 3] = 0;
436 	    AsciiForm[CrntPlace] = 0;
437 	    printf("\n%05lx: %-49s  %-17s", ExtCount, HexForm, AsciiForm);
438 	    ExtCount += 16;
439 	    CrntPlace = 0;
440 	}
441     }
442 }
443 
444 /******************************************************************************
445  Print the image as LZ codes (each 12bits), until EOF marker is reached.
446 ******************************************************************************/
PrintLZCodes(GifFileType * GifFile)447 static void PrintLZCodes(GifFileType *GifFile)
448 {
449     int Code, CrntPlace = 0;
450     long CodeCount = 0;
451 
452     do {
453 	if (CrntPlace == 0) printf("\n%05lx:", CodeCount);
454 	if (DGifGetLZCodes(GifFile, &Code) == GIF_ERROR) {
455 	    PrintGifError(GifFile->Error);
456 	    exit(EXIT_FAILURE);
457 	}
458 	if (Code >= 0)
459 	    printf(" %03x", Code);	      /* EOF Code is returned as -1. */
460 	CodeCount++;
461 	if (++CrntPlace >= 16) CrntPlace = 0;
462     }
463     while (Code >= 0);
464 }
465 
466 /* end */
467