blob: 90bef9238f103c885ad85e446fa12ee54f761d5c [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>
Eric Andersen999bf722000-07-09 06:59:58 +000058#include <getopt.h>
Eric Andersen2b6ab3c2000-06-13 06:54:53 +000059#include <pwd.h>
60
61/*struct passwd *getpwnam();*/
62
63/* Single character decode. */
64#define DEC(Char) (((Char) - ' ') & 077)
65
66static int read_stduu (const char *inname)
67{
68 char buf[2 * BUFSIZ];
69
70 while (1) {
71 int n;
72 char *p;
73
74 if (fgets (buf, sizeof(buf), stdin) == NULL) {
75 errorMsg("%s: Short file\n", inname);
76 return FALSE;
77 }
78 p = buf;
79
80 /* N is used to avoid writing out all the characters at the end of
81 the file. */
82 n = DEC (*p);
83 if (n <= 0)
84 break;
85 for (++p; n > 0; p += 4, n -= 3) {
86 char ch;
87
88 if (n >= 3) {
89 ch = DEC (p[0]) << 2 | DEC (p[1]) >> 4;
90 putchar (ch);
91 ch = DEC (p[1]) << 4 | DEC (p[2]) >> 2;
92 putchar (ch);
93 ch = DEC (p[2]) << 6 | DEC (p[3]);
94 putchar (ch);
95 } else {
96 if (n >= 1) {
97 ch = DEC (p[0]) << 2 | DEC (p[1]) >> 4;
98 putchar (ch);
99 }
100 if (n >= 2) {
101 ch = DEC (p[1]) << 4 | DEC (p[2]) >> 2;
102 putchar (ch);
103 }
104 }
105 }
106 }
107
108 if (fgets (buf, sizeof(buf), stdin) == NULL
109 || strcmp (buf, "end\n")) {
110 errorMsg("%s: No `end' line\n", inname);
111 return FALSE;
112 }
113
114 return TRUE;
115}
116
117static int read_base64 (const char *inname)
118{
119 static const char b64_tab[256] = {
120 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*000-007*/
121 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*010-017*/
122 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*020-027*/
123 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*030-037*/
124 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*040-047*/
125 '\177', '\177', '\177', '\76', '\177', '\177', '\177', '\77', /*050-057*/
126 '\64', '\65', '\66', '\67', '\70', '\71', '\72', '\73', /*060-067*/
127 '\74', '\75', '\177', '\177', '\177', '\100', '\177', '\177', /*070-077*/
128 '\177', '\0', '\1', '\2', '\3', '\4', '\5', '\6', /*100-107*/
129 '\7', '\10', '\11', '\12', '\13', '\14', '\15', '\16', /*110-117*/
130 '\17', '\20', '\21', '\22', '\23', '\24', '\25', '\26', /*120-127*/
131 '\27', '\30', '\31', '\177', '\177', '\177', '\177', '\177', /*130-137*/
132 '\177', '\32', '\33', '\34', '\35', '\36', '\37', '\40', /*140-147*/
133 '\41', '\42', '\43', '\44', '\45', '\46', '\47', '\50', /*150-157*/
134 '\51', '\52', '\53', '\54', '\55', '\56', '\57', '\60', /*160-167*/
135 '\61', '\62', '\63', '\177', '\177', '\177', '\177', '\177', /*170-177*/
136 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*200-207*/
137 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*210-217*/
138 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*220-227*/
139 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*230-237*/
140 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*240-247*/
141 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*250-257*/
142 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*260-267*/
143 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*270-277*/
144 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*300-307*/
145 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*310-317*/
146 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*320-327*/
147 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*330-337*/
148 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*340-347*/
149 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*350-357*/
150 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*360-367*/
151 '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*370-377*/
152 };
153 unsigned char buf[2 * BUFSIZ];
154
155 while (1) {
156 int last_data = 0;
157 unsigned char *p;
158
159 if (fgets (buf, sizeof(buf), stdin) == NULL) {
160 errorMsg("%s: Short file\n", inname);
161 return FALSE;
162 }
163 p = buf;
164
165 if (memcmp (buf, "====", 4) == 0)
166 break;
167 if (last_data != 0) {
168 errorMsg("%s: data following `=' padding character\n", inname);
169 return FALSE;
170 }
171
172 /* The following implementation of the base64 decoding might look
173 a bit clumsy but I only try to follow the POSIX standard:
174 ``All line breaks or other characters not found in the table
175 [with base64 characters] shall be ignored by decoding
176 software.'' */
177 while (*p != '\n') {
178 char c1, c2, c3;
179
180 while ((b64_tab[*p] & '\100') != 0)
181 if (*p == '\n' || *p++ == '=')
182 break;
183 if (*p == '\n')
184 /* This leaves the loop. */
185 continue;
186 c1 = b64_tab[*p++];
187
188 while ((b64_tab[*p] & '\100') != 0)
189 if (*p == '\n' || *p++ == '=') {
190 errorMsg("%s: illegal line\n", inname);
191 return FALSE;
192 }
193 c2 = b64_tab[*p++];
194
195 while (b64_tab[*p] == '\177')
196 if (*p++ == '\n') {
197 errorMsg("%s: illegal line\n", inname);
198 return FALSE;
199 }
200 if (*p == '=') {
201 putchar (c1 << 2 | c2 >> 4);
202 last_data = 1;
203 break;
204 }
205 c3 = b64_tab[*p++];
206
207 while (b64_tab[*p] == '\177')
208 if (*p++ == '\n') {
209 errorMsg("%s: illegal line\n", inname);
210 return FALSE;
211 }
212 putchar (c1 << 2 | c2 >> 4);
213 putchar (c2 << 4 | c3 >> 2);
214 if (*p == '=') {
215 last_data = 1;
216 break;
217 }
218 else
219 putchar (c3 << 6 | b64_tab[*p++]);
220 }
221 }
222
223 return TRUE;
224}
225
226static int decode (const char *inname,
227 const char *forced_outname)
228{
229 struct passwd *pw;
230 register int n;
231 register char *p;
232 int mode, n1;
233 char buf[2 * BUFSIZ];
234 char *outname;
235 int do_base64 = 0;
236
237 /* Search for header line. */
238
239 while (1) {
240 if (fgets (buf, sizeof (buf), stdin) == NULL) {
241 errorMsg("%s: No `begin' line\n", inname);
242 return FALSE;
243 }
244
245 if (strncmp (buf, "begin", 5) == 0) {
246 if (sscanf (buf, "begin-base64 %o %s", &mode, buf) == 2) {
247 do_base64 = 1;
248 break;
249 } else if (sscanf (buf, "begin %o %s", &mode, buf) == 2)
250 break;
251 }
252 }
253
254 /* If the output file name is given on the command line this rules. */
255 if (forced_outname != NULL)
256 outname = (char *) forced_outname;
257 else {
258 /* Handle ~user/file format. */
259 if (buf[0] != '~')
260 outname = buf;
261 else {
262 p = buf + 1;
263 while (*p != '/')
264 ++p;
265 if (*p == '\0') {
266 errorMsg("%s: Illegal ~user\n", inname);
267 return FALSE;
268 }
269 *p++ = '\0';
270 pw = getpwnam (buf + 1);
271 if (pw == NULL) {
272 errorMsg("%s: No user `%s'\n", inname, buf + 1);
273 return FALSE;
274 }
275 n = strlen (pw->pw_dir);
276 n1 = strlen (p);
277 outname = (char *) alloca ((size_t) (n + n1 + 2));
278 memcpy (outname + n + 1, p, (size_t) (n1 + 1));
279 memcpy (outname, pw->pw_dir, (size_t) n);
280 outname[n] = '/';
281 }
282 }
283
284 /* Create output file and set mode. */
285 if (strcmp (outname, "/dev/stdout") != 0 && strcmp (outname, "-") != 0
286 && (freopen (outname, "w", stdout) == NULL
287 || chmod (outname, mode & (S_IRWXU | S_IRWXG | S_IRWXO))
288 )) {
Matt Kraaibe84cd42000-07-12 17:02:35 +0000289 errorMsg("%s: %s %s\n", outname, inname, strerror(errno)); /* */
Eric Andersen2b6ab3c2000-06-13 06:54:53 +0000290 return FALSE;
291 }
292
293 /* We differenciate decoding standard UU encoding and base64. A
294 common function would only slow down the program. */
295
296 /* For each input line: */
297 if (do_base64)
298 return read_base64 (inname);
299 else
300 return read_stduu (inname);
301}
302
Eric Andersen2b6ab3c2000-06-13 06:54:53 +0000303int uudecode_main (int argc,
304 char **argv)
305{
306 int opt;
307 int exit_status;
308 const char *outname;
309 outname = NULL;
310
311 while ((opt = getopt(argc, argv, "o:")) != EOF) {
312 switch (opt) {
313 case 0:
314 break;
315
316 case 'o':
317 outname = optarg;
318 break;
319
320 default:
321 usage(uudecode_usage);
322 }
323 }
324
325 if (optind == argc)
326 exit_status = decode ("stdin", outname) == 0 ? TRUE : FALSE;
327 else {
328 exit_status = TRUE;
329 do {
330 if (freopen (argv[optind], "r", stdin) != NULL) {
331 if (decode (argv[optind], outname) != 0)
332 exit_status = FALSE;
333 } else {
Matt Kraaibe84cd42000-07-12 17:02:35 +0000334 errorMsg("%s: %s\n", argv[optind], strerror(errno));
Eric Andersen2b6ab3c2000-06-13 06:54:53 +0000335 exit_status = FALSE;
336 }
337 optind++;
338 }
339 while (optind < argc);
340 }
Eric Andersenb6106152000-06-19 17:25:40 +0000341 return(exit_status);
Eric Andersen2b6ab3c2000-06-13 06:54:53 +0000342}