Update internal.h to conditionally include asm/string.h
[oweals/busybox.git] / uudecode.c
1 /* 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
65 static 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
116 static 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
225 static 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
302 static 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
311 int 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   return(exit_status);
350 }