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