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