xref: /freebsd/usr.bin/bintrans/uuencode.c (revision cc1a53bc1aea0675d64e9547cdca241612906592)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1983, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #if 0
33 #ifndef lint
34 static const char copyright[] =
35 "@(#) Copyright (c) 1983, 1993\n\
36 	The Regents of the University of California.  All rights reserved.\n";
37 #endif /* not lint */
38 
39 #ifndef lint
40 static char sccsid[] = "@(#)uuencode.c	8.2 (Berkeley) 4/2/94";
41 #endif /* not lint */
42 #endif
43 #include <sys/cdefs.h>
44 __FBSDID("$FreeBSD$");
45 
46 /*
47  * uuencode [input] output
48  *
49  * Encode a file so it can be mailed to a remote system.
50  */
51 #include <sys/param.h>
52 #include <sys/socket.h>
53 #include <sys/stat.h>
54 
55 #include <netinet/in.h>
56 
57 #include <err.h>
58 #include <errno.h>
59 #include <libgen.h>
60 #include <resolv.h>
61 #include <stdio.h>
62 #include <stdbool.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <unistd.h>
66 
67 extern int main_encode(int, char *[]);
68 extern int main_base64_encode(const char *, const char *);
69 
70 static void encode(void);
71 static void base64_encode(void);
72 static int arg_to_col(const char *);
73 static void usage(void);
74 
75 static FILE *output;
76 static int mode;
77 static bool raw;
78 static char **av;
79 static int columns = 76;
80 
81 int
82 main_base64_encode(const char *in, const char *w)
83 {
84 	raw = 1;
85 	if (in != NULL && freopen(in, "r", stdin) == NULL)
86 		err(1, "%s", in);
87 	output = stdout;
88 	if (w != NULL)
89 		columns = arg_to_col(w);
90 	base64_encode();
91 	if (ferror(output))
92 		errx(1, "write error");
93 	exit(0);
94 }
95 
96 int
97 main_encode(int argc, char *argv[])
98 {
99 	struct stat sb;
100 	bool base64;
101 	int ch;
102 	const char *outfile;
103 
104 	base64 = false;
105 	outfile = NULL;
106 
107 	if (strcmp(basename(argv[0]), "b64encode") == 0)
108 		base64 = 1;
109 
110 	while ((ch = getopt(argc, argv, "mo:rw:")) != -1) {
111 		switch (ch) {
112 		case 'm':
113 			base64 = true;
114 			break;
115 		case 'o':
116 			outfile = optarg;
117 			break;
118 		case 'r':
119 			raw = true;
120 			break;
121 		case 'w':
122 			columns = arg_to_col(optarg);
123 			break;
124 		case '?':
125 		default:
126 			usage();
127 		}
128 	}
129 	argv += optind;
130 	argc -= optind;
131 
132 	switch (argc) {
133 	case 2:			/* optional first argument is input file */
134 		if (!freopen(*argv, "r", stdin) || fstat(fileno(stdin), &sb))
135 			err(1, "%s", *argv);
136 #define	RWX	(S_IRWXU|S_IRWXG|S_IRWXO)
137 		mode = sb.st_mode & RWX;
138 		++argv;
139 		break;
140 	case 1:
141 #define	RW	(S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
142 		mode = RW & ~umask(RW);
143 		break;
144 	case 0:
145 	default:
146 		usage();
147 	}
148 
149 	av = argv;
150 
151 	if (outfile != NULL) {
152 		output = fopen(outfile, "w+");
153 		if (output == NULL)
154 			err(1, "unable to open %s for output", outfile);
155 	} else
156 		output = stdout;
157 	if (base64)
158 		base64_encode();
159 	else
160 		encode();
161 	if (ferror(output))
162 		errx(1, "write error");
163 	exit(0);
164 }
165 
166 /* ENC is the basic 1 character encoding function to make a char printing */
167 #define	ENC(c) ((c) ? ((c) & 077) + ' ': '`')
168 
169 /*
170  * Copy from in to out, encoding in base64 as you go along.
171  */
172 static void
173 base64_encode(void)
174 {
175 	/*
176 	 * This buffer's length should be a multiple of 24 bits to avoid "="
177 	 * padding. Once it reached ~1 KB, further expansion didn't improve
178 	 * performance for me.
179 	 */
180 	unsigned char buf[1023];
181 	char buf2[sizeof(buf) * 2 + 1];
182 	size_t n;
183 	unsigned carry = 0;
184 	int rv, written;
185 
186 	if (!raw)
187 		fprintf(output, "begin-base64 %o %s\n", mode, *av);
188 	while ((n = fread(buf, 1, sizeof(buf), stdin))) {
189 		rv = b64_ntop(buf, n, buf2, nitems(buf2));
190 		if (rv == -1)
191 			errx(1, "b64_ntop: error encoding base64");
192 		if (columns == 0) {
193 			fputs(buf2, output);
194 			continue;
195 		}
196 		for (int i = 0; i < rv; i += written) {
197 			written = fprintf(output, "%.*s", columns - carry,
198 			    &buf2[i]);
199 
200 			carry = (carry + written) % columns;
201 			if (carry == 0)
202 				fputc('\n', output);
203 		}
204 	}
205 	if (columns == 0 || carry != 0)
206 		fputc('\n', output);
207 	if (!raw)
208 		fprintf(output, "====\n");
209 }
210 
211 /*
212  * Copy from in to out, encoding as you go along.
213  */
214 static void
215 encode(void)
216 {
217 	int ch, n;
218 	char *p;
219 	char buf[80];
220 
221 	if (!raw)
222 		(void)fprintf(output, "begin %o %s\n", mode, *av);
223 	while ((n = fread(buf, 1, 45, stdin))) {
224 		ch = ENC(n);
225 		if (fputc(ch, output) == EOF)
226 			break;
227 		for (p = buf; n > 0; n -= 3, p += 3) {
228 			/* Pad with nulls if not a multiple of 3. */
229 			if (n < 3) {
230 				p[2] = '\0';
231 				if (n < 2)
232 					p[1] = '\0';
233 			}
234 			ch = *p >> 2;
235 			ch = ENC(ch);
236 			if (fputc(ch, output) == EOF)
237 				break;
238 			ch = ((*p << 4) & 060) | ((p[1] >> 4) & 017);
239 			ch = ENC(ch);
240 			if (fputc(ch, output) == EOF)
241 				break;
242 			ch = ((p[1] << 2) & 074) | ((p[2] >> 6) & 03);
243 			ch = ENC(ch);
244 			if (fputc(ch, output) == EOF)
245 				break;
246 			ch = p[2] & 077;
247 			ch = ENC(ch);
248 			if (fputc(ch, output) == EOF)
249 				break;
250 		}
251 		if (fputc('\n', output) == EOF)
252 			break;
253 	}
254 	if (ferror(stdin))
255 		errx(1, "read error");
256 	if (!raw)
257 		(void)fprintf(output, "%c\nend\n", ENC('\0'));
258 }
259 
260 static int
261 arg_to_col(const char *w)
262 {
263 	char *ep;
264 	long option;
265 
266 	errno = 0;
267 	option = strtol(w, &ep, 10);
268 	if (option > INT_MAX)
269 		errno = ERANGE;
270 	else if (ep[0] != '\0')
271 		errno = EINVAL;
272 	if (errno != 0)
273 		err(2, NULL);
274 
275 	if (option < 0) {
276 		errno = EINVAL;
277 		err(2, "columns argument must be non-negative");
278 	}
279 	return (option);
280 }
281 
282 static void
283 usage(void)
284 {
285 	(void)fprintf(stderr,
286 "usage: uuencode [-m] [-o outfile] [infile] remotefile\n"
287 "       b64encode [-o outfile] [infile] remotefile\n");
288 	exit(1);
289 }
290