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