uudecode.c - sbase - suckless unix tools | |
git clone git://git.suckless.org/sbase | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
uudecode.c (7090B) | |
--- | |
1 /* See LICENSE file for copyright and license details. */ | |
2 #include <sys/stat.h> | |
3 | |
4 #include <errno.h> | |
5 #include <stdio.h> | |
6 #include <stdlib.h> | |
7 #include <string.h> | |
8 | |
9 #include "util.h" | |
10 | |
11 static int mflag = 0; | |
12 static int oflag = 0; | |
13 | |
14 static FILE * | |
15 parsefile(const char *fname) | |
16 { | |
17 struct stat st; | |
18 int ret; | |
19 | |
20 if (!strcmp(fname, "/dev/stdout") || !strcmp(fname, "-")) | |
21 return stdout; | |
22 ret = lstat(fname, &st); | |
23 /* if it is a new file, try to open it */ | |
24 if (ret < 0 && errno == ENOENT) | |
25 goto tropen; | |
26 if (ret < 0) { | |
27 weprintf("lstat %s:", fname); | |
28 return NULL; | |
29 } | |
30 if (!S_ISREG(st.st_mode)) { | |
31 weprintf("for safety uudecode operates only on regular f… | |
32 return NULL; | |
33 } | |
34 tropen: | |
35 return fopen(fname, "w"); | |
36 } | |
37 | |
38 static void | |
39 parseheader(FILE *fp, const char *s, char **header, mode_t *mode, char *… | |
40 { | |
41 static char bufs[PATH_MAX + 18]; /* len header + mode + maxname … | |
42 char *p, *q; | |
43 size_t n; | |
44 | |
45 if (!fgets(bufs, sizeof(bufs), fp)) | |
46 if (ferror(fp)) | |
47 eprintf("%s: read error:", s); | |
48 if (bufs[0] == '\0' || feof(fp)) | |
49 eprintf("empty or nil header string\n"); | |
50 if (!(p = strchr(bufs, '\n'))) | |
51 eprintf("header string too long or non-newline terminate… | |
52 p = bufs; | |
53 if (!(q = strchr(p, ' '))) | |
54 eprintf("malformed mode string in header, expected ' '\n… | |
55 *header = bufs; | |
56 *q++ = '\0'; | |
57 p = q; | |
58 /* now header should be null terminated, q points to mode */ | |
59 if (!(q = strchr(p, ' '))) | |
60 eprintf("malformed mode string in header, expected ' '\n… | |
61 *q++ = '\0'; | |
62 /* now mode should be null terminated, q points to fname */ | |
63 *mode = parsemode(p, *mode, 0); | |
64 n = strlen(q); | |
65 while (n > 0 && (q[n - 1] == '\n' || q[n - 1] == '\r')) | |
66 q[--n] = '\0'; | |
67 if (n > 0) | |
68 *fname = q; | |
69 else | |
70 eprintf("header string does not contain output file\n"); | |
71 } | |
72 | |
73 static const char b64dt[] = { | |
74 -1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2,-1,-1,-1,-1,-1,-1,-1,-… | |
75 -1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-… | |
76 52,53,54,55,56,57,58,59,60,61,-1,-1,-1, 0,-1,-1,-1, 0, 1, 2, 3, … | |
77 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-… | |
78 -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,4… | |
79 49,50,51,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-… | |
80 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-… | |
81 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-… | |
82 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-… | |
83 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-… | |
84 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, | |
85 }; | |
86 | |
87 static void | |
88 uudecodeb64(FILE *fp, FILE *outfp) | |
89 { | |
90 char bufb[60], *pb; | |
91 char out[45], *po; | |
92 size_t n; | |
93 int b = 0, e, t = -1, l = 1; | |
94 unsigned char b24[3] = {0, 0, 0}; | |
95 | |
96 while ((n = fread(bufb, 1, sizeof(bufb), fp))) { | |
97 for (pb = bufb, po = out; pb < bufb + n; pb++) { | |
98 if (*pb == '\n') { | |
99 l++; | |
100 continue; | |
101 } else if (*pb == '=') { | |
102 switch (b) { | |
103 case 0: | |
104 /* expected '=' remaining | |
105 * including footer */ | |
106 if (--t) { | |
107 fwrite(out, 1, | |
108 (po - out), | |
109 outfp); | |
110 return; | |
111 } | |
112 continue; | |
113 case 1: | |
114 eprintf("%d: unexpected \"=\"" | |
115 "appeared\n", l); | |
116 case 2: | |
117 *po++ = b24[0]; | |
118 b = 0; | |
119 t = 5; /* expect 5 '=' */ | |
120 continue; | |
121 case 3: | |
122 *po++ = b24[0]; | |
123 *po++ = b24[1]; | |
124 b = 0; | |
125 t = 6; /* expect 6 '=' */ | |
126 continue; | |
127 } | |
128 } else if ((e = b64dt[(int)*pb]) == -1) | |
129 eprintf("%d: invalid byte \"%c\"\n", l, … | |
130 else if (e == -2) /* whitespace */ | |
131 continue; | |
132 else if (t > 0) /* state is parsing pad/footer */ | |
133 eprintf("%d: invalid byte \"%c\"" | |
134 " after padding\n", | |
135 l, *pb); | |
136 switch (b) { /* decode next base64 chr based on … | |
137 case 0: b24[0] |= e << 2; break; | |
138 case 1: b24[0] |= (e >> 4) & 0x3; | |
139 b24[1] |= (e & 0xf) << 4; break; | |
140 case 2: b24[1] |= (e >> 2) & 0xf; | |
141 b24[2] |= (e & 0x3) << 6; break; | |
142 case 3: b24[2] |= e; break; | |
143 } | |
144 if (++b == 4) { /* complete decoding an octet */ | |
145 *po++ = b24[0]; | |
146 *po++ = b24[1]; | |
147 *po++ = b24[2]; | |
148 b24[0] = b24[1] = b24[2] = 0; | |
149 b = 0; | |
150 } | |
151 } | |
152 fwrite(out, 1, (po - out), outfp); | |
153 } | |
154 eprintf("%d: invalid uudecode footer \"====\" not found\n", l); | |
155 } | |
156 | |
157 static void | |
158 uudecode(FILE *fp, FILE *outfp) | |
159 { | |
160 char *bufb = NULL, *p; | |
161 size_t n = 0; | |
162 ssize_t len; | |
163 int ch, i; | |
164 | |
165 #define DEC(c) (((c) - ' ') & 077) /* single character decode */ | |
166 #define IS_DEC(c) ( (((c) - ' ') >= 0) && (((c) - ' ') <= 077 + 1) ) | |
167 #define OUT_OF_RANGE(c) eprintf("character %c out of range: [%d-%d]\n", … | |
168 | |
169 while ((len = getline(&bufb, &n, fp)) > 0) { | |
170 p = bufb; | |
171 /* trim newlines */ | |
172 if (!len || bufb[len - 1] != '\n') | |
173 eprintf("no newline found, aborting\n"); | |
174 bufb[len - 1] = '\0'; | |
175 | |
176 /* check for last line */ | |
177 if ((i = DEC(*p)) <= 0) | |
178 break; | |
179 for (++p; i > 0; p += 4, i -= 3) { | |
180 if (i >= 3) { | |
181 if (!(IS_DEC(*p) && IS_DEC(*(p + 1)) && | |
182 IS_DEC(*(p + 2)) && IS_DEC(*(p + 3… | |
183 OUT_OF_RANGE(*p); | |
184 | |
185 ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4; | |
186 putc(ch, outfp); | |
187 ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2; | |
188 putc(ch, outfp); | |
189 ch = DEC(p[2]) << 6 | DEC(p[3]); | |
190 putc(ch, outfp); | |
191 } else { | |
192 if (i >= 1) { | |
193 if (!(IS_DEC(*p) && IS_DEC(*(p +… | |
194 OUT_OF_RANGE(*p); | |
195 | |
196 ch = DEC(p[0]) << 2 | DEC(p[1]) … | |
197 putc(ch, outfp); | |
198 } | |
199 if (i >= 2) { | |
200 if (!(IS_DEC(*(p + 1)) && | |
201 IS_DEC(*(p + 2)))) | |
202 OUT_OF_RANGE(*p); | |
203 | |
204 ch = DEC(p[1]) << 4 | DEC(p[2]) … | |
205 putc(ch, outfp); | |
206 } | |
207 } | |
208 } | |
209 if (ferror(fp)) | |
210 eprintf("read error:"); | |
211 } | |
212 /* check for end or fail */ | |
213 if ((len = getline(&bufb, &n, fp)) < 0) | |
214 eprintf("getline:"); | |
215 if (len < 3 || strncmp(bufb, "end", 3) || bufb[3] != '\n') | |
216 eprintf("invalid uudecode footer \"end\" not found\n"); | |
217 free(bufb); | |
218 } | |
219 | |
220 static void | |
221 usage(void) | |
222 { | |
223 eprintf("usage: %s [-m] [-o output] [file]\n", argv0); | |
224 } | |
225 | |
226 int | |
227 main(int argc, char *argv[]) | |
228 { | |
229 FILE *fp = NULL, *nfp = NULL; | |
230 mode_t mode = 0; | |
231 int ret = 0; | |
232 char *fname, *header, *ifname, *ofname = NULL; | |
233 void (*d) (FILE *, FILE *) = NULL; | |
234 | |
235 ARGBEGIN { | |
236 case 'm': | |
237 mflag = 1; /* accepted but unused (autodetect file type)… | |
238 break; | |
239 case 'o': | |
240 oflag = 1; | |
241 ofname = EARGF(usage()); | |
242 break; | |
243 default: | |
244 usage(); | |
245 } ARGEND | |
246 | |
247 if (argc > 1) | |
248 usage(); | |
249 | |
250 if (!argc || !strcmp(argv[0], "-")) { | |
251 fp = stdin; | |
252 ifname = "<stdin>"; | |
253 } else { | |
254 if (!(fp = fopen(argv[0], "r"))) | |
255 eprintf("fopen %s:", argv[0]); | |
256 ifname = argv[0]; | |
257 } | |
258 | |
259 parseheader(fp, ifname, &header, &mode, &fname); | |
260 | |
261 if (!strncmp(header, "begin", sizeof("begin"))) | |
262 d = uudecode; | |
263 else if (!strncmp(header, "begin-base64", sizeof("begin-base64")… | |
264 d = uudecodeb64; | |
265 else | |
266 eprintf("unknown header %s:", header); | |
267 | |
268 if (oflag) | |
269 fname = ofname; | |
270 if (!(nfp = parsefile(fname))) | |
271 eprintf("fopen %s:", fname); | |
272 | |
273 d(fp, nfp); | |
274 | |
275 if (nfp != stdout && chmod(fname, mode) < 0) | |
276 eprintf("chmod %s:", fname); | |
277 | |
278 ret |= fshut(fp, (fp == stdin) ? "<stdin>" : argv[0]); | |
279 ret |= fshut(nfp, (nfp == stdout) ? "<stdout>" : fname); | |
280 | |
281 return ret; | |
282 } |