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