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