]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/bintrans/qp.c
zfs: merge openzfs/zfs@d96e29576
[FreeBSD/FreeBSD.git] / usr.bin / bintrans / qp.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 }