1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2022 The FreeBSD Foundation
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/param.h>
29#include <getopt.h>
30#include <libgen.h>
31#include <stdbool.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <sysexits.h>
36
37extern int	main_decode(int, char *[]);
38extern int	main_encode(int, char *[]);
39extern int	main_base64_decode(const char *);
40extern int	main_base64_encode(const char *, const char *);
41extern int	main_quotedprintable(int, char*[]);
42
43static int	search(const char *const);
44static void	usage_base64(bool);
45static void	version_base64(void);
46static void	base64_encode_or_decode(int, char *[]);
47
48enum coders {
49	uuencode, uudecode, b64encode, b64decode, base64, qp
50};
51
52int
53main(int argc, char *argv[])
54{
55	const char *const progname = getprogname();
56	int coder = search(progname);
57
58	if (coder == -1 && argc > 1) {
59		argc--;
60		argv++;
61		coder = search(argv[0]);
62	}
63	switch (coder) {
64	case uuencode:
65	case b64encode:
66		main_encode(argc, argv);
67		break;
68	case uudecode:
69	case b64decode:
70		main_decode(argc, argv);
71		break;
72	case base64:
73		base64_encode_or_decode(argc, argv);
74		break;
75	case qp:
76		main_quotedprintable(argc, argv);
77		break;
78	default:
79		(void)fprintf(stderr,
80		    "usage: %1$s <uuencode | uudecode> ...\n"
81		    "       %1$s <b64encode | b64decode> ...\n"
82		    "       %1$s <base64> ...\n"
83		    "       %1$s <qp> ...\n",
84		    progname);
85		exit(EX_USAGE);
86	}
87}
88
89static int
90search(const char *const progname)
91{
92#define DESIGNATE(item) [item] = #item
93	const char *const known[] = {
94		DESIGNATE(uuencode),
95		DESIGNATE(uudecode),
96		DESIGNATE(b64encode),
97		DESIGNATE(b64decode),
98		DESIGNATE(base64),
99		DESIGNATE(qp)
100	};
101
102	for (size_t i = 0; i < nitems(known); i++)
103		if (strcmp(progname, known[i]) == 0)
104			return ((int)i);
105	return (-1);
106}
107
108static void
109usage_base64(bool failure)
110{
111	(void)fputs("usage: base64 [-w col | --wrap=col] "
112	    "[-d | --decode] [FILE]\n"
113	    "       base64 --help\n"
114	    "       base64 --version\n", stderr);
115	exit(failure ? EXIT_FAILURE : EXIT_SUCCESS);
116}
117
118static void
119version_base64(void)
120{
121	(void)fputs("FreeBSD base64\n", stderr);
122	exit(EXIT_SUCCESS);
123}
124
125static void
126base64_encode_or_decode(int argc, char *argv[])
127{
128	int ch;
129	bool decode = false;
130	const char *w = NULL;
131	enum { HELP, VERSION };
132	static const struct option opts[] =
133	{
134		{"decode",	no_argument,		NULL, 'd'},
135		{"ignore-garbage",no_argument,		NULL, 'i'},
136		{"wrap",	required_argument,	NULL, 'w'},
137		{"help",	no_argument,		NULL, HELP},
138		{"version",	no_argument,		NULL, VERSION},
139		{NULL,		no_argument,		NULL, 0}
140	};
141
142	while ((ch = getopt_long(argc, argv, "diw:", opts, NULL)) != -1)
143		switch (ch) {
144		case 'd':
145			decode = true;
146			break;
147		case 'w':
148			w = optarg;
149			break;
150		case 'i':
151			/* silently ignore */
152			break;
153		case VERSION:
154			version_base64();
155			break;
156		case HELP:
157		default:
158			usage_base64(ch == '?');
159		}
160
161	if (decode)
162		main_base64_decode(argv[optind]);
163	else
164		main_base64_encode(argv[optind], w);
165}
166