1 /*	$NetBSD: vis.c,v 1.35 2006/08/28 20:42:12 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  * 3. Neither the name of The NetBSD Foundation nor the names of its
45  *    contributors may be used to endorse or promote products derived
46  *    from this software without specific prior written permission.
47  *
48  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
49  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
50  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
51  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
52  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
53  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
54  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
55  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
56  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
57  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
58  * POSSIBILITY OF SUCH DAMAGE.
59  */
60 
61 #include <sys/cdefs.h>
62 __RCSID("$MirOS: src/lib/libc/gen/vis.c,v 1.3 2010/01/07 22:34:50 tg Exp $");
63 __RCSID("$OpenBSD: vis.c,v 1.19 2005/09/01 17:15:49 millert Exp $");
64 __RCSID("$NetBSD: vis.c,v 1.35 2006/08/28 20:42:12 christos Exp $");
65 
66 #include <sys/types.h>
67 
68 #include <assert.h>
69 #include <vis.h>
70 #include <stdlib.h>
71 
72 #include <ctype.h>
73 #include <limits.h>
74 #include <stdio.h>
75 #include <string.h>
76 
77 extern const uint8_t mbsd_digits_hex[17];
78 
79 #define isoctal(c)	(((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
80 #define iswhite(c)	(c == ' ' || c == '\t' || c == '\n')
81 #define issafe(c)	(c == '\b' || c == '\a' || c == '\r' || isgraph((u_char)(c)))
82 #define xtoa(c)		mbsd_digits_hex[c]
83 
84 #define MAXEXTRAS	9
85 
86 
87 #define MAKEEXTRALIST(flag, extra, orig_str)				      \
88 do {									      \
89 	const char *orig = orig_str;					      \
90 	const char *o = orig;						      \
91 	char *e;							      \
92 	while (*o++)							      \
93 		continue;						      \
94 	extra = malloc((size_t)((o - orig) + MAXEXTRAS));		      \
95 	if (!extra) break;						      \
96 	for (o = orig, e = extra; (*e++ = *o++) != '\0';)		      \
97 		continue;						      \
98 	e--;								      \
99 	if (flag & VIS_SP) *e++ = ' ';					      \
100 	if (flag & VIS_TAB) *e++ = '\t';				      \
101 	if (flag & VIS_NL) *e++ = '\n';					      \
102 	if ((flag & VIS_NOSLASH) == 0) *e++ = '\\';			      \
103 	if (flag & VIS_GLOB) {						      \
104 		*e++ = '*';						      \
105 		*e++ = '?';						      \
106 		*e++ = '[';						      \
107 		*e++ = '#';						      \
108 	}								      \
109 	*e = '\0';							      \
110 } while (/*CONSTCOND*/0)
111 
112 
113 /*
114  * This is HVIS, the macro of vis used to HTTP style (RFC 1808)
115  */
116 #define HVIS(dst, c, flag, nextc, extra)				      \
117 do									      \
118 	if (!isascii(c) || !isalnum(c) || strchr("$-_.+!*'(),", c) != NULL) { \
119 		*dst++ = '%';						      \
120 		*dst++ = xtoa(((unsigned int)c >> 4) & 0xf);		      \
121 		*dst++ = xtoa((unsigned int)c & 0xf);			      \
122 	} else {							      \
123 		SVIS(dst, c, flag, nextc, extra);			      \
124 	}								      \
125 while (/*CONSTCOND*/0)
126 
127 /*
128  * This is SVIS, the central macro of vis.
129  * dst:	      Pointer to the destination buffer
130  * c:	      Character to encode
131  * flag:      Flag word
132  * nextc:     The character following 'c'
133  * extra:     Pointer to the list of extra characters to be
134  *	      backslash-protected.
135  */
136 #define SVIS(dst, c, flag, nextc, extra)				      \
137 do {									      \
138 	int isextra;							      \
139 	isextra = strchr(extra, c) != NULL;				      \
140 	if (!isextra && isascii(c) && (isgraph(c) || iswhite(c) ||	      \
141 	    ((flag & VIS_SAFE) && issafe(c)))) {			      \
142 		*dst++ = c;						      \
143 		break;							      \
144 	}								      \
145 	if (flag & VIS_CSTYLE) {					      \
146 		switch (c) {						      \
147 		case '\n':						      \
148 			*dst++ = '\\'; *dst++ = 'n';			      \
149 			continue;					      \
150 		case '\r':						      \
151 			*dst++ = '\\'; *dst++ = 'r';			      \
152 			continue;					      \
153 		case '\b':						      \
154 			*dst++ = '\\'; *dst++ = 'b';			      \
155 			continue;					      \
156 		case '\a':						      \
157 			*dst++ = '\\'; *dst++ = 'a';			      \
158 			continue;					      \
159 		case '\v':						      \
160 			*dst++ = '\\'; *dst++ = 'v';			      \
161 			continue;					      \
162 		case '\t':						      \
163 			*dst++ = '\\'; *dst++ = 't';			      \
164 			continue;					      \
165 		case '\f':						      \
166 			*dst++ = '\\'; *dst++ = 'f';			      \
167 			continue;					      \
168 		case ' ':						      \
169 			*dst++ = '\\'; *dst++ = 's';			      \
170 			continue;					      \
171 		case '\0':						      \
172 			*dst++ = '\\'; *dst++ = '0';			      \
173 			if (isoctal(nextc)) {				      \
174 				*dst++ = '0';				      \
175 				*dst++ = '0';				      \
176 			}						      \
177 			continue;					      \
178 		default:						      \
179 			if (isgraph(c)) {				      \
180 				*dst++ = '\\'; *dst++ = c;		      \
181 				continue;				      \
182 			}						      \
183 		}							      \
184 	}								      \
185 	if (isextra || ((c & 0177) == ' ') || (flag & VIS_OCTAL)) {	      \
186 		*dst++ = '\\';						      \
187 		*dst++ = (u_char)(((u_int32_t)(u_char)c >> 6) & 03) + '0';    \
188 		*dst++ = (u_char)(((u_int32_t)(u_char)c >> 3) & 07) + '0';    \
189 		*dst++ =			     (c	      & 07) + '0';    \
190 	} else {							      \
191 		if ((flag & VIS_NOSLASH) == 0) *dst++ = '\\';		      \
192 		if (c & 0200) {						      \
193 			c &= 0177; *dst++ = 'M';			      \
194 		}							      \
195 		if (iscntrl(c)) {					      \
196 			*dst++ = '^';					      \
197 			if (c == 0177)					      \
198 				*dst++ = '?';				      \
199 			else						      \
200 				*dst++ = c + '@';			      \
201 		} else {						      \
202 			*dst++ = '-'; *dst++ = c;			      \
203 		}							      \
204 	}								      \
205 } while (/*CONSTCOND*/0)
206 
207 
208 /*
209  * svis - visually encode characters, also encoding the characters
210  *	  pointed to by `extra'
211  */
212 char *
svis(char * dst,int c,int flag,int nextc,const char * extra)213 svis(char *dst, int c, int flag, int nextc, const char *extra)
214 {
215 	char *nextra = NULL;
216 
217 	MAKEEXTRALIST(flag, nextra, extra);
218 	if (!nextra) {
219 		*dst = '\0';		/* can't create nextra, return "" */
220 		return dst;
221 	}
222 	if (flag & VIS_HTTPSTYLE)
223 		HVIS(dst, c, flag, nextc, nextra);
224 	else
225 		SVIS(dst, c, flag, nextc, nextra);
226 	free(nextra);
227 	*dst = '\0';
228 	return dst;
229 }
230 
231 
232 /*
233  * strsvis, strnsvis, strsvisx - visually encode characters from src into dst
234  *
235  *	Extra is a pointer to a \0-terminated list of characters to
236  *	be encoded, too. These functions are useful e. g. to
237  *	encode strings in such a way so that they are not interpreted
238  *	by a shell.
239  *
240  *	Dst must be 4 times the size of src to account for possible
241  *	expansion.  The length of dst, not including the trailing NUL,
242  *	is returned.
243  *
244  *	Strsvisx encodes exactly len bytes from src into dst.
245  *	This is useful for encoding a block of data.
246  */
247 int
strsvis(char * dst,const char * csrc,int flag,const char * extra)248 strsvis(char *dst, const char *csrc, int flag, const char *extra)
249 {
250 	int c;
251 	char *start;
252 	char *nextra = NULL;
253 	const unsigned char *src = (const unsigned char *)csrc;
254 
255 	MAKEEXTRALIST(flag, nextra, extra);
256 	if (!nextra) {
257 		*dst = '\0';		/* can't create nextra, return "" */
258 		return 0;
259 	}
260 	if (flag & VIS_HTTPSTYLE) {
261 		for (start = dst; (c = *src++) != '\0'; /* empty */)
262 			HVIS(dst, c, flag, *src, nextra);
263 	} else {
264 		for (start = dst; (c = *src++) != '\0'; /* empty */)
265 			SVIS(dst, c, flag, *src, nextra);
266 	}
267 	free(nextra);
268 	*dst = '\0';
269 	return (dst - start);
270 }
271 
272 
273 int
strnsvis(char * dst,const char * src,size_t siz,int flag,const char * extra)274 strnsvis(char *dst, const char *src, size_t siz, int flag, const char *extra)
275 {
276 	char *start, *end;
277 	char tbuf[5];
278 	int c, i;
279 	char *nextra = NULL;
280 
281 	MAKEEXTRALIST(flag, nextra, extra);
282 	if (!nextra) {
283 		*dst = '\0';		/* can't create nextra, return "" */
284 		return 0;
285 	}
286 
287 	i = 0;
288 	for (start = dst, end = start + siz - 1; (c = *src) && dst < end; ) {
289 		/* unoptimised but safe to extend version */
290 		i = vis(tbuf, c, flag, *++src) - tbuf;
291 		if (dst + i <= end) {
292 			memcpy(dst, tbuf, i);
293 			dst += i;
294 		} else {
295 			src--;
296 			break;
297 		}
298 	}
299 	if (siz > 0)
300 		*dst = '\0';
301 	if (dst + i > end) {
302 		/* adjust return value for truncation */
303 		while ((c = *src))
304 			dst += vis(tbuf, c, flag, *++src) - tbuf;
305 	}
306 	free(nextra);
307 	return (dst - start);
308 }
309 
310 
311 int
strsvisx(char * dst,const char * csrc,size_t len,int flag,const char * extra)312 strsvisx(char *dst, const char *csrc, size_t len, int flag, const char *extra)
313 {
314 	unsigned char c;
315 	char *start;
316 	char *nextra = NULL;
317 	const unsigned char *src = (const unsigned char *)csrc;
318 
319 	MAKEEXTRALIST(flag, nextra, extra);
320 	if (! nextra) {
321 		*dst = '\0';		/* can't create nextra, return "" */
322 		return 0;
323 	}
324 
325 	if (flag & VIS_HTTPSTYLE) {
326 		for (start = dst; len > 0; len--) {
327 			c = *src++;
328 			HVIS(dst, c, flag, len ? *src : '\0', nextra);
329 		}
330 	} else {
331 		for (start = dst; len > 0; len--) {
332 			c = *src++;
333 			SVIS(dst, c, flag, len ? *src : '\0', nextra);
334 		}
335 	}
336 	free(nextra);
337 	*dst = '\0';
338 	return (dst - start);
339 }
340 
341 /*
342  * vis - visually encode characters
343  */
344 char *
vis(char * dst,int c,int flag,int nextc)345 vis(char *dst, int c, int flag, int nextc)
346 {
347 	char *extra = NULL;
348 	unsigned char uc = (unsigned char)c;
349 
350 	MAKEEXTRALIST(flag, extra, "");
351 	if (! extra) {
352 		*dst = '\0';		/* can't create extra, return "" */
353 		return dst;
354 	}
355 	if (flag & VIS_HTTPSTYLE)
356 		HVIS(dst, uc, flag, nextc, extra);
357 	else
358 		SVIS(dst, uc, flag, nextc, extra);
359 	free(extra);
360 	*dst = '\0';
361 	return dst;
362 }
363 
364 
365 /*
366  * strvis, strnvis, strvisx - visually encode characters from src into dst
367  *
368  *	Dst must be 4 times the size of src to account for possible
369  *	expansion.  The length of dst, not including the trailing NUL,
370  *	is returned.
371  *
372  *	Strnvis will write no more than siz-1 bytes (and will NUL terminate).
373  *	The number of bytes needed to fully encode the string is returned.
374  *
375  *	Strvisx encodes exactly len bytes from src into dst.
376  *	This is useful for encoding a block of data.
377  */
378 int
strvis(char * dst,const char * src,int flag)379 strvis(char *dst, const char *src, int flag)
380 {
381 	char *extra = NULL;
382 	int rv;
383 
384 	MAKEEXTRALIST(flag, extra, "");
385 	if (!extra) {
386 		*dst = '\0';		/* can't create extra, return "" */
387 		return 0;
388 	}
389 	rv = strsvis(dst, src, flag, extra);
390 	free(extra);
391 	return rv;
392 }
393 
394 
395 int
strnvis(char * dst,const char * src,size_t siz,int flag)396 strnvis(char *dst, const char *src, size_t siz, int flag)
397 {
398 	char *extra = NULL;
399 	int rv;
400 
401 	MAKEEXTRALIST(flag, extra, "");
402 	if (!extra) {
403 		*dst = '\0';		/* can't create extra, return "" */
404 		return 0;
405 	}
406 	rv = strnsvis(dst, src, siz, flag, extra);
407 	free(extra);
408 	return rv;
409 }
410 
411 
412 int
strvisx(char * dst,const char * src,size_t len,int flag)413 strvisx(char *dst, const char *src, size_t len, int flag)
414 {
415 	char *extra = NULL;
416 	int rv;
417 
418 	MAKEEXTRALIST(flag, extra, "");
419 	if (!extra) {
420 		*dst = '\0';		/* can't create extra, return "" */
421 		return 0;
422 	}
423 	rv = strsvisx(dst, src, len, flag, extra);
424 	free(extra);
425 	return rv;
426 }
427