xref: /netbsd-src/lib/libc/gen/vis.c (revision 56a34939419542e88b386b2229be7565f4f45461)
1 /*	$NetBSD: vis.c,v 1.38 2008/09/04 09:41:44 lukem 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.38 2008/09/04 09:41:44 lukem 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 
95 #define MAXEXTRAS	5
96 
97 #define MAKEEXTRALIST(flag, extra, orig_str)				      \
98 do {									      \
99 	const char *orig = orig_str;					      \
100 	const char *o = orig;						      \
101 	char *e;							      \
102 	while (*o++)							      \
103 		continue;						      \
104 	extra = malloc((size_t)((o - orig) + MAXEXTRAS));		      \
105 	if (!extra) break;						      \
106 	for (o = orig, e = extra; (*e++ = *o++) != '\0';)		      \
107 		continue;						      \
108 	e--;								      \
109 	if (flag & VIS_SP) *e++ = ' ';					      \
110 	if (flag & VIS_TAB) *e++ = '\t';				      \
111 	if (flag & VIS_NL) *e++ = '\n';					      \
112 	if ((flag & VIS_NOSLASH) == 0) *e++ = '\\';			      \
113 	*e = '\0';							      \
114 } while (/*CONSTCOND*/0)
115 
116 /*
117  * This is do_hvis, for HTTP style (RFC 1808)
118  */
119 static char *
120 do_hvis(char *dst, int c, int flag, int nextc, const char *extra)
121 {
122 	if (!isascii(c) || !isalnum(c) || strchr("$-_.+!*'(),", c) != NULL) {
123 		*dst++ = '%';
124 		*dst++ = xtoa(((unsigned int)c >> 4) & 0xf);
125 		*dst++ = xtoa((unsigned int)c & 0xf);
126 	} else {
127 		dst = do_svis(dst, c, flag, nextc, extra);
128 	}
129 	return dst;
130 }
131 
132 /*
133  * This is do_vis, the central code of vis.
134  * dst:	      Pointer to the destination buffer
135  * c:	      Character to encode
136  * flag:      Flag word
137  * nextc:     The character following 'c'
138  * extra:     Pointer to the list of extra characters to be
139  *	      backslash-protected.
140  */
141 static char *
142 do_svis(char *dst, int c, int flag, int nextc, const char *extra)
143 {
144 	int isextra;
145 	isextra = strchr(extra, c) != NULL;
146 	if (!isextra && isascii(c) && (isgraph(c) || iswhite(c) ||
147 	    ((flag & VIS_SAFE) && issafe(c)))) {
148 		*dst++ = c;
149 		return dst;
150 	}
151 	if (flag & VIS_CSTYLE) {
152 		switch (c) {
153 		case '\n':
154 			*dst++ = '\\'; *dst++ = 'n';
155 			return dst;
156 		case '\r':
157 			*dst++ = '\\'; *dst++ = 'r';
158 			return dst;
159 		case '\b':
160 			*dst++ = '\\'; *dst++ = 'b';
161 			return dst;
162 		case BELL:
163 			*dst++ = '\\'; *dst++ = 'a';
164 			return dst;
165 		case '\v':
166 			*dst++ = '\\'; *dst++ = 'v';
167 			return dst;
168 		case '\t':
169 			*dst++ = '\\'; *dst++ = 't';
170 			return dst;
171 		case '\f':
172 			*dst++ = '\\'; *dst++ = 'f';
173 			return dst;
174 		case ' ':
175 			*dst++ = '\\'; *dst++ = 's';
176 			return dst;
177 		case '\0':
178 			*dst++ = '\\'; *dst++ = '0';
179 			if (isoctal(nextc)) {
180 				*dst++ = '0';
181 				*dst++ = '0';
182 			}
183 			return dst;
184 		default:
185 			if (isgraph(c)) {
186 				*dst++ = '\\'; *dst++ = c;
187 				return dst;
188 			}
189 		}
190 	}
191 	if (isextra || ((c & 0177) == ' ') || (flag & VIS_OCTAL)) {
192 		*dst++ = '\\';
193 		*dst++ = (u_char)(((u_int32_t)(u_char)c >> 6) & 03) + '0';
194 		*dst++ = (u_char)(((u_int32_t)(u_char)c >> 3) & 07) + '0';
195 		*dst++ =			     (c	      & 07) + '0';
196 	} else {
197 		if ((flag & VIS_NOSLASH) == 0) *dst++ = '\\';
198 		if (c & 0200) {
199 			c &= 0177; *dst++ = 'M';
200 		}
201 		if (iscntrl(c)) {
202 			*dst++ = '^';
203 			if (c == 0177)
204 				*dst++ = '?';
205 			else
206 				*dst++ = c + '@';
207 		} else {
208 			*dst++ = '-'; *dst++ = c;
209 		}
210 	}
211 	return dst;
212 }
213 
214 
215 /*
216  * svis - visually encode characters, also encoding the characters
217  *	  pointed to by `extra'
218  */
219 char *
220 svis(char *dst, int c, int flag, int nextc, const char *extra)
221 {
222 	char *nextra = NULL;
223 
224 	_DIAGASSERT(dst != NULL);
225 	_DIAGASSERT(extra != NULL);
226 	MAKEEXTRALIST(flag, nextra, extra);
227 	if (!nextra) {
228 		*dst = '\0';		/* can't create nextra, return "" */
229 		return dst;
230 	}
231 	if (flag & VIS_HTTPSTYLE)
232 		dst = do_hvis(dst, c, flag, nextc, nextra);
233 	else
234 		dst = do_svis(dst, c, flag, nextc, nextra);
235 	free(nextra);
236 	*dst = '\0';
237 	return dst;
238 }
239 
240 
241 /*
242  * strsvis, strsvisx - visually encode characters from src into dst
243  *
244  *	Extra is a pointer to a \0-terminated list of characters to
245  *	be encoded, too. These functions are useful e. g. to
246  *	encode strings in such a way so that they are not interpreted
247  *	by a shell.
248  *
249  *	Dst must be 4 times the size of src to account for possible
250  *	expansion.  The length of dst, not including the trailing NULL,
251  *	is returned.
252  *
253  *	Strsvisx encodes exactly len bytes from src into dst.
254  *	This is useful for encoding a block of data.
255  */
256 int
257 strsvis(char *dst, const char *csrc, int flag, const char *extra)
258 {
259 	int c;
260 	char *start;
261 	char *nextra = NULL;
262 	const unsigned char *src = (const unsigned char *)csrc;
263 
264 	_DIAGASSERT(dst != NULL);
265 	_DIAGASSERT(src != NULL);
266 	_DIAGASSERT(extra != NULL);
267 	MAKEEXTRALIST(flag, nextra, extra);
268 	if (!nextra) {
269 		*dst = '\0';		/* can't create nextra, return "" */
270 		return 0;
271 	}
272 	if (flag & VIS_HTTPSTYLE) {
273 		for (start = dst; (c = *src++) != '\0'; /* empty */)
274 			dst = do_hvis(dst, c, flag, *src, nextra);
275 	} else {
276 		for (start = dst; (c = *src++) != '\0'; /* empty */)
277 			dst = do_svis(dst, c, flag, *src, nextra);
278 	}
279 	free(nextra);
280 	*dst = '\0';
281 	return (dst - start);
282 }
283 
284 
285 int
286 strsvisx(char *dst, const char *csrc, size_t len, int flag, const char *extra)
287 {
288 	unsigned char c;
289 	char *start;
290 	char *nextra = NULL;
291 	const unsigned char *src = (const unsigned char *)csrc;
292 
293 	_DIAGASSERT(dst != NULL);
294 	_DIAGASSERT(src != NULL);
295 	_DIAGASSERT(extra != NULL);
296 	MAKEEXTRALIST(flag, nextra, extra);
297 	if (! nextra) {
298 		*dst = '\0';		/* can't create nextra, return "" */
299 		return 0;
300 	}
301 
302 	if (flag & VIS_HTTPSTYLE) {
303 		for (start = dst; len > 0; len--) {
304 			c = *src++;
305 			dst = do_hvis(dst, c, flag,
306 			    len > 1 ? *src : '\0', nextra);
307 		}
308 	} else {
309 		for (start = dst; len > 0; len--) {
310 			c = *src++;
311 			dst = do_svis(dst, c, flag,
312 			    len > 1 ? *src : '\0', nextra);
313 		}
314 	}
315 	free(nextra);
316 	*dst = '\0';
317 	return (dst - start);
318 }
319 #endif
320 
321 #if !HAVE_VIS
322 /*
323  * vis - visually encode characters
324  */
325 char *
326 vis(char *dst, int c, int flag, int nextc)
327 {
328 	char *extra = NULL;
329 	unsigned char uc = (unsigned char)c;
330 
331 	_DIAGASSERT(dst != NULL);
332 
333 	MAKEEXTRALIST(flag, extra, "");
334 	if (! extra) {
335 		*dst = '\0';		/* can't create extra, return "" */
336 		return dst;
337 	}
338 	if (flag & VIS_HTTPSTYLE)
339 		dst = do_hvis(dst, uc, flag, nextc, extra);
340 	else
341 		dst = do_svis(dst, uc, flag, nextc, extra);
342 	free(extra);
343 	*dst = '\0';
344 	return dst;
345 }
346 
347 
348 /*
349  * strvis, strvisx - visually encode characters from src into dst
350  *
351  *	Dst must be 4 times the size of src to account for possible
352  *	expansion.  The length of dst, not including the trailing NULL,
353  *	is returned.
354  *
355  *	Strvisx encodes exactly len bytes from src into dst.
356  *	This is useful for encoding a block of data.
357  */
358 int
359 strvis(char *dst, const char *src, int flag)
360 {
361 	char *extra = NULL;
362 	int rv;
363 
364 	MAKEEXTRALIST(flag, extra, "");
365 	if (!extra) {
366 		*dst = '\0';		/* can't create extra, return "" */
367 		return 0;
368 	}
369 	rv = strsvis(dst, src, flag, extra);
370 	free(extra);
371 	return rv;
372 }
373 
374 
375 int
376 strvisx(char *dst, const char *src, size_t len, int flag)
377 {
378 	char *extra = NULL;
379 	int rv;
380 
381 	MAKEEXTRALIST(flag, extra, "");
382 	if (!extra) {
383 		*dst = '\0';		/* can't create extra, return "" */
384 		return 0;
385 	}
386 	rv = strsvisx(dst, src, len, flag, extra);
387 	free(extra);
388 	return rv;
389 }
390 #endif
391