1 /*****************************************************************************
2
3 gifbuild - dump GIF data in a textual format, or undump it to a GIF
4
5 SPDX-License-Identifier: MIT
6
7 *****************************************************************************/
8
9 #include <ctype.h>
10 #include <stdbool.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14
15 #include "getarg.h"
16 #include "gif_lib.h"
17
18 #define PROGRAM_NAME "gifbuild"
19
20 static char *VersionStr = PROGRAM_NAME VERSION_COOKIE
21 " Eric Raymond, " __DATE__ ", " __TIME__ "\n"
22 "(C) Copyright 1992 Eric Raymond.\n";
23 static char *CtrlStr =
24 PROGRAM_NAME " v%- d%- t%-Characters!s h%- GifFile(s)!*s";
25
26 static char KeyLetters[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO"
27 "PQRSTUVWXYZ!\"#$%&'()*+,-./:<=>?@[\\]^_`{|}~";
28 #define PRINTABLES (sizeof(KeyLetters) - 1)
29
30 static void Icon2Gif(char *FileName, FILE *txtin, bool, int fdout);
31 static void Gif2Icon(char *FileName, int fdin, int fdout, char NameTable[]);
32 static int EscapeString(char *cp, char *tp);
33
34 /******************************************************************************
35 Main sequence
36 ******************************************************************************/
main(int argc,char ** argv)37 int main(int argc, char **argv) {
38 int NumFiles;
39 bool Error, DisasmFlag = false, HelpFlag = false, TextLineFlag = false,
40 GifNoisyPrint = false;
41 char **FileNames = NULL;
42 char *TextLines[1];
43
44 if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, &DisasmFlag,
45 &TextLineFlag, &TextLines[0], &HelpFlag,
46 &NumFiles, &FileNames)) != false) {
47 GAPrintErrMsg(Error);
48 GAPrintHowTo(CtrlStr);
49 exit(EXIT_FAILURE);
50 }
51
52 if (HelpFlag) {
53 (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR);
54 GAPrintHowTo(CtrlStr);
55 exit(EXIT_SUCCESS);
56 }
57
58 if (!DisasmFlag && NumFiles > 1) {
59 GIF_MESSAGE(
60 "Error in command line parsing - one text input please.");
61 GAPrintHowTo(CtrlStr);
62 exit(EXIT_FAILURE);
63 }
64
65 if (!DisasmFlag && TextLineFlag) {
66 GIF_MESSAGE(
67 "Error in command line parsing - -t invalid without -d.");
68 GAPrintHowTo(CtrlStr);
69 exit(EXIT_FAILURE);
70 }
71
72 if (NumFiles == 0) {
73 if (DisasmFlag)
74 Gif2Icon("Stdin", 0, 1,
75 TextLineFlag ? TextLines[0] : KeyLetters);
76 else
77 Icon2Gif("Stdin", stdin, GifNoisyPrint, 1);
78 } else {
79 int i;
80 for (i = 0; i < NumFiles; i++) {
81 FILE *fp;
82
83 if ((fp = fopen(FileNames[i], "r")) == (FILE *)NULL) {
84 (void)fprintf(stderr, "Can't open %s\n",
85 FileNames[i]);
86 exit(EXIT_FAILURE);
87 }
88
89 if (DisasmFlag) {
90 printf("#\n# GIF information from %s\n",
91 FileNames[i]);
92 Gif2Icon(FileNames[i], -1, 1,
93 TextLineFlag ? TextLines[0]
94 : KeyLetters);
95 } else {
96 Icon2Gif(FileNames[i], fp, GifNoisyPrint, 1);
97 }
98
99 (void)fclose(fp);
100 }
101 }
102
103 return 0;
104 }
105
106 /******************************************************************************
107 Parse image directives
108 ******************************************************************************/
109 #define PARSE_ERROR(str) \
110 (void)fprintf(stderr, "%s:%d: %s\n", FileName, LineNum, str);
111
Icon2Gif(char * FileName,FILE * txtin,bool GifNoisyPrint,int fdout)112 static void Icon2Gif(char *FileName, FILE *txtin, bool GifNoisyPrint,
113 int fdout) {
114 unsigned int ColorMapSize = 0;
115 GifColorType GlobalColorMap[256], LocalColorMap[256],
116 *ColorMap = GlobalColorMap;
117 char GlobalColorKeys[PRINTABLES], LocalColorKeys[PRINTABLES],
118 *KeyTable = GlobalColorKeys;
119 bool SortFlag = false;
120 unsigned int ExtCode, intval;
121 int red, green, blue, n;
122 char buf[BUFSIZ * 2], InclusionFile[64];
123 GifFileType *GifFileOut;
124 SavedImage *NewImage = NULL;
125 int LeadingExtensionBlockCount = 0;
126 ExtensionBlock *LeadingExtensionBlocks = NULL;
127 int ErrorCode, LineNum = 0;
128
129 if ((GifFileOut = EGifOpenFileHandle(fdout, &ErrorCode)) == NULL) {
130 PrintGifError(ErrorCode);
131 exit(EXIT_FAILURE);
132 }
133
134 /* OK, interpret directives */
135 /* coverity[tainted_data_transitive] */
136 while (fgets(buf, sizeof(buf), txtin) != (char *)NULL) {
137 char *cp;
138
139 ++LineNum;
140
141 /*
142 * Skip lines consisting only of whitespace and comments
143 */
144 for (cp = buf; isspace((int)(*cp)); cp++) {
145 continue;
146 }
147 if (*cp == '#' || *cp == '\0') {
148 continue;
149 }
150
151 /*
152 * If there's a trailing comment, nuke it and all preceding
153 * whitespace. But preserve the EOL.
154 */
155 if ((cp = strchr(buf, '#')) && (cp == strrchr(cp, '#'))) {
156 while (isspace((int)(*--cp))) {
157 continue;
158 }
159 *++cp = '\n';
160 *++cp = '\0';
161 }
162
163 /*
164 * Explicit header declarations
165 */
166
167 if (sscanf(buf, "screen width %d\n", &GifFileOut->SWidth) ==
168 1) {
169 continue;
170 }
171
172 else if (sscanf(buf, "screen height %d\n",
173 &GifFileOut->SHeight) == 1) {
174 continue;
175 }
176
177 else if (sscanf(buf, "screen colors %d\n", &n) == 1) {
178 int ResBits = GifBitSize(n);
179
180 if (n > 256 || n < 0 || n != (1 << ResBits)) {
181 PARSE_ERROR("Invalid color resolution value.");
182 exit(EXIT_FAILURE);
183 }
184
185 GifFileOut->SColorResolution = ResBits;
186 continue;
187 }
188
189 else if (sscanf(buf, "screen background %d\n",
190 &GifFileOut->SBackGroundColor) == 1) {
191 continue;
192 }
193
194 else if (sscanf(buf, "pixel aspect byte %u\n", &intval) == 1) {
195 GifFileOut->AspectByte = (GifByteType)(intval & 0xff);
196 continue;
197 }
198
199 /*
200 * Color table parsing
201 */
202
203 else if (strcmp(buf, "screen map\n") == 0) {
204 if (GifFileOut->SColorMap != NULL) {
205 PARSE_ERROR("You've already declared a global "
206 "color map.");
207 exit(EXIT_FAILURE);
208 }
209
210 ColorMapSize = 0;
211 ColorMap = GlobalColorMap;
212 SortFlag = false;
213 KeyTable = GlobalColorKeys;
214 memset(GlobalColorKeys, '\0', sizeof(GlobalColorKeys));
215 }
216
217 else if (strcmp(buf, "image map\n") == 0) {
218 if (NewImage == NULL) {
219 PARSE_ERROR("No previous image declaration.");
220 exit(EXIT_FAILURE);
221 }
222
223 ColorMapSize = 0;
224 ColorMap = LocalColorMap;
225 KeyTable = LocalColorKeys;
226 memset(LocalColorKeys, '\0', sizeof(LocalColorKeys));
227 }
228
229 else if (sscanf(buf, " rgb %d %d %d is %c", &red, &green,
230 &blue, &KeyTable[ColorMapSize]) == 4) {
231 if (ColorMapSize >= 256) {
232 PARSE_ERROR("Too many color entries.");
233 exit(EXIT_FAILURE);
234 }
235 ColorMap[ColorMapSize].Red = red;
236 ColorMap[ColorMapSize].Green = green;
237 ColorMap[ColorMapSize].Blue = blue;
238 ColorMapSize++;
239 }
240
241 else if (sscanf(buf, " rgb %d %d %d", &red, &green, &blue) ==
242 3) {
243 if (ColorMapSize >= 256) {
244 PARSE_ERROR("Too many color entries.");
245 exit(EXIT_FAILURE);
246 }
247 ColorMap[ColorMapSize].Red = red;
248 ColorMap[ColorMapSize].Green = green;
249 ColorMap[ColorMapSize].Blue = blue;
250 ColorMapSize++;
251 }
252
253 else if (strcmp(buf, " sort flag on\n") == 0) {
254 SortFlag = true;
255 }
256
257 else if (strcmp(buf, " sort flag off\n") == 0) {
258 SortFlag = false;
259 }
260
261 else if (strcmp(buf, "end\n") == 0) {
262 ColorMapObject *NewMap;
263
264 NewMap = GifMakeMapObject(1 << GifBitSize(ColorMapSize),
265 ColorMap);
266 if (NewMap == (ColorMapObject *)NULL) {
267 PARSE_ERROR("Out of memory while allocating "
268 "new color map.");
269 exit(EXIT_FAILURE);
270 }
271
272 NewMap->SortFlag = SortFlag;
273
274 if (NewImage) {
275 NewImage->ImageDesc.ColorMap = NewMap;
276 } else {
277 GifFileOut->SColorMap = NewMap;
278 }
279 }
280
281 /* GIF inclusion */
282 /* ugly magic number is because scanf has no */
283 else if (sscanf(buf, "include %63s", InclusionFile) == 1) {
284 bool DoTranslation;
285 GifPixelType Translation[256];
286
287 GifFileType *Inclusion;
288 SavedImage *CopyFrom;
289
290 if ((Inclusion = DGifOpenFileName(
291 InclusionFile, &ErrorCode)) == NULL) {
292 PrintGifError(ErrorCode);
293 exit(EXIT_FAILURE);
294 }
295
296 if (DGifSlurp(Inclusion) == GIF_ERROR) {
297 PARSE_ERROR("Inclusion read failed.");
298 // cppcheck-suppress knownConditionTrueFalse
299 if (Inclusion != NULL) {
300 PrintGifError(Inclusion->Error);
301 DGifCloseFile(Inclusion, NULL);
302 }
303 if (GifFileOut != NULL) {
304 EGifCloseFile(GifFileOut, NULL);
305 };
306 exit(EXIT_FAILURE);
307 }
308
309 // cppcheck-suppress nullPointerRedundantCheck
310 if ((DoTranslation = (GifFileOut->SColorMap !=
311 (ColorMapObject *)NULL))) {
312 ColorMapObject *UnionMap;
313
314 UnionMap = GifUnionColorMap(
315 // cppcheck-suppress nullPointerRedundantCheck
316 GifFileOut->SColorMap, Inclusion->SColorMap,
317 Translation);
318
319 if (UnionMap == NULL) {
320 PARSE_ERROR("Inclusion failed --- "
321 "global map conflict.");
322 // cppcheck-suppress nullPointerRedundantCheck
323 PrintGifError(GifFileOut->Error);
324 // cppcheck-suppress knownConditionTrueFalse
325 if (Inclusion != NULL) {
326 DGifCloseFile(Inclusion, NULL);
327 }
328 if (GifFileOut != NULL) {
329 EGifCloseFile(GifFileOut, NULL);
330 }
331 exit(EXIT_FAILURE);
332 }
333
334 GifFreeMapObject(GifFileOut->SColorMap);
335 GifFileOut->SColorMap = UnionMap;
336 }
337
338 for (CopyFrom = Inclusion->SavedImages;
339 CopyFrom <
340 Inclusion->SavedImages + Inclusion->ImageCount;
341 CopyFrom++) {
342 if ((NewImage = GifMakeSavedImage(
343 GifFileOut, CopyFrom)) == NULL) {
344 PARSE_ERROR("Inclusion failed --- out "
345 "of memory.");
346 // cppcheck-suppress nullPointerRedundantCheck
347 PrintGifError(GifFileOut->Error);
348 // cppcheck-suppress knownConditionTrueFalse
349 if (Inclusion != NULL) {
350 DGifCloseFile(Inclusion, NULL);
351 }
352 if (GifFileOut != NULL) {
353 EGifCloseFile(GifFileOut, NULL);
354 }
355 exit(EXIT_FAILURE);
356 } else if (DoTranslation) {
357 GifApplyTranslation(NewImage,
358 Translation);
359 }
360
361 GifQprintf("%s: Image %d at (%d, %d) [%dx%d]: "
362 "from %s\n",
363 PROGRAM_NAME, GifFileOut->ImageCount,
364 NewImage->ImageDesc.Left,
365 NewImage->ImageDesc.Top,
366 NewImage->ImageDesc.Width,
367 NewImage->ImageDesc.Height,
368 InclusionFile);
369 }
370
371 (void)DGifCloseFile(Inclusion, NULL);
372 }
373
374 /*
375 * Extension blocks.
376 */
377 else if (strcmp(buf, "comment\n") == 0) {
378 int bc = 0;
379 while (fgets(buf, sizeof(buf), txtin) != (char *)NULL) {
380 if (strcmp(buf, "end\n") == 0) {
381 break;
382 } else {
383 int Len;
384
385 buf[strlen(buf) - 1] = '\0';
386 Len = EscapeString(buf, buf);
387 if (GifAddExtensionBlock(
388 &LeadingExtensionBlockCount,
389 &LeadingExtensionBlocks,
390 bc++ == CONTINUE_EXT_FUNC_CODE
391 ? COMMENT_EXT_FUNC_CODE
392 : 0,
393 Len, (unsigned char *)buf) ==
394 GIF_ERROR) {
395 PARSE_ERROR(
396 "out of memory while "
397 "adding comment block.");
398 exit(EXIT_FAILURE);
399 }
400 }
401 }
402 } else if (strcmp(buf, "plaintext\n") == 0) {
403 int bc = 0;
404 while (fgets(buf, sizeof(buf), txtin) != (char *)NULL) {
405 if (strcmp(buf, "end\n") == 0) {
406 break;
407 } else {
408 int Len;
409
410 buf[strlen(buf) - 1] = '\0';
411 Len = EscapeString(buf, buf);
412 if (GifAddExtensionBlock(
413 &LeadingExtensionBlockCount,
414 &LeadingExtensionBlocks,
415 bc++ == CONTINUE_EXT_FUNC_CODE
416 ? PLAINTEXT_EXT_FUNC_CODE
417 : 0,
418 Len, (unsigned char *)buf) ==
419 GIF_ERROR) {
420 PARSE_ERROR(
421 "out of memory while "
422 "adding plaintext block.");
423 exit(EXIT_FAILURE);
424 }
425 }
426 }
427 } else if (strcmp(buf, "graphics control\n") == 0) {
428 GraphicsControlBlock gcb;
429 size_t Len;
430
431 memset(&gcb, '\0', sizeof(gcb));
432 gcb.TransparentColor = NO_TRANSPARENT_COLOR;
433 while (fgets(buf, sizeof(buf), txtin) != (char *)NULL) {
434 if (strcmp(buf, "end\n") == 0) {
435 break;
436 } else {
437 char *tp = buf;
438
439 while (isspace(*tp)) {
440 tp++;
441 }
442 if (sscanf(tp, "disposal mode %d\n",
443 &gcb.DisposalMode)) {
444 continue;
445 }
446 if (strcmp(tp,
447 "user input flag on\n") ==
448 0) {
449 gcb.UserInputFlag = true;
450 continue;
451 }
452 if (strcmp(tp,
453 "user input flag off\n") ==
454 0) {
455 gcb.UserInputFlag = false;
456 continue;
457 }
458 if (sscanf(tp, "delay %d\n",
459 &gcb.DelayTime)) {
460 continue;
461 }
462 if (sscanf(tp, "transparent index %d\n",
463 &gcb.TransparentColor)) {
464 continue;
465 }
466 (void)fputs(tp, stderr);
467 PARSE_ERROR("unrecognized directive in "
468 "GCB block.");
469 exit(EXIT_FAILURE);
470 }
471 }
472 Len = EGifGCBToExtension(&gcb, (GifByteType *)buf);
473 if (GifAddExtensionBlock(
474 &LeadingExtensionBlockCount,
475 &LeadingExtensionBlocks, GRAPHICS_EXT_FUNC_CODE,
476 Len, (unsigned char *)buf) == GIF_ERROR) {
477 PARSE_ERROR("out of memory while adding GCB.");
478 exit(EXIT_FAILURE);
479 }
480
481 } else if (sscanf(buf, "netscape loop %u", &intval)) {
482 unsigned char params[3] = {1, 0, 0};
483 /* Create a Netscape 2.0 loop block */
484 if (GifAddExtensionBlock(
485 &LeadingExtensionBlockCount,
486 &LeadingExtensionBlocks,
487 APPLICATION_EXT_FUNC_CODE, 11,
488 (unsigned char *)"NETSCAPE2.0") == GIF_ERROR) {
489 PARSE_ERROR(
490 "out of memory while adding loop block.");
491 exit(EXIT_FAILURE);
492 }
493 params[1] = (intval & 0xff);
494 params[2] = (intval >> 8) & 0xff;
495 if (GifAddExtensionBlock(&LeadingExtensionBlockCount,
496 &LeadingExtensionBlocks, 0,
497 sizeof(params),
498 params) == GIF_ERROR) {
499 PARSE_ERROR("out of memory while adding loop "
500 "continuation.");
501 exit(EXIT_FAILURE);
502 }
503
504 } else if (sscanf(buf, "extension %x", &ExtCode)) {
505 int bc = 0;
506 while (fgets(buf, sizeof(buf), txtin) != (char *)NULL) {
507 if (strcmp(buf, "end\n") == 0) {
508 break;
509 } else {
510 int Len;
511
512 buf[strlen(buf) - 1] = '\0';
513 Len = EscapeString(buf, buf);
514 if (GifAddExtensionBlock(
515 &LeadingExtensionBlockCount,
516 &LeadingExtensionBlocks,
517 bc++ == CONTINUE_EXT_FUNC_CODE
518 ? ExtCode
519 : 0,
520 Len, (unsigned char *)buf) ==
521 GIF_ERROR) {
522 PARSE_ERROR(
523 "out of memory while "
524 "adding extension block.");
525 exit(EXIT_FAILURE);
526 }
527 }
528 }
529 }
530
531 /*
532 * Explicit image declarations
533 */
534
535 else if (strcmp(buf, "image\n") == 0) {
536 if ((NewImage = GifMakeSavedImage(GifFileOut, NULL)) ==
537 (SavedImage *)NULL) {
538 PARSE_ERROR("Out of memory while allocating "
539 "image block.");
540 exit(EXIT_FAILURE);
541 }
542
543 /* use global table unless user specifies a local one */
544 ColorMap = GlobalColorMap;
545 KeyTable = GlobalColorKeys;
546
547 /* connect leading extension blocks */
548 NewImage->ExtensionBlockCount =
549 LeadingExtensionBlockCount;
550 NewImage->ExtensionBlocks = LeadingExtensionBlocks;
551 LeadingExtensionBlockCount = 0;
552 LeadingExtensionBlocks = NULL;
553 }
554
555 /*
556 * Nothing past this point is valid unless we've seen a previous
557 * image declaration.
558 */
559 else if (NewImage == (SavedImage *)NULL) {
560 (void)fputs(buf, stderr);
561 PARSE_ERROR("Syntax error in header block.");
562 exit(EXIT_FAILURE);
563 }
564
565 /*
566 * Accept image attributes
567 */
568 else if (sscanf(buf, "image top %d\n",
569 &NewImage->ImageDesc.Top) == 1) {
570 continue;
571 }
572
573 else if (sscanf(buf, "image left %d\n",
574 &NewImage->ImageDesc.Left) == 1) {
575 continue;
576 }
577
578 else if (strcmp(buf, "image interlaced\n") == 0) {
579 NewImage->ImageDesc.Interlace = true;
580 continue;
581 }
582
583 else if (sscanf(buf, "image bits %d by %d",
584 &NewImage->ImageDesc.Width,
585 &NewImage->ImageDesc.Height) == 2) {
586 int i, j;
587 static GifPixelType *Raster;
588 int c;
589 bool hex = (strstr(buf, "hex") != NULL);
590
591 /* coverity[overflow_sink] */
592 if ((Raster = (GifPixelType *)malloc(
593 sizeof(GifPixelType) *
594 NewImage->ImageDesc.Width *
595 NewImage->ImageDesc.Height)) == NULL) {
596 PARSE_ERROR("Failed to allocate raster block, "
597 "aborted.");
598 exit(EXIT_FAILURE);
599 }
600
601 GifQprintf("%s: Image %d at (%d, %d) [%dx%d]: ",
602 PROGRAM_NAME, GifFileOut->ImageCount,
603 NewImage->ImageDesc.Left,
604 NewImage->ImageDesc.Top,
605 NewImage->ImageDesc.Width,
606 NewImage->ImageDesc.Height);
607
608 GifByteType *tp = Raster;
609 for (i = 0; i < NewImage->ImageDesc.Height; i++) {
610
611 char *dp;
612
613 for (j = 0; j < NewImage->ImageDesc.Width;
614 j++) {
615 if ((c = fgetc(txtin)) == EOF) {
616 PARSE_ERROR("input file ended "
617 "prematurely.");
618 exit(EXIT_FAILURE);
619 } else if (c == '\n') {
620 --j;
621 ++LineNum;
622 } else if (isspace(c)) {
623 --j;
624 } else if (hex) {
625 const static char *hexdigits =
626 "0123456789ABCDEF";
627 unsigned char hi, lo;
628 dp = strchr(hexdigits,
629 toupper(c));
630 if (dp == NULL) {
631 PARSE_ERROR(
632 "Invalid hex high "
633 "byte.");
634 exit(EXIT_FAILURE);
635 }
636 hi = (dp - hexdigits);
637 if ((c = fgetc(txtin)) == EOF) {
638 PARSE_ERROR(
639 "input file ended "
640 "prematurely.");
641 exit(EXIT_FAILURE);
642 }
643 dp = strchr(hexdigits,
644 toupper(c));
645 if (dp == NULL) {
646 PARSE_ERROR(
647 "Invalid hex low "
648 "byte.");
649 exit(EXIT_FAILURE);
650 }
651 lo = (dp - hexdigits);
652 *tp++ = (hi << 4) | lo;
653 } else if ((dp = strchr(KeyTable, c))) {
654 *tp++ = (dp - KeyTable);
655 } else {
656 PARSE_ERROR(
657 "Invalid ASCII pixel key.");
658 exit(EXIT_FAILURE);
659 }
660 }
661
662 if (GifNoisyPrint) {
663 fprintf(stderr, "\b\b\b\b%-4d", i);
664 }
665 }
666
667 if (GifNoisyPrint) {
668 putc('\n', stderr);
669 }
670
671 NewImage->RasterBits = (unsigned char *)Raster;
672 } else {
673 (void)fputs(buf, stderr);
674 PARSE_ERROR("Syntax error in image description.");
675 exit(EXIT_FAILURE);
676 }
677 }
678
679 /* connect trailing extension blocks */
680 GifFileOut->ExtensionBlockCount = LeadingExtensionBlockCount;
681 GifFileOut->ExtensionBlocks = LeadingExtensionBlocks;
682 // LeadingExtensionBlockCount = 0;
683 LeadingExtensionBlocks = NULL;
684
685 EGifSpew(GifFileOut);
686 }
687
VisibleDumpBuffer(GifByteType * buf,int len)688 static void VisibleDumpBuffer(GifByteType *buf, int len)
689 /* Visibilize a given string */
690 {
691 GifByteType *cp;
692
693 for (cp = buf; cp < buf + len; cp++) {
694 if (isprint((int)(*cp)) || *cp == ' ') {
695 putchar(*cp);
696 } else if (*cp == '\n') {
697 putchar('\\');
698 putchar('n');
699 } else if (*cp == '\r') {
700 putchar('\\');
701 putchar('r');
702 } else if (*cp == '\b') {
703 putchar('\\');
704 putchar('b');
705 } else if (*cp < ' ') {
706 putchar('\\');
707 putchar('^');
708 putchar('@' + *cp);
709 } else {
710 printf("\\0x%02x", *cp);
711 }
712 }
713 }
714
DumpExtensions(GifFileType * GifFileOut,int ExtensionBlockCount,ExtensionBlock * ExtensionBlocks)715 static void DumpExtensions(GifFileType *GifFileOut, int ExtensionBlockCount,
716 ExtensionBlock *ExtensionBlocks) {
717 ExtensionBlock *ep;
718
719 for (ep = ExtensionBlocks; ep < ExtensionBlocks + ExtensionBlockCount;
720 ep++) {
721 bool last = (ep - ExtensionBlocks == (ExtensionBlockCount - 1));
722 if (ep->Function == COMMENT_EXT_FUNC_CODE) {
723 printf("comment\n");
724 VisibleDumpBuffer(ep->Bytes, ep->ByteCount);
725 putchar('\n');
726 while (!last &&
727 ep[1].Function == CONTINUE_EXT_FUNC_CODE) {
728 ++ep;
729 last = (ep - ExtensionBlocks ==
730 (ExtensionBlockCount - 1));
731 VisibleDumpBuffer(ep->Bytes, ep->ByteCount);
732 putchar('\n');
733 }
734 printf("end\n\n");
735 } else if (ep->Function == PLAINTEXT_EXT_FUNC_CODE) {
736 printf("plaintext\n");
737 VisibleDumpBuffer(ep->Bytes, ep->ByteCount);
738 putchar('\n');
739 while (!last &&
740 ep[1].Function == CONTINUE_EXT_FUNC_CODE) {
741 ++ep;
742 last = (ep - ExtensionBlocks ==
743 (ExtensionBlockCount - 1));
744 VisibleDumpBuffer(ep->Bytes, ep->ByteCount);
745 putchar('\n');
746 }
747 printf("end\n\n");
748 } else if (ep->Function == GRAPHICS_EXT_FUNC_CODE) {
749 GraphicsControlBlock gcb;
750 printf("graphics control\n");
751 if (DGifExtensionToGCB(ep->ByteCount, ep->Bytes,
752 &gcb) == GIF_ERROR) {
753 GIF_MESSAGE("invalid graphics control block");
754 exit(EXIT_FAILURE);
755 }
756 printf("\tdisposal mode %d\n", gcb.DisposalMode);
757 printf("\tuser input flag %s\n",
758 gcb.UserInputFlag ? "on" : "off");
759 printf("\tdelay %d\n", gcb.DelayTime);
760 printf("\ttransparent index %d\n",
761 gcb.TransparentColor);
762 printf("end\n\n");
763 } else if (!last && ep->Function == APPLICATION_EXT_FUNC_CODE &&
764 ep->ByteCount >= 11 && (ep + 1)->ByteCount >= 3 &&
765 memcmp(ep->Bytes, "NETSCAPE2.0", 11) == 0) {
766 unsigned char *params = (++ep)->Bytes;
767 unsigned int loopcount = params[1] | (params[2] << 8);
768 printf("netscape loop %u\n\n", loopcount);
769 } else {
770 printf("extension 0x%02x\n", ep->Function);
771 VisibleDumpBuffer(ep->Bytes, ep->ByteCount);
772 while (!last &&
773 ep[1].Function == CONTINUE_EXT_FUNC_CODE) {
774 ++ep;
775 last = (ep - ExtensionBlocks ==
776 (ExtensionBlockCount - 1));
777 VisibleDumpBuffer(ep->Bytes, ep->ByteCount);
778 putchar('\n');
779 }
780 printf("end\n\n");
781 }
782 }
783 }
784
Gif2Icon(char * FileName,int fdin,int fdout,char NameTable[])785 static void Gif2Icon(char *FileName, int fdin, int fdout, char NameTable[]) {
786 int ErrorCode, im, i, j, ColorCount = 0;
787 GifFileType *GifFile;
788
789 if (fdin == -1) {
790 if ((GifFile = DGifOpenFileName(FileName, &ErrorCode)) ==
791 NULL) {
792 PrintGifError(ErrorCode);
793 exit(EXIT_FAILURE);
794 }
795 } else {
796 /* Use stdin instead: */
797 if ((GifFile = DGifOpenFileHandle(fdin, &ErrorCode)) == NULL) {
798 PrintGifError(ErrorCode);
799 exit(EXIT_FAILURE);
800 }
801 }
802
803 if (DGifSlurp(GifFile) == GIF_ERROR) {
804 PrintGifError(GifFile->Error);
805 exit(EXIT_FAILURE);
806 }
807
808 printf("screen width %d\nscreen height %d\n", GifFile->SWidth,
809 GifFile->SHeight);
810
811 printf(
812 "screen colors %d\nscreen background %d\npixel aspect byte %u\n\n",
813 1 << GifFile->SColorResolution, GifFile->SBackGroundColor,
814 (unsigned)GifFile->AspectByte);
815
816 if (GifFile->SColorMap) {
817 printf("screen map\n");
818
819 printf("\tsort flag %s\n",
820 GifFile->SColorMap->SortFlag ? "on" : "off");
821
822 for (i = 0; i < GifFile->SColorMap->ColorCount; i++) {
823 if (GifFile->SColorMap->ColorCount < PRINTABLES) {
824 printf("\trgb %03d %03d %03d is %c\n",
825 GifFile->SColorMap->Colors[i].Red,
826 GifFile->SColorMap->Colors[i].Green,
827 GifFile->SColorMap->Colors[i].Blue,
828 NameTable[i]);
829 } else {
830 printf("\trgb %03d %03d %03d\n",
831 GifFile->SColorMap->Colors[i].Red,
832 GifFile->SColorMap->Colors[i].Green,
833 GifFile->SColorMap->Colors[i].Blue);
834 }
835 }
836 printf("end\n\n");
837 }
838
839 for (im = 0; im < GifFile->ImageCount; im++) {
840 SavedImage *image = &GifFile->SavedImages[im];
841
842 DumpExtensions(GifFile, image->ExtensionBlockCount,
843 image->ExtensionBlocks);
844
845 printf("image # %d\nimage left %d\nimage top %d\n", im + 1,
846 image->ImageDesc.Left, image->ImageDesc.Top);
847 if (image->ImageDesc.Interlace) {
848 printf("image interlaced\n");
849 }
850
851 if (image->ImageDesc.ColorMap) {
852 printf("image map\n");
853
854 printf("\tsort flag %s\n",
855 image->ImageDesc.ColorMap->SortFlag ? "on"
856 : "off");
857
858 if (image->ImageDesc.ColorMap->ColorCount <
859 PRINTABLES) {
860 for (i = 0;
861 i < image->ImageDesc.ColorMap->ColorCount;
862 i++) {
863 printf(
864 "\trgb %03d %03d %03d is %c\n",
865 image->ImageDesc.ColorMap->Colors[i]
866 .Red,
867 image->ImageDesc.ColorMap->Colors[i]
868 .Green,
869 image->ImageDesc.ColorMap->Colors[i]
870 .Blue,
871 NameTable[i]);
872 }
873 } else {
874 for (i = 0;
875 i < image->ImageDesc.ColorMap->ColorCount;
876 i++) {
877 printf(
878 "\trgb %03d %03d %03d\n",
879 image->ImageDesc.ColorMap->Colors[i]
880 .Red,
881 image->ImageDesc.ColorMap->Colors[i]
882 .Green,
883 image->ImageDesc.ColorMap->Colors[i]
884 .Blue);
885 }
886 }
887 printf("end\n\n");
888 }
889
890 /* one of these conditions has to be true */
891 if (image->ImageDesc.ColorMap) {
892 ColorCount = image->ImageDesc.ColorMap->ColorCount;
893 } else if (GifFile->SColorMap) {
894 ColorCount = GifFile->SColorMap->ColorCount;
895 }
896
897 if (ColorCount < PRINTABLES) {
898 printf("image bits %d by %d\n", image->ImageDesc.Width,
899 image->ImageDesc.Height);
900 } else {
901 printf("image bits %d by %d hex\n",
902 image->ImageDesc.Width, image->ImageDesc.Height);
903 }
904 for (i = 0; i < image->ImageDesc.Height; i++) {
905 for (j = 0; j < image->ImageDesc.Width; j++) {
906 GifByteType ch =
907 image->RasterBits
908 [i * image->ImageDesc.Width + j];
909 if (ColorCount < PRINTABLES &&
910 ch < PRINTABLES) {
911 putchar(NameTable[ch]);
912 } else {
913 printf("%02x", ch);
914 }
915 }
916 putchar('\n');
917 }
918 putchar('\n');
919 }
920
921 DumpExtensions(GifFile, GifFile->ExtensionBlockCount,
922 GifFile->ExtensionBlocks);
923
924 /* Tell EMACS this is a picture... */
925 printf("# The following sets edit modes for GNU EMACS\n");
926 printf("# Local "); /* ...break this up, so that EMACS doesn't */
927 printf("Variables:\n"); /* get confused when visiting *this* file! */
928 printf("# mode:picture\n");
929 printf("# truncate-lines:t\n");
930 printf("# End:\n");
931
932 if (fdin == -1) {
933 (void)printf("# End of %s dump\n", FileName);
934 }
935
936 /*
937 * Sanity checks.
938 */
939
940 /* check that the background color isn't garbage (SF bug #87) */
941 if (GifFile->SBackGroundColor < 0 ||
942 (GifFile->SColorMap &&
943 GifFile->SBackGroundColor >= GifFile->SColorMap->ColorCount)) {
944 fprintf(stderr, "gifbuild: background color invalid for screen "
945 "colormap.\n");
946 }
947
948 if (DGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR) {
949 PrintGifError(ErrorCode);
950 exit(EXIT_FAILURE);
951 }
952 }
953
EscapeString(char * cp,char * tp)954 static int EscapeString(char *cp, char *tp)
955 /* process standard C-style escape sequences in a string */
956 {
957 char *StartAddr = tp;
958
959 while (*cp) {
960 int cval = 0;
961
962 if (*cp == '\\' && strchr("0123456789xX", cp[1])) {
963 int dcount = 0;
964
965 if (*++cp == 'x' || *cp == 'X') {
966 char *dp,
967 *hex = "00112233445566778899aAbBcCdDeEfF";
968 for (++cp;
969 (dp = strchr(hex, *cp)) && (dcount++ < 2);
970 cp++) {
971 cval = (cval * 16) + (dp - hex) / 2;
972 }
973 } else if (*cp == '0') {
974 while (strchr("01234567", *cp) !=
975 (char *)NULL &&
976 (dcount++ < 3)) {
977 cval = (cval * 8) + (*cp++ - '0');
978 }
979 } else {
980 while ((strchr("0123456789", *cp) !=
981 (char *)NULL) &&
982 (dcount++ < 3)) {
983 cval = (cval * 10) + (*cp++ - '0');
984 }
985 }
986 } else if (*cp == '\\') /* C-style character escapes */
987 {
988 switch (*++cp) {
989 case '\\':
990 cval = '\\';
991 break;
992 case 'n':
993 cval = '\n';
994 break;
995 case 't':
996 cval = '\t';
997 break;
998 case 'b':
999 cval = '\b';
1000 break;
1001 case 'r':
1002 cval = '\r';
1003 break;
1004 default:
1005 cval = *cp;
1006 }
1007 cp++;
1008 } else if (*cp == '^') /* expand control-character syntax */
1009 {
1010 cval = (*++cp & 0x1f);
1011 cp++;
1012 } else {
1013 cval = *cp++;
1014 }
1015 *tp++ = cval;
1016 }
1017
1018 return (tp - StartAddr);
1019 }
1020
1021 /* end */
1022