blob: 4216e336a6445ef4dc0566ae31808c4c234774f8 [file] [log] [blame]
Eric Andersen2b6ab3c2000-06-13 06:54:53 +00001/* uudecode.c -- uudecode utility.
2 * Copyright (C) 1994, 1995 Free Software Foundation, Inc.
3 *
4 * This product is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2, or (at your option)
7 * any later version.
8 *
9 * This product is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this product; see the file COPYING. If not, write to
16 * the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
18 */
19
20/* Copyright (c) 1983 Regents of the University of California.
21 * All rights reserved.
22 *
23 * Redistribution and use in source and binary forms, with or without
24 * modification, are permitted provided that the following conditions
25 * are met:
26 * 1. Redistributions of source code must retain the above copyright
27 * notice, this list of conditions and the following disclaimer.
28 * 2. Redistributions in binary form must reproduce the above copyright
29 * notice, this list of conditions and the following disclaimer in the
30 * documentation and/or other materials provided with the distribution.
31 * 3. All advertising materials mentioning features or use of this software
32 * must display the following acknowledgement:
33 * This product includes software developed by the University of
34 * California, Berkeley and its contributors.
35 * 4. Neither the name of the University nor the names of its contributors
36 * may be used to endorse or promote products derived from this software
37 * without specific prior written permission.
38 *
39 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
40 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
42 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
43 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
44 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
45 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
46 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
47 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
48 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
49 * SUCH DAMAGE.
50 */
51
52/* Reworked to GNU style by Ian Lance Taylor, ian@airs.com, August 93. */
53
54#include "internal.h"
55
56#include <stdio.h>
57#include <errno.h>
58#include <pwd.h>
59
60/*struct passwd *getpwnam();*/
61
62/* Single character decode. */
63#define DEC(Char) (((Char) - ' ') & 077)
64
65static int read_stduu (const char *inname)
66{
67 char buf[2 * BUFSIZ];
68
69 while (1) {
70 int n;
71 char *p;
72
73 if (fgets (buf, sizeof(buf), stdin) == NULL) {
74 errorMsg("%s: Short file\n", inname);
75 return FALSE;
76 }
77 p = buf;
78
79 /* N is used to avoid writing out all the characters at the end of
80 the file. */
81 n = DEC (*p);
82 if (n <= 0)
83 break;
84 for (++p; n > 0; p += 4, n -= 3) {
85 char ch;
86
87 if (n >= 3) {
88 ch = DEC (p[0]) << 2 | DEC (p[1]) >> 4;
89 putchar (ch);
90 ch = DEC (p[1]) << 4 | DEC (p[2]) >> 2;
91 putchar (ch);
92 ch = DEC (p[2]) << 6 | DEC (p[3]);
93 putchar (ch);
94 } else {
95 if (n >= 1) {
96 ch = DEC (p[0]) << 2 | DEC (p[1]) >> 4;
97 putchar (ch);
98 }
99 if (n >= 2) {
100 ch = DEC (p[1]) << 4 | DEC (p[2]) >> 2;
101 putchar (ch);
102 }
103 }
104 }
105 }
106
107 if (fgets (buf, sizeof(buf), stdin) == NULL
108 || strcmp (buf, "end\n")) {
109 errorMsg("%s: No `end' line\n", inname);
110 return FALSE;
111 }
112
113 return TRUE;
114}
115
116static int read_base64 (const char *inname)
117{
118 static const char b64_tab[256] = {
119 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*000-007*/
120 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*010-017*/
121 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*020-027*/
122 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*030-037*/
123 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*040-047*/
124 '\177', '\177', '\177', '\76', '\177', '\177', '\177', '\77', /*050-057*/
125 '\64', '\65', '\66', '\67', '\70', '\71', '\72', '\73', /*060-067*/
126 '\74', '\75', '\177', '\177', '\177', '\100', '\177', '\177', /*070-077*/
127 '\177', '\0', '\1', '\2', '\3', '\4', '\5', '\6', /*100-107*/
128 '\7', '\10', '\11', '\12', '\13', '\14', '\15', '\16', /*110-117*/
129 '\17', '\20', '\21', '\22', '\23', '\24', '\25', '\26', /*120-127*/
130 '\27', '\30', '\31', '\177', '\177', '\177', '\177', '\177', /*130-137*/
131 '\177', '\32', '\33', '\34', '\35', '\36', '\37', '\40', /*140-147*/
132 '\41', '\42', '\43', '\44', '\45', '\46', '\47', '\50', /*150-157*/
133 '\51', '\52', '\53', '\54', '\55', '\56', '\57', '\60', /*160-167*/
134 '\61', '\62', '\63', '\177', '\177', '\177', '\177', '\177', /*170-177*/
135 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*200-207*/
136 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*210-217*/
137 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*220-227*/
138 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*230-237*/
139 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*240-247*/
140 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*250-257*/
141 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*260-267*/
142 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*270-277*/
143 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*300-307*/
144 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*310-317*/
145 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*320-327*/
146 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*330-337*/
147 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*340-347*/
148 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*350-357*/
149 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*360-367*/
150 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*370-377*/
151 };
152 unsigned char buf[2 * BUFSIZ];
153
154 while (1) {
155 int last_data = 0;
156 unsigned char *p;
157
158 if (fgets (buf, sizeof(buf), stdin) == NULL) {
159 errorMsg("%s: Short file\n", inname);
160 return FALSE;
161 }
162 p = buf;
163
164 if (memcmp (buf, "====", 4) == 0)
165 break;
166 if (last_data != 0) {
167 errorMsg("%s: data following `=' padding character\n", inname);
168 return FALSE;
169 }
170
171 /* The following implementation of the base64 decoding might look
172 a bit clumsy but I only try to follow the POSIX standard:
173 ``All line breaks or other characters not found in the table
174 [with base64 characters] shall be ignored by decoding
175 software.'' */
176 while (*p != '\n') {
177 char c1, c2, c3;
178
179 while ((b64_tab[*p] & '\100') != 0)
180 if (*p == '\n' || *p++ == '=')
181 break;
182 if (*p == '\n')
183 /* This leaves the loop. */
184 continue;
185 c1 = b64_tab[*p++];
186
187 while ((b64_tab[*p] & '\100') != 0)
188 if (*p == '\n' || *p++ == '=') {
189 errorMsg("%s: illegal line\n", inname);
190 return FALSE;
191 }
192 c2 = b64_tab[*p++];
193
194 while (b64_tab[*p] == '\177')
195 if (*p++ == '\n') {
196 errorMsg("%s: illegal line\n", inname);
197 return FALSE;
198 }
199 if (*p == '=') {
200 putchar (c1 << 2 | c2 >> 4);
201 last_data = 1;
202 break;
203 }
204 c3 = b64_tab[*p++];
205
206 while (b64_tab[*p] == '\177')
207 if (*p++ == '\n') {
208 errorMsg("%s: illegal line\n", inname);
209 return FALSE;
210 }
211 putchar (c1 << 2 | c2 >> 4);
212 putchar (c2 << 4 | c3 >> 2);
213 if (*p == '=') {
214 last_data = 1;
215 break;
216 }
217 else
218 putchar (c3 << 6 | b64_tab[*p++]);
219 }
220 }
221
222 return TRUE;
223}
224
225static int decode (const char *inname,
226 const char *forced_outname)
227{
228 struct passwd *pw;
229 register int n;
230 register char *p;
231 int mode, n1;
232 char buf[2 * BUFSIZ];
233 char *outname;
234 int do_base64 = 0;
235
236 /* Search for header line. */
237
238 while (1) {
239 if (fgets (buf, sizeof (buf), stdin) == NULL) {
240 errorMsg("%s: No `begin' line\n", inname);
241 return FALSE;
242 }
243
244 if (strncmp (buf, "begin", 5) == 0) {
245 if (sscanf (buf, "begin-base64 %o %s", &mode, buf) == 2) {
246 do_base64 = 1;
247 break;
248 } else if (sscanf (buf, "begin %o %s", &mode, buf) == 2)
249 break;
250 }
251 }
252
253 /* If the output file name is given on the command line this rules. */
254 if (forced_outname != NULL)
255 outname = (char *) forced_outname;
256 else {
257 /* Handle ~user/file format. */
258 if (buf[0] != '~')
259 outname = buf;
260 else {
261 p = buf + 1;
262 while (*p != '/')
263 ++p;
264 if (*p == '\0') {
265 errorMsg("%s: Illegal ~user\n", inname);
266 return FALSE;
267 }
268 *p++ = '\0';
269 pw = getpwnam (buf + 1);
270 if (pw == NULL) {
271 errorMsg("%s: No user `%s'\n", inname, buf + 1);
272 return FALSE;
273 }
274 n = strlen (pw->pw_dir);
275 n1 = strlen (p);
276 outname = (char *) alloca ((size_t) (n + n1 + 2));
277 memcpy (outname + n + 1, p, (size_t) (n1 + 1));
278 memcpy (outname, pw->pw_dir, (size_t) n);
279 outname[n] = '/';
280 }
281 }
282
283 /* Create output file and set mode. */
284 if (strcmp (outname, "/dev/stdout") != 0 && strcmp (outname, "-") != 0
285 && (freopen (outname, "w", stdout) == NULL
286 || chmod (outname, mode & (S_IRWXU | S_IRWXG | S_IRWXO))
287 )) {
288 errorMsg("uudeoce %s: %s %s\n", outname, inname, strerror(errno)); /* */
289 return FALSE;
290 }
291
292 /* We differenciate decoding standard UU encoding and base64. A
293 common function would only slow down the program. */
294
295 /* For each input line: */
296 if (do_base64)
297 return read_base64 (inname);
298 else
299 return read_stduu (inname);
300}
301
302static const char uudecode_usage[] =
303 "uudecode [FILE]...\n"
304#ifndef BB_FEATURE_TRIVIAL_HELP
305 "\nUudecode a file that is uuencoded.\n\n"
306 "Options:\n"
307 "\t-o FILE\tdirect output to FILE\n"
308#endif
309;
310
311int uudecode_main (int argc,
312 char **argv)
313{
314 int opt;
315 int exit_status;
316 const char *outname;
317 outname = NULL;
318
319 while ((opt = getopt(argc, argv, "o:")) != EOF) {
320 switch (opt) {
321 case 0:
322 break;
323
324 case 'o':
325 outname = optarg;
326 break;
327
328 default:
329 usage(uudecode_usage);
330 }
331 }
332
333 if (optind == argc)
334 exit_status = decode ("stdin", outname) == 0 ? TRUE : FALSE;
335 else {
336 exit_status = TRUE;
337 do {
338 if (freopen (argv[optind], "r", stdin) != NULL) {
339 if (decode (argv[optind], outname) != 0)
340 exit_status = FALSE;
341 } else {
342 errorMsg("uudecode: %s: %s\n", argv[optind], strerror(errno));
343 exit_status = FALSE;
344 }
345 optind++;
346 }
347 while (optind < argc);
348 }
349 exit(exit_status);
350}