xref: /aosp_15_r20/external/giflib/giffix.c (revision 324bb76b8d05e2a05aa88511fff61cf3f9ca5892)
1 /*****************************************************************************
2 
3 giffix - attempt to fix a truncated 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 "giffix"
19 
20 static char *VersionStr = PROGRAM_NAME VERSION_COOKIE
21     "	Gershon Elber,	" __DATE__ ",   " __TIME__ "\n"
22     "(C) Copyright 1989 Gershon Elber.\n";
23 static char *CtrlStr = PROGRAM_NAME " v%- h%- GifFile!*s";
24 
25 static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut);
26 
27 /******************************************************************************
28  Interpret the command line and scan the given GIF file.
29 ******************************************************************************/
main(int argc,char ** argv)30 int main(int argc, char **argv) {
31 	int i, j, NumFiles, ExtCode, Row, Col, Width, Height, ErrorCode,
32 	    DarkestColor = 0, ColorIntens = 10000;
33 	bool Error, HelpFlag = false, GifNoisyPrint = false;
34 	GifRecordType RecordType;
35 	GifByteType *Extension;
36 	char **FileName = NULL;
37 	GifRowType LineBuffer;
38 	ColorMapObject *ColorMap;
39 	GifFileType *GifFileIn = NULL, *GifFileOut = NULL;
40 	int ImageNum = 0;
41 
42 	if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, &HelpFlag,
43 	                       &NumFiles, &FileName)) != false ||
44 	    (NumFiles > 1 && !HelpFlag)) {
45 		if (Error) {
46 			GAPrintErrMsg(Error);
47 		} else if (NumFiles > 1) {
48 			GIF_MESSAGE("Error in command line parsing - one GIF "
49 			            "file please.");
50 		}
51 		GAPrintHowTo(CtrlStr);
52 		exit(EXIT_FAILURE);
53 	}
54 
55 	if (HelpFlag) {
56 		(void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR);
57 		GAPrintHowTo(CtrlStr);
58 		exit(EXIT_SUCCESS);
59 	}
60 
61 	if (NumFiles == 1) {
62 		if ((GifFileIn = DGifOpenFileName(*FileName, &ErrorCode)) ==
63 		    NULL) {
64 			PrintGifError(ErrorCode);
65 			exit(EXIT_FAILURE);
66 		}
67 	} else {
68 		/* Use stdin instead: */
69 		if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) {
70 			PrintGifError(ErrorCode);
71 			exit(EXIT_FAILURE);
72 		}
73 	}
74 
75 	/* Open stdout for the output file: */
76 	if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) {
77 		PrintGifError(ErrorCode);
78 		exit(EXIT_FAILURE);
79 	}
80 
81 	/* Dump out exactly same screen information: */
82 	/* coverity[var_deref_op] */
83 	if (EGifPutScreenDesc(GifFileOut, GifFileIn->SWidth, GifFileIn->SHeight,
84 	                      GifFileIn->SColorResolution,
85 	                      GifFileIn->SBackGroundColor,
86 	                      GifFileIn->SColorMap) == GIF_ERROR) {
87 		QuitGifError(GifFileIn, GifFileOut);
88 	}
89 
90 	if ((LineBuffer = (GifRowType)malloc(GifFileIn->SWidth)) == NULL) {
91 		GIF_EXIT("Failed to allocate memory required, aborted.");
92 	}
93 
94 	/* Scan the content of the GIF file and load the image(s) in: */
95 	do {
96 		if (DGifGetRecordType(GifFileIn, &RecordType) == GIF_ERROR) {
97 			QuitGifError(GifFileIn, GifFileOut);
98 		}
99 
100 		switch (RecordType) {
101 		case IMAGE_DESC_RECORD_TYPE:
102 			if (DGifGetImageDesc(GifFileIn) == GIF_ERROR) {
103 				QuitGifError(GifFileIn, GifFileOut);
104 			}
105 			if (GifFileIn->Image.Interlace) {
106 				GIF_EXIT("Cannot fix interlaced images.");
107 			}
108 
109 			Row = GifFileIn->Image
110 			          .Top; /* Image Position relative to Screen. */
111 			Col = GifFileIn->Image.Left;
112 			Width = GifFileIn->Image.Width;
113 			Height = GifFileIn->Image.Height;
114 			GifQprintf("\n%s: Image %d at (%d, %d) [%dx%d]:     ",
115 			           PROGRAM_NAME, ++ImageNum, Col, Row, Width,
116 			           Height);
117 			if (Width > GifFileIn->SWidth) {
118 				GIF_EXIT("Image is wider than total");
119 			}
120 
121 			/* Put the image descriptor to out file: */
122 			if (EGifPutImageDesc(
123 			        GifFileOut, Col, Row, Width, Height, false,
124 			        GifFileIn->Image.ColorMap) == GIF_ERROR) {
125 				QuitGifError(GifFileIn, GifFileOut);
126 			}
127 
128 			/* Find the darkest color in color map to use as a
129 			 * filler. */
130 			ColorMap = (GifFileIn->Image.ColorMap
131 			                ? GifFileIn->Image.ColorMap
132 			                : GifFileIn->SColorMap);
133 			for (i = 0; i < ColorMap->ColorCount; i++) {
134 				j = ((int)ColorMap->Colors[i].Red) * 30 +
135 				    ((int)ColorMap->Colors[i].Green) * 59 +
136 				    ((int)ColorMap->Colors[i].Blue) * 11;
137 				if (j < ColorIntens) {
138 					ColorIntens = j;
139 					DarkestColor = i;
140 				}
141 			}
142 
143 			/* Load the image, and dump it. */
144 			for (i = 0; i < Height; i++) {
145 				GifQprintf("\b\b\b\b%-4d", i);
146 				if (DGifGetLine(GifFileIn, LineBuffer, Width) ==
147 				    GIF_ERROR) {
148 					break;
149 				}
150 				if (EGifPutLine(GifFileOut, LineBuffer,
151 				                Width) == GIF_ERROR) {
152 					QuitGifError(GifFileIn, GifFileOut);
153 				}
154 			}
155 
156 			if (i < Height) {
157 				fprintf(stderr, "\nFollowing error occurred "
158 				                "(and ignored):");
159 				PrintGifError(GifFileIn->Error);
160 
161 				/* Fill in with the darkest color in color map.
162 				 */
163 				for (j = 0; j < Width; j++) {
164 					LineBuffer[j] = DarkestColor;
165 				}
166 				for (; i < Height; i++) {
167 					if (EGifPutLine(GifFileOut, LineBuffer,
168 					                Width) == GIF_ERROR) {
169 						QuitGifError(GifFileIn,
170 						             GifFileOut);
171 					}
172 				}
173 			}
174 			break;
175 		case EXTENSION_RECORD_TYPE:
176 			/* pass through extension records */
177 			if (DGifGetExtension(GifFileIn, &ExtCode, &Extension) ==
178 			    GIF_ERROR) {
179 				QuitGifError(GifFileIn, GifFileOut);
180 			}
181 			if (EGifPutExtensionLeader(GifFileOut, ExtCode) ==
182 			    GIF_ERROR) {
183 				QuitGifError(GifFileIn, GifFileOut);
184 			}
185 			if (Extension != NULL) {
186 				if (EGifPutExtensionBlock(
187 				        GifFileOut, Extension[0],
188 				        Extension + 1) == GIF_ERROR) {
189 					QuitGifError(GifFileIn, GifFileOut);
190 				}
191 			}
192 			while (Extension != NULL) {
193 				if (DGifGetExtensionNext(
194 				        GifFileIn, &Extension) == GIF_ERROR) {
195 					QuitGifError(GifFileIn, GifFileOut);
196 				}
197 				if (Extension != NULL) {
198 					if (EGifPutExtensionBlock(
199 					        GifFileOut, Extension[0],
200 					        Extension + 1) == GIF_ERROR) {
201 						QuitGifError(GifFileIn,
202 						             GifFileOut);
203 					}
204 				}
205 			}
206 			if (EGifPutExtensionTrailer(GifFileOut) == GIF_ERROR) {
207 				QuitGifError(GifFileIn, GifFileOut);
208 			}
209 			break;
210 		case TERMINATE_RECORD_TYPE:
211 			break;
212 		default: /* Should be trapped by DGifGetRecordType. */
213 			break;
214 		}
215 	} while (RecordType != TERMINATE_RECORD_TYPE);
216 
217 	if (DGifCloseFile(GifFileIn, &ErrorCode) == GIF_ERROR) {
218 		PrintGifError(ErrorCode);
219 		exit(EXIT_FAILURE);
220 	}
221 	if (EGifCloseFile(GifFileOut, &ErrorCode) == GIF_ERROR) {
222 		PrintGifError(ErrorCode);
223 		exit(EXIT_FAILURE);
224 	}
225 	return 0;
226 }
227 
228 /******************************************************************************
229  Close both input and output file (if open), and exit.
230 ******************************************************************************/
QuitGifError(GifFileType * GifFileIn,GifFileType * GifFileOut)231 static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut) {
232 	fprintf(stderr, "\nFollowing unrecoverable error occurred:");
233 	if (GifFileIn != NULL) {
234 		PrintGifError(GifFileIn->Error);
235 		EGifCloseFile(GifFileIn, NULL);
236 	}
237 	if (GifFileOut != NULL) {
238 		PrintGifError(GifFileOut->Error);
239 		EGifCloseFile(GifFileOut, NULL);
240 	}
241 	exit(EXIT_FAILURE);
242 }
243 
244 /* end */
245