xref: /freebsd/usr.bin/bintrans/uudecode.c (revision 47bcbde91dea630d4996b1a7b2c33925fe8d5b5b)
1*47bcbde9SPiotr Pawel Stefaniak /*-
2*47bcbde9SPiotr Pawel Stefaniak  * SPDX-License-Identifier: BSD-3-Clause
3*47bcbde9SPiotr Pawel Stefaniak  *
4*47bcbde9SPiotr Pawel Stefaniak  * Copyright (c) 1983, 1993
5*47bcbde9SPiotr Pawel Stefaniak  *	The Regents of the University of California.  All rights reserved.
6*47bcbde9SPiotr Pawel Stefaniak  *
7*47bcbde9SPiotr Pawel Stefaniak  * Redistribution and use in source and binary forms, with or without
8*47bcbde9SPiotr Pawel Stefaniak  * modification, are permitted provided that the following conditions
9*47bcbde9SPiotr Pawel Stefaniak  * are met:
10*47bcbde9SPiotr Pawel Stefaniak  * 1. Redistributions of source code must retain the above copyright
11*47bcbde9SPiotr Pawel Stefaniak  *    notice, this list of conditions and the following disclaimer.
12*47bcbde9SPiotr Pawel Stefaniak  * 2. Redistributions in binary form must reproduce the above copyright
13*47bcbde9SPiotr Pawel Stefaniak  *    notice, this list of conditions and the following disclaimer in the
14*47bcbde9SPiotr Pawel Stefaniak  *    documentation and/or other materials provided with the distribution.
15*47bcbde9SPiotr Pawel Stefaniak  * 3. Neither the name of the University nor the names of its contributors
16*47bcbde9SPiotr Pawel Stefaniak  *    may be used to endorse or promote products derived from this software
17*47bcbde9SPiotr Pawel Stefaniak  *    without specific prior written permission.
18*47bcbde9SPiotr Pawel Stefaniak  *
19*47bcbde9SPiotr Pawel Stefaniak  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20*47bcbde9SPiotr Pawel Stefaniak  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21*47bcbde9SPiotr Pawel Stefaniak  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22*47bcbde9SPiotr Pawel Stefaniak  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23*47bcbde9SPiotr Pawel Stefaniak  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24*47bcbde9SPiotr Pawel Stefaniak  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25*47bcbde9SPiotr Pawel Stefaniak  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26*47bcbde9SPiotr Pawel Stefaniak  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27*47bcbde9SPiotr Pawel Stefaniak  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28*47bcbde9SPiotr Pawel Stefaniak  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29*47bcbde9SPiotr Pawel Stefaniak  * SUCH DAMAGE.
30*47bcbde9SPiotr Pawel Stefaniak  */
31*47bcbde9SPiotr Pawel Stefaniak 
32*47bcbde9SPiotr Pawel Stefaniak #if 0
33*47bcbde9SPiotr Pawel Stefaniak #ifndef lint
34*47bcbde9SPiotr Pawel Stefaniak static const char copyright[] =
35*47bcbde9SPiotr Pawel Stefaniak "@(#) Copyright (c) 1983, 1993\n\
36*47bcbde9SPiotr Pawel Stefaniak 	The Regents of the University of California.  All rights reserved.\n";
37*47bcbde9SPiotr Pawel Stefaniak #endif /* not lint */
38*47bcbde9SPiotr Pawel Stefaniak 
39*47bcbde9SPiotr Pawel Stefaniak #ifndef lint
40*47bcbde9SPiotr Pawel Stefaniak static char sccsid[] = "@(#)uudecode.c	8.2 (Berkeley) 4/2/94";
41*47bcbde9SPiotr Pawel Stefaniak #endif /* not lint */
42*47bcbde9SPiotr Pawel Stefaniak #endif
43*47bcbde9SPiotr Pawel Stefaniak #include <sys/cdefs.h>
44*47bcbde9SPiotr Pawel Stefaniak __FBSDID("$FreeBSD$");
45*47bcbde9SPiotr Pawel Stefaniak 
46*47bcbde9SPiotr Pawel Stefaniak /*
47*47bcbde9SPiotr Pawel Stefaniak  * uudecode [file ...]
48*47bcbde9SPiotr Pawel Stefaniak  *
49*47bcbde9SPiotr Pawel Stefaniak  * create the specified file, decoding as you go.
50*47bcbde9SPiotr Pawel Stefaniak  * used with uuencode.
51*47bcbde9SPiotr Pawel Stefaniak  */
52*47bcbde9SPiotr Pawel Stefaniak #include <sys/param.h>
53*47bcbde9SPiotr Pawel Stefaniak #include <sys/socket.h>
54*47bcbde9SPiotr Pawel Stefaniak #include <sys/stat.h>
55*47bcbde9SPiotr Pawel Stefaniak 
56*47bcbde9SPiotr Pawel Stefaniak #include <netinet/in.h>
57*47bcbde9SPiotr Pawel Stefaniak 
58*47bcbde9SPiotr Pawel Stefaniak #include <ctype.h>
59*47bcbde9SPiotr Pawel Stefaniak #include <err.h>
60*47bcbde9SPiotr Pawel Stefaniak #include <errno.h>
61*47bcbde9SPiotr Pawel Stefaniak #include <fcntl.h>
62*47bcbde9SPiotr Pawel Stefaniak #include <libgen.h>
63*47bcbde9SPiotr Pawel Stefaniak #include <pwd.h>
64*47bcbde9SPiotr Pawel Stefaniak #include <resolv.h>
65*47bcbde9SPiotr Pawel Stefaniak #include <stdbool.h>
66*47bcbde9SPiotr Pawel Stefaniak #include <stdio.h>
67*47bcbde9SPiotr Pawel Stefaniak #include <stdlib.h>
68*47bcbde9SPiotr Pawel Stefaniak #include <string.h>
69*47bcbde9SPiotr Pawel Stefaniak #include <unistd.h>
70*47bcbde9SPiotr Pawel Stefaniak 
71*47bcbde9SPiotr Pawel Stefaniak extern int main_decode(int, char *[]);
72*47bcbde9SPiotr Pawel Stefaniak 
73*47bcbde9SPiotr Pawel Stefaniak static const char *infile, *outfile;
74*47bcbde9SPiotr Pawel Stefaniak static FILE *infp, *outfp;
75*47bcbde9SPiotr Pawel Stefaniak static bool base64, cflag, iflag, oflag, pflag, rflag, sflag;
76*47bcbde9SPiotr Pawel Stefaniak 
77*47bcbde9SPiotr Pawel Stefaniak static void	usage(void);
78*47bcbde9SPiotr Pawel Stefaniak static int	decode(void);
79*47bcbde9SPiotr Pawel Stefaniak static int	decode2(void);
80*47bcbde9SPiotr Pawel Stefaniak static int	uu_decode(void);
81*47bcbde9SPiotr Pawel Stefaniak static int	base64_decode(void);
82*47bcbde9SPiotr Pawel Stefaniak 
83*47bcbde9SPiotr Pawel Stefaniak int
84*47bcbde9SPiotr Pawel Stefaniak main_decode(int argc, char *argv[])
85*47bcbde9SPiotr Pawel Stefaniak {
86*47bcbde9SPiotr Pawel Stefaniak 	int rval, ch;
87*47bcbde9SPiotr Pawel Stefaniak 
88*47bcbde9SPiotr Pawel Stefaniak 	if (strcmp(basename(argv[0]), "b64decode") == 0)
89*47bcbde9SPiotr Pawel Stefaniak 		base64 = true;
90*47bcbde9SPiotr Pawel Stefaniak 
91*47bcbde9SPiotr Pawel Stefaniak 	while ((ch = getopt(argc, argv, "cimo:prs")) != -1) {
92*47bcbde9SPiotr Pawel Stefaniak 		switch (ch) {
93*47bcbde9SPiotr Pawel Stefaniak 		case 'c':
94*47bcbde9SPiotr Pawel Stefaniak 			if (oflag || rflag)
95*47bcbde9SPiotr Pawel Stefaniak 				usage();
96*47bcbde9SPiotr Pawel Stefaniak 			cflag = true; /* multiple uudecode'd files */
97*47bcbde9SPiotr Pawel Stefaniak 			break;
98*47bcbde9SPiotr Pawel Stefaniak 		case 'i':
99*47bcbde9SPiotr Pawel Stefaniak 			iflag = true; /* ask before override files */
100*47bcbde9SPiotr Pawel Stefaniak 			break;
101*47bcbde9SPiotr Pawel Stefaniak 		case 'm':
102*47bcbde9SPiotr Pawel Stefaniak 			base64 = true;
103*47bcbde9SPiotr Pawel Stefaniak 			break;
104*47bcbde9SPiotr Pawel Stefaniak 		case 'o':
105*47bcbde9SPiotr Pawel Stefaniak 			if (cflag || pflag || rflag || sflag)
106*47bcbde9SPiotr Pawel Stefaniak 				usage();
107*47bcbde9SPiotr Pawel Stefaniak 			oflag = true; /* output to the specified file */
108*47bcbde9SPiotr Pawel Stefaniak 			sflag = true; /* do not strip pathnames for output */
109*47bcbde9SPiotr Pawel Stefaniak 			outfile = optarg; /* set the output filename */
110*47bcbde9SPiotr Pawel Stefaniak 			break;
111*47bcbde9SPiotr Pawel Stefaniak 		case 'p':
112*47bcbde9SPiotr Pawel Stefaniak 			if (oflag)
113*47bcbde9SPiotr Pawel Stefaniak 				usage();
114*47bcbde9SPiotr Pawel Stefaniak 			pflag = true; /* print output to stdout */
115*47bcbde9SPiotr Pawel Stefaniak 			break;
116*47bcbde9SPiotr Pawel Stefaniak 		case 'r':
117*47bcbde9SPiotr Pawel Stefaniak 			if (cflag || oflag)
118*47bcbde9SPiotr Pawel Stefaniak 				usage();
119*47bcbde9SPiotr Pawel Stefaniak 			rflag = true; /* decode raw data */
120*47bcbde9SPiotr Pawel Stefaniak 			break;
121*47bcbde9SPiotr Pawel Stefaniak 		case 's':
122*47bcbde9SPiotr Pawel Stefaniak 			if (oflag)
123*47bcbde9SPiotr Pawel Stefaniak 				usage();
124*47bcbde9SPiotr Pawel Stefaniak 			sflag = true; /* do not strip pathnames for output */
125*47bcbde9SPiotr Pawel Stefaniak 			break;
126*47bcbde9SPiotr Pawel Stefaniak 		default:
127*47bcbde9SPiotr Pawel Stefaniak 			usage();
128*47bcbde9SPiotr Pawel Stefaniak 		}
129*47bcbde9SPiotr Pawel Stefaniak 	}
130*47bcbde9SPiotr Pawel Stefaniak 	argc -= optind;
131*47bcbde9SPiotr Pawel Stefaniak 	argv += optind;
132*47bcbde9SPiotr Pawel Stefaniak 
133*47bcbde9SPiotr Pawel Stefaniak 	if (*argv != NULL) {
134*47bcbde9SPiotr Pawel Stefaniak 		rval = 0;
135*47bcbde9SPiotr Pawel Stefaniak 		do {
136*47bcbde9SPiotr Pawel Stefaniak 			infp = fopen(infile = *argv, "r");
137*47bcbde9SPiotr Pawel Stefaniak 			if (infp == NULL) {
138*47bcbde9SPiotr Pawel Stefaniak 				warn("%s", *argv);
139*47bcbde9SPiotr Pawel Stefaniak 				rval = 1;
140*47bcbde9SPiotr Pawel Stefaniak 				continue;
141*47bcbde9SPiotr Pawel Stefaniak 			}
142*47bcbde9SPiotr Pawel Stefaniak 			rval |= decode();
143*47bcbde9SPiotr Pawel Stefaniak 			fclose(infp);
144*47bcbde9SPiotr Pawel Stefaniak 		} while (*++argv);
145*47bcbde9SPiotr Pawel Stefaniak 	} else {
146*47bcbde9SPiotr Pawel Stefaniak 		infile = "stdin";
147*47bcbde9SPiotr Pawel Stefaniak 		infp = stdin;
148*47bcbde9SPiotr Pawel Stefaniak 		rval = decode();
149*47bcbde9SPiotr Pawel Stefaniak 	}
150*47bcbde9SPiotr Pawel Stefaniak 	exit(rval);
151*47bcbde9SPiotr Pawel Stefaniak }
152*47bcbde9SPiotr Pawel Stefaniak 
153*47bcbde9SPiotr Pawel Stefaniak static int
154*47bcbde9SPiotr Pawel Stefaniak decode(void)
155*47bcbde9SPiotr Pawel Stefaniak {
156*47bcbde9SPiotr Pawel Stefaniak 	int r, v;
157*47bcbde9SPiotr Pawel Stefaniak 
158*47bcbde9SPiotr Pawel Stefaniak 	if (rflag) {
159*47bcbde9SPiotr Pawel Stefaniak 		/* relaxed alternative to decode2() */
160*47bcbde9SPiotr Pawel Stefaniak 		outfile = "/dev/stdout";
161*47bcbde9SPiotr Pawel Stefaniak 		outfp = stdout;
162*47bcbde9SPiotr Pawel Stefaniak 		if (base64)
163*47bcbde9SPiotr Pawel Stefaniak 			return (base64_decode());
164*47bcbde9SPiotr Pawel Stefaniak 		else
165*47bcbde9SPiotr Pawel Stefaniak 			return (uu_decode());
166*47bcbde9SPiotr Pawel Stefaniak 	}
167*47bcbde9SPiotr Pawel Stefaniak 	v = decode2();
168*47bcbde9SPiotr Pawel Stefaniak 	if (v == EOF) {
169*47bcbde9SPiotr Pawel Stefaniak 		warnx("%s: missing or bad \"begin\" line", infile);
170*47bcbde9SPiotr Pawel Stefaniak 		return (1);
171*47bcbde9SPiotr Pawel Stefaniak 	}
172*47bcbde9SPiotr Pawel Stefaniak 	for (r = v; cflag; r |= v) {
173*47bcbde9SPiotr Pawel Stefaniak 		v = decode2();
174*47bcbde9SPiotr Pawel Stefaniak 		if (v == EOF)
175*47bcbde9SPiotr Pawel Stefaniak 			break;
176*47bcbde9SPiotr Pawel Stefaniak 	}
177*47bcbde9SPiotr Pawel Stefaniak 	return (r);
178*47bcbde9SPiotr Pawel Stefaniak }
179*47bcbde9SPiotr Pawel Stefaniak 
180*47bcbde9SPiotr Pawel Stefaniak static int
181*47bcbde9SPiotr Pawel Stefaniak decode2(void)
182*47bcbde9SPiotr Pawel Stefaniak {
183*47bcbde9SPiotr Pawel Stefaniak 	int flags, fd, mode;
184*47bcbde9SPiotr Pawel Stefaniak 	size_t n, m;
185*47bcbde9SPiotr Pawel Stefaniak 	char *p, *q;
186*47bcbde9SPiotr Pawel Stefaniak 	void *handle;
187*47bcbde9SPiotr Pawel Stefaniak 	struct passwd *pw;
188*47bcbde9SPiotr Pawel Stefaniak 	struct stat st;
189*47bcbde9SPiotr Pawel Stefaniak 	char buf[MAXPATHLEN + 1];
190*47bcbde9SPiotr Pawel Stefaniak 
191*47bcbde9SPiotr Pawel Stefaniak 	base64 = false;
192*47bcbde9SPiotr Pawel Stefaniak 	/* search for header line */
193*47bcbde9SPiotr Pawel Stefaniak 	for (;;) {
194*47bcbde9SPiotr Pawel Stefaniak 		if (fgets(buf, sizeof(buf), infp) == NULL)
195*47bcbde9SPiotr Pawel Stefaniak 			return (EOF);
196*47bcbde9SPiotr Pawel Stefaniak 		p = buf;
197*47bcbde9SPiotr Pawel Stefaniak 		if (strncmp(p, "begin-base64 ", 13) == 0) {
198*47bcbde9SPiotr Pawel Stefaniak 			base64 = true;
199*47bcbde9SPiotr Pawel Stefaniak 			p += 13;
200*47bcbde9SPiotr Pawel Stefaniak 		} else if (strncmp(p, "begin ", 6) == 0)
201*47bcbde9SPiotr Pawel Stefaniak 			p += 6;
202*47bcbde9SPiotr Pawel Stefaniak 		else
203*47bcbde9SPiotr Pawel Stefaniak 			continue;
204*47bcbde9SPiotr Pawel Stefaniak 		/* p points to mode */
205*47bcbde9SPiotr Pawel Stefaniak 		q = strchr(p, ' ');
206*47bcbde9SPiotr Pawel Stefaniak 		if (q == NULL)
207*47bcbde9SPiotr Pawel Stefaniak 			continue;
208*47bcbde9SPiotr Pawel Stefaniak 		*q++ = '\0';
209*47bcbde9SPiotr Pawel Stefaniak 		/* q points to filename */
210*47bcbde9SPiotr Pawel Stefaniak 		n = strlen(q);
211*47bcbde9SPiotr Pawel Stefaniak 		while (n > 0 && (q[n-1] == '\n' || q[n-1] == '\r'))
212*47bcbde9SPiotr Pawel Stefaniak 			q[--n] = '\0';
213*47bcbde9SPiotr Pawel Stefaniak 		/* found valid header? */
214*47bcbde9SPiotr Pawel Stefaniak 		if (n > 0)
215*47bcbde9SPiotr Pawel Stefaniak 			break;
216*47bcbde9SPiotr Pawel Stefaniak 	}
217*47bcbde9SPiotr Pawel Stefaniak 
218*47bcbde9SPiotr Pawel Stefaniak 	handle = setmode(p);
219*47bcbde9SPiotr Pawel Stefaniak 	if (handle == NULL) {
220*47bcbde9SPiotr Pawel Stefaniak 		warnx("%s: unable to parse file mode", infile);
221*47bcbde9SPiotr Pawel Stefaniak 		return (1);
222*47bcbde9SPiotr Pawel Stefaniak 	}
223*47bcbde9SPiotr Pawel Stefaniak 	mode = getmode(handle, 0) & 0666;
224*47bcbde9SPiotr Pawel Stefaniak 	free(handle);
225*47bcbde9SPiotr Pawel Stefaniak 
226*47bcbde9SPiotr Pawel Stefaniak 	if (sflag) {
227*47bcbde9SPiotr Pawel Stefaniak 		/* don't strip, so try ~user/file expansion */
228*47bcbde9SPiotr Pawel Stefaniak 		p = NULL;
229*47bcbde9SPiotr Pawel Stefaniak 		pw = NULL;
230*47bcbde9SPiotr Pawel Stefaniak 		if (*q == '~')
231*47bcbde9SPiotr Pawel Stefaniak 			p = strchr(q, '/');
232*47bcbde9SPiotr Pawel Stefaniak 		if (p != NULL) {
233*47bcbde9SPiotr Pawel Stefaniak 			*p = '\0';
234*47bcbde9SPiotr Pawel Stefaniak 			pw = getpwnam(q + 1);
235*47bcbde9SPiotr Pawel Stefaniak 			*p = '/';
236*47bcbde9SPiotr Pawel Stefaniak 		}
237*47bcbde9SPiotr Pawel Stefaniak 		if (pw != NULL) {
238*47bcbde9SPiotr Pawel Stefaniak 			n = strlen(pw->pw_dir);
239*47bcbde9SPiotr Pawel Stefaniak 			if (buf + n > p) {
240*47bcbde9SPiotr Pawel Stefaniak 				/* make room */
241*47bcbde9SPiotr Pawel Stefaniak 				m = strlen(p);
242*47bcbde9SPiotr Pawel Stefaniak 				if (sizeof(buf) < n + m) {
243*47bcbde9SPiotr Pawel Stefaniak 					warnx("%s: bad output filename",
244*47bcbde9SPiotr Pawel Stefaniak 					    infile);
245*47bcbde9SPiotr Pawel Stefaniak 					return (1);
246*47bcbde9SPiotr Pawel Stefaniak 				}
247*47bcbde9SPiotr Pawel Stefaniak 				p = memmove(buf + n, p, m);
248*47bcbde9SPiotr Pawel Stefaniak 			}
249*47bcbde9SPiotr Pawel Stefaniak 			q = memcpy(p - n, pw->pw_dir, n);
250*47bcbde9SPiotr Pawel Stefaniak 		}
251*47bcbde9SPiotr Pawel Stefaniak 	} else {
252*47bcbde9SPiotr Pawel Stefaniak 		/* strip down to leaf name */
253*47bcbde9SPiotr Pawel Stefaniak 		p = strrchr(q, '/');
254*47bcbde9SPiotr Pawel Stefaniak 		if (p != NULL)
255*47bcbde9SPiotr Pawel Stefaniak 			q = p + 1;
256*47bcbde9SPiotr Pawel Stefaniak 	}
257*47bcbde9SPiotr Pawel Stefaniak 	if (!oflag)
258*47bcbde9SPiotr Pawel Stefaniak 		outfile = q;
259*47bcbde9SPiotr Pawel Stefaniak 
260*47bcbde9SPiotr Pawel Stefaniak 	/* POSIX says "/dev/stdout" is a 'magic cookie' not a special file. */
261*47bcbde9SPiotr Pawel Stefaniak 	if (pflag || strcmp(outfile, "/dev/stdout") == 0)
262*47bcbde9SPiotr Pawel Stefaniak 		outfp = stdout;
263*47bcbde9SPiotr Pawel Stefaniak 	else {
264*47bcbde9SPiotr Pawel Stefaniak 		flags = O_WRONLY | O_CREAT | O_EXCL;
265*47bcbde9SPiotr Pawel Stefaniak 		if (lstat(outfile, &st) == 0) {
266*47bcbde9SPiotr Pawel Stefaniak 			if (iflag) {
267*47bcbde9SPiotr Pawel Stefaniak 				warnc(EEXIST, "%s: %s", infile, outfile);
268*47bcbde9SPiotr Pawel Stefaniak 				return (0);
269*47bcbde9SPiotr Pawel Stefaniak 			}
270*47bcbde9SPiotr Pawel Stefaniak 			switch (st.st_mode & S_IFMT) {
271*47bcbde9SPiotr Pawel Stefaniak 			case S_IFREG:
272*47bcbde9SPiotr Pawel Stefaniak 			case S_IFLNK:
273*47bcbde9SPiotr Pawel Stefaniak 				/* avoid symlink attacks */
274*47bcbde9SPiotr Pawel Stefaniak 				if (unlink(outfile) == 0 || errno == ENOENT)
275*47bcbde9SPiotr Pawel Stefaniak 					break;
276*47bcbde9SPiotr Pawel Stefaniak 				warn("%s: unlink %s", infile, outfile);
277*47bcbde9SPiotr Pawel Stefaniak 				return (1);
278*47bcbde9SPiotr Pawel Stefaniak 			case S_IFDIR:
279*47bcbde9SPiotr Pawel Stefaniak 				warnc(EISDIR, "%s: %s", infile, outfile);
280*47bcbde9SPiotr Pawel Stefaniak 				return (1);
281*47bcbde9SPiotr Pawel Stefaniak 			default:
282*47bcbde9SPiotr Pawel Stefaniak 				if (oflag) {
283*47bcbde9SPiotr Pawel Stefaniak 					/* trust command-line names */
284*47bcbde9SPiotr Pawel Stefaniak 					flags &= ~O_EXCL;
285*47bcbde9SPiotr Pawel Stefaniak 					break;
286*47bcbde9SPiotr Pawel Stefaniak 				}
287*47bcbde9SPiotr Pawel Stefaniak 				warnc(EEXIST, "%s: %s", infile, outfile);
288*47bcbde9SPiotr Pawel Stefaniak 				return (1);
289*47bcbde9SPiotr Pawel Stefaniak 			}
290*47bcbde9SPiotr Pawel Stefaniak 		} else if (errno != ENOENT) {
291*47bcbde9SPiotr Pawel Stefaniak 			warn("%s: %s", infile, outfile);
292*47bcbde9SPiotr Pawel Stefaniak 			return (1);
293*47bcbde9SPiotr Pawel Stefaniak 		}
294*47bcbde9SPiotr Pawel Stefaniak 		if ((fd = open(outfile, flags, mode)) < 0 ||
295*47bcbde9SPiotr Pawel Stefaniak 		    (outfp = fdopen(fd, "w")) == NULL) {
296*47bcbde9SPiotr Pawel Stefaniak 			warn("%s: %s", infile, outfile);
297*47bcbde9SPiotr Pawel Stefaniak 			return (1);
298*47bcbde9SPiotr Pawel Stefaniak 		}
299*47bcbde9SPiotr Pawel Stefaniak 	}
300*47bcbde9SPiotr Pawel Stefaniak 
301*47bcbde9SPiotr Pawel Stefaniak 	if (base64)
302*47bcbde9SPiotr Pawel Stefaniak 		return (base64_decode());
303*47bcbde9SPiotr Pawel Stefaniak 	else
304*47bcbde9SPiotr Pawel Stefaniak 		return (uu_decode());
305*47bcbde9SPiotr Pawel Stefaniak }
306*47bcbde9SPiotr Pawel Stefaniak 
307*47bcbde9SPiotr Pawel Stefaniak static int
308*47bcbde9SPiotr Pawel Stefaniak get_line(char *buf, size_t size)
309*47bcbde9SPiotr Pawel Stefaniak {
310*47bcbde9SPiotr Pawel Stefaniak 
311*47bcbde9SPiotr Pawel Stefaniak 	if (fgets(buf, size, infp) != NULL)
312*47bcbde9SPiotr Pawel Stefaniak 		return (2);
313*47bcbde9SPiotr Pawel Stefaniak 	if (rflag)
314*47bcbde9SPiotr Pawel Stefaniak 		return (0);
315*47bcbde9SPiotr Pawel Stefaniak 	warnx("%s: %s: short file", infile, outfile);
316*47bcbde9SPiotr Pawel Stefaniak 	return (1);
317*47bcbde9SPiotr Pawel Stefaniak }
318*47bcbde9SPiotr Pawel Stefaniak 
319*47bcbde9SPiotr Pawel Stefaniak static int
320*47bcbde9SPiotr Pawel Stefaniak checkend(const char *ptr, const char *end, const char *msg)
321*47bcbde9SPiotr Pawel Stefaniak {
322*47bcbde9SPiotr Pawel Stefaniak 	size_t n;
323*47bcbde9SPiotr Pawel Stefaniak 
324*47bcbde9SPiotr Pawel Stefaniak 	n = strlen(end);
325*47bcbde9SPiotr Pawel Stefaniak 	if (strncmp(ptr, end, n) != 0 ||
326*47bcbde9SPiotr Pawel Stefaniak 	    strspn(ptr + n, " \t\r\n") != strlen(ptr + n)) {
327*47bcbde9SPiotr Pawel Stefaniak 		warnx("%s: %s: %s", infile, outfile, msg);
328*47bcbde9SPiotr Pawel Stefaniak 		return (1);
329*47bcbde9SPiotr Pawel Stefaniak 	}
330*47bcbde9SPiotr Pawel Stefaniak 	if (fclose(outfp) != 0) {
331*47bcbde9SPiotr Pawel Stefaniak 		warn("%s: %s", infile, outfile);
332*47bcbde9SPiotr Pawel Stefaniak 		return (1);
333*47bcbde9SPiotr Pawel Stefaniak 	}
334*47bcbde9SPiotr Pawel Stefaniak 	return (0);
335*47bcbde9SPiotr Pawel Stefaniak }
336*47bcbde9SPiotr Pawel Stefaniak 
337*47bcbde9SPiotr Pawel Stefaniak static int
338*47bcbde9SPiotr Pawel Stefaniak uu_decode(void)
339*47bcbde9SPiotr Pawel Stefaniak {
340*47bcbde9SPiotr Pawel Stefaniak 	int i, ch;
341*47bcbde9SPiotr Pawel Stefaniak 	char *p;
342*47bcbde9SPiotr Pawel Stefaniak 	char buf[MAXPATHLEN+1];
343*47bcbde9SPiotr Pawel Stefaniak 
344*47bcbde9SPiotr Pawel Stefaniak 	/* for each input line */
345*47bcbde9SPiotr Pawel Stefaniak 	for (;;) {
346*47bcbde9SPiotr Pawel Stefaniak 		switch (get_line(buf, sizeof(buf))) {
347*47bcbde9SPiotr Pawel Stefaniak 		case 0:
348*47bcbde9SPiotr Pawel Stefaniak 			return (0);
349*47bcbde9SPiotr Pawel Stefaniak 		case 1:
350*47bcbde9SPiotr Pawel Stefaniak 			return (1);
351*47bcbde9SPiotr Pawel Stefaniak 		}
352*47bcbde9SPiotr Pawel Stefaniak 
353*47bcbde9SPiotr Pawel Stefaniak #define	DEC(c)		(((c) - ' ') & 077)	/* single character decode */
354*47bcbde9SPiotr Pawel Stefaniak #define IS_DEC(c)	 ( (((c) - ' ') >= 0) && (((c) - ' ') <= 077 + 1) )
355*47bcbde9SPiotr Pawel Stefaniak 
356*47bcbde9SPiotr Pawel Stefaniak #define OUT_OF_RANGE do {						\
357*47bcbde9SPiotr Pawel Stefaniak 	warnx("%s: %s: character out of range: [%d-%d]",		\
358*47bcbde9SPiotr Pawel Stefaniak 	    infile, outfile, ' ', 077 + ' ' + 1);			\
359*47bcbde9SPiotr Pawel Stefaniak 	return (1);							\
360*47bcbde9SPiotr Pawel Stefaniak } while (0)
361*47bcbde9SPiotr Pawel Stefaniak 
362*47bcbde9SPiotr Pawel Stefaniak 		/*
363*47bcbde9SPiotr Pawel Stefaniak 		 * `i' is used to avoid writing out all the characters
364*47bcbde9SPiotr Pawel Stefaniak 		 * at the end of the file.
365*47bcbde9SPiotr Pawel Stefaniak 		 */
366*47bcbde9SPiotr Pawel Stefaniak 		p = buf;
367*47bcbde9SPiotr Pawel Stefaniak 		if ((i = DEC(*p)) <= 0)
368*47bcbde9SPiotr Pawel Stefaniak 			break;
369*47bcbde9SPiotr Pawel Stefaniak 		for (++p; i > 0; p += 4, i -= 3)
370*47bcbde9SPiotr Pawel Stefaniak 			if (i >= 3) {
371*47bcbde9SPiotr Pawel Stefaniak 				if (!(IS_DEC(*p) && IS_DEC(*(p + 1)) &&
372*47bcbde9SPiotr Pawel Stefaniak 				    IS_DEC(*(p + 2)) && IS_DEC(*(p + 3))))
373*47bcbde9SPiotr Pawel Stefaniak 					OUT_OF_RANGE;
374*47bcbde9SPiotr Pawel Stefaniak 
375*47bcbde9SPiotr Pawel Stefaniak 				ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
376*47bcbde9SPiotr Pawel Stefaniak 				putc(ch, outfp);
377*47bcbde9SPiotr Pawel Stefaniak 				ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
378*47bcbde9SPiotr Pawel Stefaniak 				putc(ch, outfp);
379*47bcbde9SPiotr Pawel Stefaniak 				ch = DEC(p[2]) << 6 | DEC(p[3]);
380*47bcbde9SPiotr Pawel Stefaniak 				putc(ch, outfp);
381*47bcbde9SPiotr Pawel Stefaniak 			} else {
382*47bcbde9SPiotr Pawel Stefaniak 				if (i >= 1) {
383*47bcbde9SPiotr Pawel Stefaniak 					if (!(IS_DEC(*p) && IS_DEC(*(p + 1))))
384*47bcbde9SPiotr Pawel Stefaniak 						OUT_OF_RANGE;
385*47bcbde9SPiotr Pawel Stefaniak 					ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
386*47bcbde9SPiotr Pawel Stefaniak 					putc(ch, outfp);
387*47bcbde9SPiotr Pawel Stefaniak 				}
388*47bcbde9SPiotr Pawel Stefaniak 				if (i >= 2) {
389*47bcbde9SPiotr Pawel Stefaniak 					if (!(IS_DEC(*(p + 1)) &&
390*47bcbde9SPiotr Pawel Stefaniak 					    IS_DEC(*(p + 2))))
391*47bcbde9SPiotr Pawel Stefaniak 						OUT_OF_RANGE;
392*47bcbde9SPiotr Pawel Stefaniak 
393*47bcbde9SPiotr Pawel Stefaniak 					ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
394*47bcbde9SPiotr Pawel Stefaniak 					putc(ch, outfp);
395*47bcbde9SPiotr Pawel Stefaniak 				}
396*47bcbde9SPiotr Pawel Stefaniak 				if (i >= 3) {
397*47bcbde9SPiotr Pawel Stefaniak 					if (!(IS_DEC(*(p + 2)) &&
398*47bcbde9SPiotr Pawel Stefaniak 					    IS_DEC(*(p + 3))))
399*47bcbde9SPiotr Pawel Stefaniak 						OUT_OF_RANGE;
400*47bcbde9SPiotr Pawel Stefaniak 					ch = DEC(p[2]) << 6 | DEC(p[3]);
401*47bcbde9SPiotr Pawel Stefaniak 					putc(ch, outfp);
402*47bcbde9SPiotr Pawel Stefaniak 				}
403*47bcbde9SPiotr Pawel Stefaniak 			}
404*47bcbde9SPiotr Pawel Stefaniak 	}
405*47bcbde9SPiotr Pawel Stefaniak 	switch (get_line(buf, sizeof(buf))) {
406*47bcbde9SPiotr Pawel Stefaniak 	case 0:
407*47bcbde9SPiotr Pawel Stefaniak 		return (0);
408*47bcbde9SPiotr Pawel Stefaniak 	case 1:
409*47bcbde9SPiotr Pawel Stefaniak 		return (1);
410*47bcbde9SPiotr Pawel Stefaniak 	default:
411*47bcbde9SPiotr Pawel Stefaniak 		return (checkend(buf, "end", "no \"end\" line"));
412*47bcbde9SPiotr Pawel Stefaniak 	}
413*47bcbde9SPiotr Pawel Stefaniak }
414*47bcbde9SPiotr Pawel Stefaniak 
415*47bcbde9SPiotr Pawel Stefaniak static int
416*47bcbde9SPiotr Pawel Stefaniak base64_decode(void)
417*47bcbde9SPiotr Pawel Stefaniak {
418*47bcbde9SPiotr Pawel Stefaniak 	int n, count, count4;
419*47bcbde9SPiotr Pawel Stefaniak 	char inbuf[MAXPATHLEN + 1], *p;
420*47bcbde9SPiotr Pawel Stefaniak 	unsigned char outbuf[MAXPATHLEN * 4];
421*47bcbde9SPiotr Pawel Stefaniak 	char leftover[MAXPATHLEN + 1];
422*47bcbde9SPiotr Pawel Stefaniak 
423*47bcbde9SPiotr Pawel Stefaniak 	leftover[0] = '\0';
424*47bcbde9SPiotr Pawel Stefaniak 	for (;;) {
425*47bcbde9SPiotr Pawel Stefaniak 		strcpy(inbuf, leftover);
426*47bcbde9SPiotr Pawel Stefaniak 		switch (get_line(inbuf + strlen(inbuf),
427*47bcbde9SPiotr Pawel Stefaniak 		    sizeof(inbuf) - strlen(inbuf))) {
428*47bcbde9SPiotr Pawel Stefaniak 		case 0:
429*47bcbde9SPiotr Pawel Stefaniak 			return (0);
430*47bcbde9SPiotr Pawel Stefaniak 		case 1:
431*47bcbde9SPiotr Pawel Stefaniak 			return (1);
432*47bcbde9SPiotr Pawel Stefaniak 		}
433*47bcbde9SPiotr Pawel Stefaniak 
434*47bcbde9SPiotr Pawel Stefaniak 		count = 0;
435*47bcbde9SPiotr Pawel Stefaniak 		count4 = -1;
436*47bcbde9SPiotr Pawel Stefaniak 		p = inbuf;
437*47bcbde9SPiotr Pawel Stefaniak 		while (*p != '\0') {
438*47bcbde9SPiotr Pawel Stefaniak 			/*
439*47bcbde9SPiotr Pawel Stefaniak 			 * Base64 encoded strings have the following
440*47bcbde9SPiotr Pawel Stefaniak 			 * characters in them: A-Z, a-z, 0-9 and +, / and =
441*47bcbde9SPiotr Pawel Stefaniak 			 */
442*47bcbde9SPiotr Pawel Stefaniak 			if (isalnum(*p) || *p == '+' || *p == '/' || *p == '=')
443*47bcbde9SPiotr Pawel Stefaniak 				count++;
444*47bcbde9SPiotr Pawel Stefaniak 			if (count % 4 == 0)
445*47bcbde9SPiotr Pawel Stefaniak 				count4 = p - inbuf;
446*47bcbde9SPiotr Pawel Stefaniak 			p++;
447*47bcbde9SPiotr Pawel Stefaniak 		}
448*47bcbde9SPiotr Pawel Stefaniak 
449*47bcbde9SPiotr Pawel Stefaniak 		strcpy(leftover, inbuf + count4 + 1);
450*47bcbde9SPiotr Pawel Stefaniak 		inbuf[count4 + 1] = 0;
451*47bcbde9SPiotr Pawel Stefaniak 
452*47bcbde9SPiotr Pawel Stefaniak 		n = b64_pton(inbuf, outbuf, sizeof(outbuf));
453*47bcbde9SPiotr Pawel Stefaniak 
454*47bcbde9SPiotr Pawel Stefaniak 		if (n < 0)
455*47bcbde9SPiotr Pawel Stefaniak 			break;
456*47bcbde9SPiotr Pawel Stefaniak 		fwrite(outbuf, 1, n, outfp);
457*47bcbde9SPiotr Pawel Stefaniak 	}
458*47bcbde9SPiotr Pawel Stefaniak 	return (checkend(inbuf, "====", "error decoding base64 input stream"));
459*47bcbde9SPiotr Pawel Stefaniak }
460*47bcbde9SPiotr Pawel Stefaniak 
461*47bcbde9SPiotr Pawel Stefaniak static void
462*47bcbde9SPiotr Pawel Stefaniak usage(void)
463*47bcbde9SPiotr Pawel Stefaniak {
464*47bcbde9SPiotr Pawel Stefaniak 
465*47bcbde9SPiotr Pawel Stefaniak 	(void)fprintf(stderr,
466*47bcbde9SPiotr Pawel Stefaniak 	    "usage: uudecode [-cimprs] [file ...]\n"
467*47bcbde9SPiotr Pawel Stefaniak 	    "       uudecode [-i] -o output_file [file]\n"
468*47bcbde9SPiotr Pawel Stefaniak 	    "       b64decode [-cimprs] [file ...]\n"
469*47bcbde9SPiotr Pawel Stefaniak 	    "       b64decode [-i] -o output_file [file]\n");
470*47bcbde9SPiotr Pawel Stefaniak 	exit(1);
471*47bcbde9SPiotr Pawel Stefaniak }
472