don't whine if all we need to do is remove a bg job
[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 int n;
205   register char *p;
206   int mode, n1;
207   char buf[2 * BUFSIZ];
208   char *outname;
209   int do_base64 = 0;
210
211   /* Search for header line.  */
212
213   while (1) {
214     if (fgets (buf, sizeof (buf), stdin) == NULL) {
215       error_msg("%s: No `begin' line", inname);
216       return FALSE;
217     }
218
219     if (strncmp (buf, "begin", 5) == 0) {
220       if (sscanf (buf, "begin-base64 %o %s", &mode, buf) == 2) {
221         do_base64 = 1;
222         break;
223       } else if (sscanf (buf, "begin %o %s", &mode, buf) == 2)
224         break;
225     }
226   }
227
228   /* If the output file name is given on the command line this rules.  */
229   if (forced_outname != NULL)
230     outname = (char *) forced_outname;
231   else {
232     /* Handle ~user/file format.  */
233     if (buf[0] != '~')
234       outname = buf;
235     else {
236       p = buf + 1;
237       while (*p != '/')
238         ++p;
239       if (*p == '\0') {
240         error_msg("%s: Illegal ~user", inname);
241         return FALSE;
242       }
243       *p++ = '\0';
244       pw = getpwnam (buf + 1);
245       if (pw == NULL) {
246         error_msg("%s: No user `%s'", inname, buf + 1);
247         return FALSE;
248       }
249       n = strlen (pw->pw_dir);
250       n1 = strlen (p);
251       outname = (char *) alloca ((size_t) (n + n1 + 2));
252       memcpy (outname + n + 1, p, (size_t) (n1 + 1));
253       memcpy (outname, pw->pw_dir, (size_t) n);
254       outname[n] = '/';
255     }
256   }
257
258   /* Create output file and set mode.  */
259   if (strcmp (outname, "/dev/stdout") != 0 && strcmp (outname, "-") != 0
260       && (freopen (outname, "w", stdout) == NULL
261           || chmod (outname, mode & (S_IRWXU | S_IRWXG | S_IRWXO))
262          )) {
263     perror_msg("%s", 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     return read_base64 (inname);
273   else
274     return read_stduu (inname);
275 }
276
277 int uudecode_main (int argc,
278                    char **argv)
279 {
280   int opt;
281   int exit_status;
282   const char *outname;
283   outname = NULL;
284
285   while ((opt = getopt(argc, argv, "o:")) != EOF) {
286     switch (opt) {
287      case 0:
288       break;
289
290      case 'o':
291       outname = optarg;
292       break;
293
294      default:
295       show_usage();
296     }
297   }
298
299   if (optind == argc)
300     exit_status = decode ("stdin", outname) == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
301   else {
302     exit_status = EXIT_SUCCESS;
303     do {
304       if (freopen (argv[optind], "r", stdin) != NULL) {
305         if (decode (argv[optind], outname) != 0)
306           exit_status = FALSE;
307       } else {
308         perror_msg("%s", argv[optind]);
309         exit_status = EXIT_FAILURE;
310       }
311       optind++;
312     }
313     while (optind < argc);
314   }
315   return(exit_status);
316 }
317
318 /* Copyright (c) 1983 Regents of the University of California.
319  * All rights reserved.
320  *
321  * Redistribution and use in source and binary forms, with or without
322  * modification, are permitted provided that the following conditions
323  * are met:
324  * 1. Redistributions of source code must retain the above copyright
325  *    notice, this list of conditions and the following disclaimer.
326  * 2. Redistributions in binary form must reproduce the above copyright
327  *    notice, this list of conditions and the following disclaimer in the
328  *    documentation and/or other materials provided with the distribution.
329  *
330  * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change 
331  *              ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change> 
332  *
333  * 4. Neither the name of the University nor the names of its contributors
334  *    may be used to endorse or promote products derived from this software
335  *    without specific prior written permission.
336  *
337  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
338  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
339  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
340  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
341  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
342  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
343  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
344  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
345  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
346  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
347  * SUCH DAMAGE.
348  */
349
350