xref: /netbsd-src/lib/libc/gen/vis.c (revision b5677b36047b601b9addaaa494a58ceae82c2a6c)
1 /*	$NetBSD: vis.c,v 1.40 2009/02/11 13:52:28 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 1989, 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 /*-
33  * Copyright (c) 1999, 2005 The NetBSD Foundation, Inc.
34  * All rights reserved.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  *
45  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
46  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
47  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
48  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
49  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
50  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
51  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
52  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
53  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
54  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
55  * POSSIBILITY OF SUCH DAMAGE.
56  */
57 
58 #include <sys/cdefs.h>
59 #if defined(LIBC_SCCS) && !defined(lint)
60 __RCSID("$NetBSD: vis.c,v 1.40 2009/02/11 13:52:28 christos Exp $");
61 #endif /* LIBC_SCCS and not lint */
62 
63 #include "namespace.h"
64 #include <sys/types.h>
65 
66 #include <assert.h>
67 #include <vis.h>
68 #include <stdlib.h>
69 
70 #ifdef __weak_alias
71 __weak_alias(strsvis,_strsvis)
72 __weak_alias(strsvisx,_strsvisx)
73 __weak_alias(strvis,_strvis)
74 __weak_alias(strvisx,_strvisx)
75 __weak_alias(svis,_svis)
76 __weak_alias(vis,_vis)
77 #endif
78 
79 #if !HAVE_VIS || !HAVE_SVIS
80 #include <ctype.h>
81 #include <limits.h>
82 #include <stdio.h>
83 #include <string.h>
84 
85 static char *do_svis(char *, int, int, int, const char *);
86 
87 #undef BELL
88 #define BELL '\a'
89 
90 #define isoctal(c)	(((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
91 #define iswhite(c)	(c == ' ' || c == '\t' || c == '\n')
92 #define issafe(c)	(c == '\b' || c == BELL || c == '\r')
93 #define xtoa(c)		"0123456789abcdef"[c]
94 #define XTOA(c)		"0123456789ABCDEF"[c]
95 
96 #define MAXEXTRAS	5
97 
98 #define MAKEEXTRALIST(flag, extra, orig_str)				      \
99 do {									      \
100 	const char *orig = orig_str;					      \
101 	const char *o = orig;						      \
102 	char *e;							      \
103 	while (*o++)							      \
104 		continue;						      \
105 	extra = malloc((size_t)((o - orig) + MAXEXTRAS));		      \
106 	if (!extra) break;						      \
107 	for (o = orig, e = extra; (*e++ = *o++) != '\0';)		      \
108 		continue;						      \
109 	e--;								      \
110 	if (flag & VIS_SP) *e++ = ' ';					      \
111 	if (flag & VIS_TAB) *e++ = '\t';				      \
112 	if (flag & VIS_NL) *e++ = '\n';					      \
113 	if ((flag & VIS_NOSLASH) == 0) *e++ = '\\';			      \
114 	*e = '\0';							      \
115 } while (/*CONSTCOND*/0)
116 
117 /*
118  * This is do_hvis, for HTTP style (RFC 1808)
119  */
120 static char *
121 do_hvis(char *dst, int c, int flag, int nextc, const char *extra)
122 {
123 	if (!isascii(c) || !isalnum(c) || strchr("$-_.+!*'(),", c) != NULL) {
124 		*dst++ = '%';
125 		*dst++ = xtoa(((unsigned int)c >> 4) & 0xf);
126 		*dst++ = xtoa((unsigned int)c & 0xf);
127 	} else {
128 		dst = do_svis(dst, c, flag, nextc, extra);
129 	}
130 	return dst;
131 }
132 
133 /*
134  * This is do_mvis, for Quoted-Printable MIME (RFC 2045)
135  * NB: No handling of long lines or CRLF.
136  */
137 static char *
138 do_mvis(char *dst, int c, int flag, int nextc, const char *extra)
139 {
140 	if ((c != '\n') &&
141 	    /* Space at the end of the line */
142 	    ((isspace(c) && (nextc == '\r' || nextc == '\n')) ||
143 	    /* Out of range */
144 	    (!isspace(c) && (c < 33 || (c > 60 && c < 62) || c > 126)) ||
145 	    /* Specific char to be escaped */
146 	    strchr("#$@[\\]^`{|}~", c) != NULL)) {
147 		*dst++ = '=';
148 		*dst++ = XTOA(((unsigned int)c >> 4) & 0xf);
149 		*dst++ = XTOA((unsigned int)c & 0xf);
150 	} else {
151 		dst = do_svis(dst, c, flag, nextc, extra);
152 	}
153 	return dst;
154 }
155 
156 /*
157  * This is do_vis, the central code of vis.
158  * dst:	      Pointer to the destination buffer
159  * c:	      Character to encode
160  * flag:      Flag word
161  * nextc:     The character following 'c'
162  * extra:     Pointer to the list of extra characters to be
163  *	      backslash-protected.
164  */
165 static char *
166 do_svis(char *dst, int c, int flag, int nextc, const char *extra)
167 {
168 	int isextra;
169 	isextra = strchr(extra, c) != NULL;
170 	if (!isextra && isascii(c) && (isgraph(c) || iswhite(c) ||
171 	    ((flag & VIS_SAFE) && issafe(c)))) {
172 		*dst++ = c;
173 		return dst;
174 	}
175 	if (flag & VIS_CSTYLE) {
176 		switch (c) {
177 		case '\n':
178 			*dst++ = '\\'; *dst++ = 'n';
179 			return dst;
180 		case '\r':
181 			*dst++ = '\\'; *dst++ = 'r';
182 			return dst;
183 		case '\b':
184 			*dst++ = '\\'; *dst++ = 'b';
185 			return dst;
186 		case BELL:
187 			*dst++ = '\\'; *dst++ = 'a';
188 			return dst;
189 		case '\v':
190 			*dst++ = '\\'; *dst++ = 'v';
191 			return dst;
192 		case '\t':
193 			*dst++ = '\\'; *dst++ = 't';
194 			return dst;
195 		case '\f':
196 			*dst++ = '\\'; *dst++ = 'f';
197 			return dst;
198 		case ' ':
199 			*dst++ = '\\'; *dst++ = 's';
200 			return dst;
201 		case '\0':
202 			*dst++ = '\\'; *dst++ = '0';
203 			if (isoctal(nextc)) {
204 				*dst++ = '0';
205 				*dst++ = '0';
206 			}
207 			return dst;
208 		default:
209 			if (isgraph(c)) {
210 				*dst++ = '\\'; *dst++ = c;
211 				return dst;
212 			}
213 		}
214 	}
215 	if (isextra || ((c & 0177) == ' ') || (flag & VIS_OCTAL)) {
216 		*dst++ = '\\';
217 		*dst++ = (u_char)(((u_int32_t)(u_char)c >> 6) & 03) + '0';
218 		*dst++ = (u_char)(((u_int32_t)(u_char)c >> 3) & 07) + '0';
219 		*dst++ =			     (c	      & 07) + '0';
220 	} else {
221 		if ((flag & VIS_NOSLASH) == 0) *dst++ = '\\';
222 		if (c & 0200) {
223 			c &= 0177; *dst++ = 'M';
224 		}
225 		if (iscntrl(c)) {
226 			*dst++ = '^';
227 			if (c == 0177)
228 				*dst++ = '?';
229 			else
230 				*dst++ = c + '@';
231 		} else {
232 			*dst++ = '-'; *dst++ = c;
233 		}
234 	}
235 	return dst;
236 }
237 
238 typedef char *(*visfun_t)(char *, int, int, int, const char *);
239 
240 /*
241  * Return the appropriate encoding function depending on the flags given.
242  */
243 static visfun_t
244 getvisfun(int flag)
245 {
246 	if (flag & VIS_HTTPSTYLE)
247 		return do_hvis;
248 	if (flag & VIS_MIMESTYLE)
249 		return do_mvis;
250 	return do_svis;
251 }
252 
253 /*
254  * svis - visually encode characters, also encoding the characters
255  *	  pointed to by `extra'
256  */
257 char *
258 svis(char *dst, int c, int flag, int nextc, const char *extra)
259 {
260 	char *nextra = NULL;
261 	visfun_t f;
262 
263 	_DIAGASSERT(dst != NULL);
264 	_DIAGASSERT(extra != NULL);
265 	MAKEEXTRALIST(flag, nextra, extra);
266 	if (!nextra) {
267 		*dst = '\0';		/* can't create nextra, return "" */
268 		return dst;
269 	}
270 	f = getvisfun(flag);
271 	dst = (*f)(dst, c, flag, nextc, nextra);
272 	free(nextra);
273 	*dst = '\0';
274 	return dst;
275 }
276 
277 
278 /*
279  * strsvis, strsvisx - visually encode characters from src into dst
280  *
281  *	Extra is a pointer to a \0-terminated list of characters to
282  *	be encoded, too. These functions are useful e. g. to
283  *	encode strings in such a way so that they are not interpreted
284  *	by a shell.
285  *
286  *	Dst must be 4 times the size of src to account for possible
287  *	expansion.  The length of dst, not including the trailing NULL,
288  *	is returned.
289  *
290  *	Strsvisx encodes exactly len bytes from src into dst.
291  *	This is useful for encoding a block of data.
292  */
293 int
294 strsvis(char *dst, const char *csrc, int flag, const char *extra)
295 {
296 	int c;
297 	char *start;
298 	char *nextra = NULL;
299 	const unsigned char *src = (const unsigned char *)csrc;
300 	visfun_t f;
301 
302 	_DIAGASSERT(dst != NULL);
303 	_DIAGASSERT(src != NULL);
304 	_DIAGASSERT(extra != NULL);
305 	MAKEEXTRALIST(flag, nextra, extra);
306 	if (!nextra) {
307 		*dst = '\0';		/* can't create nextra, return "" */
308 		return 0;
309 	}
310 	f = getvisfun(flag);
311 	for (start = dst; (c = *src++) != '\0'; /* empty */)
312 		dst = (*f)(dst, c, flag, *src, nextra);
313 	free(nextra);
314 	*dst = '\0';
315 	return (int)(dst - start);
316 }
317 
318 
319 int
320 strsvisx(char *dst, const char *csrc, size_t len, int flag, const char *extra)
321 {
322 	unsigned char c;
323 	char *start;
324 	char *nextra = NULL;
325 	const unsigned char *src = (const unsigned char *)csrc;
326 	visfun_t f;
327 
328 	_DIAGASSERT(dst != NULL);
329 	_DIAGASSERT(src != NULL);
330 	_DIAGASSERT(extra != NULL);
331 	MAKEEXTRALIST(flag, nextra, extra);
332 	if (! nextra) {
333 		*dst = '\0';		/* can't create nextra, return "" */
334 		return 0;
335 	}
336 
337 	f = getvisfun(flag);
338 	for (start = dst; len > 0; len--) {
339 		c = *src++;
340 		dst = (*f)(dst, c, flag, len > 1 ? *src : '\0', nextra);
341 	}
342 	free(nextra);
343 	*dst = '\0';
344 	return (int)(dst - start);
345 }
346 #endif
347 
348 #if !HAVE_VIS
349 /*
350  * vis - visually encode characters
351  */
352 char *
353 vis(char *dst, int c, int flag, int nextc)
354 {
355 	char *extra = NULL;
356 	unsigned char uc = (unsigned char)c;
357 	visfun_t f;
358 
359 	_DIAGASSERT(dst != NULL);
360 
361 	MAKEEXTRALIST(flag, extra, "");
362 	if (! extra) {
363 		*dst = '\0';		/* can't create extra, return "" */
364 		return dst;
365 	}
366 	f = getvisfun(flag);
367 	dst = (*f)(dst, uc, flag, nextc, extra);
368 	free(extra);
369 	*dst = '\0';
370 	return dst;
371 }
372 
373 
374 /*
375  * strvis, strvisx - visually encode characters from src into dst
376  *
377  *	Dst must be 4 times the size of src to account for possible
378  *	expansion.  The length of dst, not including the trailing NULL,
379  *	is returned.
380  *
381  *	Strvisx encodes exactly len bytes from src into dst.
382  *	This is useful for encoding a block of data.
383  */
384 int
385 strvis(char *dst, const char *src, int flag)
386 {
387 	char *extra = NULL;
388 	int rv;
389 
390 	MAKEEXTRALIST(flag, extra, "");
391 	if (!extra) {
392 		*dst = '\0';		/* can't create extra, return "" */
393 		return 0;
394 	}
395 	rv = strsvis(dst, src, flag, extra);
396 	free(extra);
397 	return rv;
398 }
399 
400 
401 int
402 strvisx(char *dst, const char *src, size_t len, int flag)
403 {
404 	char *extra = NULL;
405 	int rv;
406 
407 	MAKEEXTRALIST(flag, extra, "");
408 	if (!extra) {
409 		*dst = '\0';		/* can't create extra, return "" */
410 		return 0;
411 	}
412 	rv = strsvisx(dst, src, len, flag, extra);
413 	free(extra);
414 	return rv;
415 }
416 #endif
417