1 /*****************************************************************************
2
3 gifinto - save GIF on stdin to file if size over set threshold
4
5 SPDX-License-Identifier: MIT
6
7 *****************************************************************************/
8
9 #include <ctype.h>
10 #include <fcntl.h>
11 #include <stdbool.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15
16 #ifdef _WIN32
17 #include <io.h>
18 #else
19 #include <unistd.h>
20 #endif /* _WIN32 */
21
22 #include "getarg.h"
23 #include "gif_lib.h"
24
25 #define PROGRAM_NAME "gifinto"
26
27 #define STRLEN 512
28
29 #define DEFAULT_MIN_FILE_SIZE 14 /* More than GIF stamp + screen desc. */
30 #define DEFAULT_OUT_NAME "GifInto.Gif"
31 #define DEFAULT_TMP_NAME "TempInto.XXXXXX"
32
33 static char *VersionStr = PROGRAM_NAME VERSION_COOKIE
34 " Gershon Elber, " __DATE__ ", " __TIME__ "\n"
35 "(C) Copyright 1989 Gershon Elber.\n";
36 static char *CtrlStr = PROGRAM_NAME " v%- s%-MinFileSize!d h%- GifFile!*s";
37
38 static int MinFileSize = DEFAULT_MIN_FILE_SIZE;
39
40 #ifdef _WIN32
41 #include <errno.h>
42 #include <sys/stat.h>
mkstemp(char * tpl)43 int mkstemp(char *tpl) {
44 int fd = -1;
45 char *p;
46 int e = errno;
47
48 errno = 0;
49 p = _mktemp(tpl);
50 if (*p && errno == 0) {
51 errno = e;
52 fd = _open(p, _O_RDWR | _O_CREAT | _O_EXCL | _O_BINARY,
53 _S_IREAD | _S_IWRITE);
54 }
55 return fd;
56 }
57 #endif
58
59 /******************************************************************************
60 This is simply: read until EOF, then close the output, test its length, and
61 if non zero then rename it.
62 ******************************************************************************/
main(int argc,char ** argv)63 int main(int argc, char **argv) {
64 int FD;
65 int NumFiles;
66 bool Error, MinSizeFlag = false, HelpFlag = false,
67 GifNoisyPrint = false;
68 char **FileName = NULL, FoutTmpName[STRLEN + 1], FullPath[STRLEN + 1],
69 *p;
70 FILE *Fin, *Fout;
71
72 if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint,
73 &MinSizeFlag, &MinFileSize, &HelpFlag, &NumFiles,
74 &FileName)) != false ||
75 (NumFiles > 1 && !HelpFlag)) {
76 if (Error) {
77 GAPrintErrMsg(Error);
78 } else if (NumFiles != 1) {
79 GIF_MESSAGE("Error in command line parsing - one GIF "
80 "file please.");
81 }
82 GAPrintHowTo(CtrlStr);
83 exit(EXIT_FAILURE);
84 }
85
86 if (HelpFlag) {
87 (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR);
88 GAPrintHowTo(CtrlStr);
89 exit(EXIT_SUCCESS);
90 }
91
92 /* Open the stdin in binary mode and increase its buffer size: */
93 #ifdef _WIN32
94 _setmode(0, O_BINARY); /* Make sure it is in binary mode. */
95 #endif
96
97 Fin = fdopen(0, "rb"); /* Make it into a stream: */
98
99 if (Fin == NULL) {
100 GIF_EXIT("Failed to open input.");
101 }
102
103 /* Isolate the directory where our destination is, and set tmp file name
104 */
105 /* in the very same directory. This code is isecure because it creates
106 */
107 /* predictable names, but it's not worth the effort and risk to fix. */
108 if (*FileName == NULL) {
109 GIF_EXIT("No valid Filename given.");
110 }
111 if (strlen(*FileName) > STRLEN - 1) {
112 GIF_EXIT("Filename too long.");
113 }
114 memset(FullPath, '\0', sizeof(FullPath));
115 strncpy(FullPath, *FileName, STRLEN);
116 if ((p = strrchr(FullPath, '/')) != NULL ||
117 (p = strrchr(FullPath, '\\')) != NULL) {
118 p[1] = 0;
119 } else if ((p = strrchr(FullPath, ':')) != NULL) {
120 p[1] = 0;
121 } else {
122 FullPath[0] = 0; /* No directory or disk specified. */
123 }
124 if (strlen(FullPath) > STRLEN - 1) {
125 GIF_EXIT("Filename too long.");
126 }
127 strncpy(FoutTmpName, FullPath, STRLEN); /* First setup the Path */
128 /* then add a name for the tempfile */
129 if ((strlen(FoutTmpName) + strlen(DEFAULT_TMP_NAME)) > STRLEN - 1) {
130 GIF_EXIT("Filename too long.");
131 }
132 strcat(FoutTmpName, DEFAULT_TMP_NAME);
133 #ifdef _WIN32
134 char *tmpFN = _mktemp(FoutTmpName);
135 if (tmpFN) {
136 FD = open(tmpFN, O_CREAT | O_EXCL | O_WRONLY);
137 } else {
138 FD = -1;
139 }
140 #else
141 FD = mkstemp(FoutTmpName); /* returns filedescriptor */
142 #endif
143 if (FD == -1) {
144 GIF_EXIT("Failed to open output.");
145 }
146 Fout = fdopen(FD, "wb"); /* returns a stream with FD */
147 if (Fout == NULL) {
148 GIF_EXIT("Failed to open output.");
149 }
150
151 while (1) {
152 int c = getc(Fin);
153
154 if (feof(Fin)) {
155 break;
156 }
157 if (putc(c, Fout) == EOF) {
158 GIF_EXIT("Failed to write output.");
159 }
160 }
161
162 fclose(Fin);
163 if (ftell(Fout) >= (long)MinFileSize) {
164 fclose(Fout);
165 unlink(*FileName);
166 if (rename(FoutTmpName, *FileName) != 0) {
167 char DefaultName[STRLEN + 1];
168 memset(DefaultName, '\0', sizeof(DefaultName));
169 if ((strlen(FullPath) + strlen(DEFAULT_OUT_NAME)) >
170 STRLEN - 1) {
171 GIF_EXIT("Filename too long.");
172 }
173 strncpy(DefaultName, FullPath, STRLEN);
174 strcat(DefaultName, DEFAULT_OUT_NAME);
175 if (rename(FoutTmpName, DefaultName) == 0) {
176 char s[STRLEN];
177 snprintf(
178 s, STRLEN,
179 "Failed to rename out file - left as %s.",
180 DefaultName);
181 GIF_MESSAGE(s);
182 } else {
183 unlink(FoutTmpName);
184 GIF_MESSAGE(
185 "Failed to rename out file - deleted.");
186 }
187 }
188 } else {
189 fclose(Fout);
190 unlink(FoutTmpName);
191 GIF_MESSAGE("File too small - not renamed.");
192 }
193
194 return 0;
195 }
196
197 /* end */
198