]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/uudecode/uudecode.c
This commit was generated by cvs2svn to compensate for changes in r99060,
[FreeBSD/FreeBSD.git] / usr.bin / uudecode / uudecode.c
1 /*-
2  * Copyright (c) 1983, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #ifndef lint
35 static const char copyright[] =
36 "@(#) Copyright (c) 1983, 1993\n\
37         The Regents of the University of California.  All rights reserved.\n";
38 #endif /* not lint */
39
40 #if 0
41 #ifndef lint
42 static char sccsid[] = "@(#)uudecode.c  8.2 (Berkeley) 4/2/94";
43 #endif /* not lint */
44 #endif
45
46 #include <sys/cdefs.h>
47 __FBSDID("$FreeBSD$");
48
49 /*
50  * uudecode [file ...]
51  *
52  * create the specified file, decoding as you go.
53  * used with uuencode.
54  */
55 #include <sys/param.h>
56 #include <sys/socket.h>
57 #include <sys/stat.h>
58
59 #include <netinet/in.h>
60
61 #include <err.h>
62 #include <pwd.h>
63 #include <resolv.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67 #include <unistd.h>
68
69 const char *filename;
70 char *outfile;
71 int cflag, iflag, oflag, pflag, sflag;
72
73 static void usage(void);
74 int     decode(void);
75 int     decode2(int);
76 void    base64_decode(const char *);
77
78 int
79 main(int argc, char *argv[])
80 {
81         int rval, ch;
82
83         while ((ch = getopt(argc, argv, "cio:ps")) != -1) {
84                 switch(ch) {
85                 case 'c':
86                         if (oflag)
87                                 usage();
88                         cflag = 1; /* multiple uudecode'd files */
89                         break;
90                 case 'i':
91                         iflag = 1; /* ask before override files */
92                         break;
93                 case 'o':
94                         if (cflag || pflag || sflag)
95                                 usage();
96                         oflag = 1; /* output to the specified file */
97                         sflag = 1; /* do not strip pathnames for output */
98                         outfile = optarg; /* set the output filename */
99                         break;
100                 case 'p':
101                         if (oflag)
102                                 usage();
103                         pflag = 1; /* print output to stdout */
104                         break;
105                 case 's':
106                         if (oflag)
107                                 usage();
108                         sflag = 1; /* do not strip pathnames for output */
109                         break;
110                 default:
111                         usage();
112                 }
113         }
114         argc -= optind;
115         argv += optind;
116
117                         
118         if (*argv) {
119                 rval = 0;
120                 do {
121                         if (!freopen(filename = *argv, "r", stdin)) {
122                                 warn("%s", *argv);
123                                 rval = 1;
124                                 continue;
125                         }
126                         rval |= decode();
127                 } while (*++argv);
128         } else {
129                 filename = "stdin";
130                 rval = decode();
131         }
132         exit(rval);
133 }
134
135 int
136 decode(void)
137 {
138         int flag;
139
140         /* decode only one file per input stream */
141         if (!cflag) 
142                 return(decode2(0));
143
144         /* multiple uudecode'd files */
145         for (flag = 0; ; flag++)
146                 if (decode2(flag))
147                         return(1);
148                 else if (feof(stdin))
149                         break;
150
151         return(0);
152 }
153
154 int
155 decode2(int flag)
156 {
157         struct passwd *pw;
158         register int n;
159         register char ch, *p;
160         int base64, ignore, n1;
161         char buf[MAXPATHLEN+1];
162         char buffn[MAXPATHLEN+1]; /* file name buffer */
163         char *mode, *s;
164         void *mode_handle;
165
166         base64 = ignore = 0;
167         /* search for header line */
168         do {
169                 if (!fgets(buf, sizeof(buf), stdin)) {
170                         if (flag) /* no error */
171                                 return(0);
172
173                         warnx("%s: no \"begin\" line", filename);
174                         return(1);
175                 }
176         } while (strncmp(buf, "begin", 5) != 0);
177
178         if (strncmp(buf, "begin-base64", 12) == 0)
179                 base64 = 1;
180
181         /* Parse the header: begin{,-base64} mode outfile. */
182         s = strtok(buf, " ");
183         if (s == NULL)
184                 errx(1, "no mode or filename in input file");
185         s = strtok(NULL, " ");
186         if (s == NULL)
187                 errx(1, "no mode in input file");
188         else {
189                 mode = strdup(s);
190                 if (mode == NULL)
191                         err(1, "strdup()");
192         }
193         if (!oflag) {
194                 outfile = strtok(NULL, "\r\n");
195                 if (outfile == NULL)
196                         errx(1, "no filename in input file");
197         }
198
199         if (strlcpy(buf, outfile, sizeof(buf)) >= sizeof(buf))
200                 errx(1, "%s: filename too long", outfile);
201         if (!sflag && !pflag) {
202                 strlcpy(buffn, buf, sizeof(buffn)); 
203                 if (strrchr(buffn, '/') != NULL)
204                         strncpy(buf, strrchr(buffn, '/') + 1, sizeof(buf));
205                 if (buf[0] == '\0') {
206                         warnx("%s: illegal filename", buffn);
207                         return(1);
208                 }
209
210                 /* handle ~user/file format */
211                 if (buf[0] == '~') {
212                         if (!(p = index(buf, '/'))) {
213                                 warnx("%s: illegal ~user", filename);
214                                 return(1);
215                         }
216                         *p++ = '\0';
217                         if (!(pw = getpwnam(buf + 1))) {
218                                 warnx("%s: no user %s", filename, buf);
219                                 return(1);
220                         }
221                         n = strlen(pw->pw_dir);
222                         n1 = strlen(p);
223                         if (n + n1 + 2 > MAXPATHLEN) {
224                                 warnx("%s: path too long", filename);
225                                 return(1);
226                         }
227                         bcopy(p, buf + n + 1, n1 + 1);
228                         bcopy(pw->pw_dir, buf, n);
229                         buf[n] = '/';
230                 }
231         }
232
233         /* create output file, set mode */
234         if (pflag)
235                 ; /* print to stdout */
236
237         else {
238                 mode_handle = setmode(mode);
239                 if (mode_handle == NULL)
240                         err(1, "setmode()");
241                 if (iflag && !access(buf, F_OK)) {
242                         (void)fprintf(stderr, "not overwritten: %s\n", buf);
243                         ignore++;
244                 } else if (!freopen(buf, "w", stdout) ||
245                     fchmod(fileno(stdout), getmode(mode_handle, 0) & 0666)) {
246                         warn("%s: %s", buf, filename);
247                         return(1);
248                 }
249                 free(mode_handle);
250                 free(mode);
251         }
252         strcpy(buffn, buf); /* store file name from header line */
253
254         /* for each input line */
255 next:
256         for (;;) {
257                 if (!fgets(p = buf, sizeof(buf), stdin)) {
258                         warnx("%s: short file", filename);
259                         return(1);
260                 }
261                 if (base64) {
262                         if (strncmp(buf, "====", 4) == 0)
263                                 return (0);
264                         base64_decode(buf);
265                         goto next;
266                 }
267 #define DEC(c)  (((c) - ' ') & 077)             /* single character decode */
268 #define IS_DEC(c) ( (((c) - ' ') >= 0) &&  (((c) - ' ') <= 077 + 1) )
269 /* #define IS_DEC(c) (1) */
270
271 #define OUT_OF_RANGE \
272 {       \
273     warnx( \
274 "\n\tinput file: %s\n\tencoded file: %s\n\tcharacter out of range: [%d-%d]", \
275         filename, buffn, 1 + ' ', 077 + ' ' + 1); \
276         return(1); \
277 }
278 #define PUTCHAR(c) \
279 if (!ignore) \
280         putchar(c)
281
282
283                 /*
284                  * `n' is used to avoid writing out all the characters
285                  * at the end of the file.
286                  */
287                 if ((n = DEC(*p)) <= 0)
288                         break;
289                 for (++p; n > 0; p += 4, n -= 3)
290                         if (n >= 3) {
291                                 if (!(IS_DEC(*p) && IS_DEC(*(p + 1)) && 
292                                      IS_DEC(*(p + 2)) && IS_DEC(*(p + 3))))
293                                         OUT_OF_RANGE
294
295                                 ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
296                                 PUTCHAR(ch);
297                                 ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
298                                 PUTCHAR(ch);
299                                 ch = DEC(p[2]) << 6 | DEC(p[3]);
300                                 PUTCHAR(ch);
301                                 
302                         }
303                         else {
304                                 if (n >= 1) {
305                                         if (!(IS_DEC(*p) && IS_DEC(*(p + 1))))
306                                                 OUT_OF_RANGE
307                                         ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
308                                         PUTCHAR(ch);
309                                 }
310                                 if (n >= 2) {
311                                         if (!(IS_DEC(*(p + 1)) && 
312                                                 IS_DEC(*(p + 2))))
313                                                 OUT_OF_RANGE
314
315                                         ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
316                                         PUTCHAR(ch);
317                                 }
318                                 if (n >= 3) {
319                                         if (!(IS_DEC(*(p + 2)) && 
320                                                 IS_DEC(*(p + 3))))
321                                                 OUT_OF_RANGE
322                                         ch = DEC(p[2]) << 6 | DEC(p[3]);
323                                         PUTCHAR(ch);
324                                 }
325                         }
326         }
327         if (fgets(buf, sizeof(buf), stdin) == NULL || 
328             (strcmp(buf, "end") && strcmp(buf, "end\n") &&
329              strcmp(buf, "end\r\n"))) {
330                 warnx("%s: no \"end\" line", filename);
331                 return(1);
332         }
333         return(0);
334 }
335
336 void
337 base64_decode(const char *stream)
338 {
339         unsigned char out[MAXPATHLEN * 4];
340         int rv;
341
342         if (index(stream, '\r') != NULL)
343                 *index(stream, '\r') = '\0';
344         if (index(stream, '\n') != NULL)
345                 *index(stream, '\n') = '\0';
346         rv = b64_pton(stream, out, (sizeof(out) / sizeof(out[0])));
347         if (rv == -1)
348                 errx(1, "b64_pton: error decoding base64 input stream");
349         fwrite(out, 1, rv, stdout);
350 }
351
352 static void
353 usage(void)
354 {
355         (void)fprintf(stderr,
356 "usage: uudecode [-cips] [file ...]\n"
357 "       uudecode [-i] -o output_file [file]\n"
358 "       b64decode [-cips] [file ...]\n"
359 "       b64decode [-i] -o output_file [file]\n");
360         exit(1);
361 }