xref: /aosp_15_r20/external/giflib/gifbuild.c (revision 324bb76b8d05e2a05aa88511fff61cf3f9ca5892)
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