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