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