xref: /freebsd/usr.bin/bintrans/qp.c (revision 2ba1d4970a06a1660b46f6fd99351d154b295683)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2020 Baptiste Daroussin <bapt@FreeBSD.org>
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 AUTHOR 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 <ctype.h>
29 #include <stdbool.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <stdlib.h>
33 
34 extern int main_quotedprintable(int, char *[]);
35 
36 static int
37 hexval(int c)
38 {
39 	if ('0' <= c && c <= '9')
40 		return c - '0';
41 	return (10 + c - 'A');
42 }
43 
44 
45 static int
46 decode_char(const char *s)
47 {
48 	return (16 * hexval(toupper(s[1])) + hexval(toupper(s[2])));
49 }
50 
51 
52 static void
53 decode_quoted_printable(const char *body, FILE *fpo)
54 {
55 	while (*body != '\0') {
56 		switch (*body) {
57 		case '=':
58 			if (strlen(body) < 2) {
59 				fputc(*body, fpo);
60 				break;
61 			}
62 
63 			if (body[1] == '\r' && body[2] == '\n') {
64 				body += 2;
65 				break;
66 			}
67 			if (body[1] == '\n') {
68 				body++;
69 				break;
70 			}
71 			if (strchr("0123456789ABCDEFabcdef", body[1]) == NULL) {
72 				fputc(*body, fpo);
73 				break;
74 			}
75 			if (strchr("0123456789ABCDEFabcdef", body[2]) == NULL) {
76 				fputc(*body, fpo);
77 				break;
78 			}
79 			fputc(decode_char(body), fpo);
80 			body += 2;
81 			break;
82 		default:
83 			fputc(*body, fpo);
84 			break;
85 		}
86 		body++;
87 	}
88 }
89 
90 static void
91 encode_quoted_printable(const char *body, FILE *fpo)
92 {
93 	char prev;
94 	const char *end = body + strlen(body);
95 	size_t linelen = 0;
96 
97 	while (*body != '\0') {
98 		if (linelen == 75) {
99 			fputs("=\r\n", fpo);
100 			linelen = 0;
101 		}
102 		if (!isascii(*body) ||
103 		    *body == '=' ||
104 		    (*body == '.' && body + 1 < end &&
105 		      (body[1] == '\n' || body[1] == '\r'))) {
106 			fprintf(fpo, "=%02X", (unsigned char)*body);
107 			linelen += 2;
108 			prev = *body;
109 		} else if (*body < 33 && *body != '\n') {
110 			if ((*body == ' ' || *body == '\t') &&
111 			    body + 1 < end &&
112 			    (body[1] != '\n' && body[1] != '\r')) {
113 				fputc(*body, fpo);
114 				prev = *body;
115 			} else {
116 				fprintf(fpo, "=%02X", (unsigned char)*body);
117 				linelen += 2;
118 				prev = '_';
119 			}
120 		} else if (*body == '\n') {
121 			if (prev == ' ' || prev == '\t') {
122 				fputc('=', fpo);
123 			}
124 			fputc('\n', fpo);
125 			linelen = 0;
126 			prev = 0;
127 		} else {
128 			fputc(*body, fpo);
129 			prev = *body;
130 		}
131 		body++;
132 		linelen++;
133 	}
134 }
135 
136 static void
137 qp(FILE *fp, FILE *fpo, bool encode)
138 {
139 	char *line = NULL;
140 	size_t linecap = 0;
141 	ssize_t linelen;
142 	void (*codec)(const char *line, FILE *f);
143 
144 	codec = encode ? encode_quoted_printable : decode_quoted_printable ;
145 
146 	while ((linelen = getline(&line, &linecap, fp)) > 0)
147 		codec(line, fpo);
148 	free(line);
149 }
150 
151 static void
152 usage(void)
153 {
154 	fprintf(stderr,
155 	   "usage: bintrans qp [-u] [-o outputfile] [file name]\n");
156 }
157 
158 int
159 main_quotedprintable(int argc, char *argv[])
160 {
161 	int i;
162 	bool encode = true;
163 	FILE *fp = stdin;
164 	FILE *fpo = stdout;
165 
166 	for (i = 1; i < argc; ++i) {
167 		if (argv[i][0] == '-') {
168 			switch (argv[i][1]) {
169 			case 'o':
170 				if (++i >= argc) {
171 					fprintf(stderr, "qp: -o requires a file name.\n");
172 					exit(EXIT_FAILURE);
173 				}
174 				fpo = fopen(argv[i], "w");
175 				if (fpo == NULL) {
176 					perror(argv[i]);
177 					exit(EXIT_FAILURE);
178 				}
179 				break;
180 			case 'u':
181 				encode = false;
182 				break;
183 			default:
184 				usage();
185 				exit(EXIT_FAILURE);
186 			}
187 		} else {
188 			fp = fopen(argv[i], "r");
189 			if (fp == NULL) {
190 				perror(argv[i]);
191 				exit(EXIT_FAILURE);
192 			}
193 		}
194 	}
195 	qp(fp, fpo, encode);
196 
197 	return (EXIT_SUCCESS);
198 }
199