Extract usage information into a separate file.
[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 <getopt.h>
59 #include <pwd.h>
60
61 /*struct passwd *getpwnam();*/
62
63 /* Single character decode.  */
64 #define DEC(Char) (((Char) - ' ') & 077)
65
66 static 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
117 static 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
226 static 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          )) {
289     errorMsg("%s: %s %s\n", outname, inname, strerror(errno)); /* */
290     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
303 int 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 {
334         errorMsg("%s: %s\n", argv[optind], strerror(errno));
335         exit_status = FALSE;
336       }
337       optind++;
338     }
339     while (optind < argc);
340   }
341   return(exit_status);
342 }