xref: /freebsd/contrib/libc-vis/vis.c (revision 955c8cbb4960e6cf3602de144b1b9154a5092968)
1 /*	$NetBSD: vis.c,v 1.45 2012/12/14 21:38:18 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.45 2012/12/14 21:38:18 christos Exp $");
61 #endif /* LIBC_SCCS and not lint */
62 __FBSDID("$FreeBSD$");
63 
64 #include "namespace.h"
65 #include <sys/types.h>
66 
67 #include <assert.h>
68 #include <vis.h>
69 #include <errno.h>
70 #include <stdlib.h>
71 
72 #define	_DIAGASSERT(x)	assert(x)
73 
74 #ifdef __weak_alias
75 __weak_alias(strvisx,_strvisx)
76 #endif
77 
78 #if !HAVE_VIS || !HAVE_SVIS
79 #include <ctype.h>
80 #include <limits.h>
81 #include <stdio.h>
82 #include <string.h>
83 
84 static char *do_svis(char *, size_t *, int, int, int, const char *);
85 
86 #undef BELL
87 #define BELL '\a'
88 
89 #define isoctal(c)	(((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
90 #define iswhite(c)	(c == ' ' || c == '\t' || c == '\n')
91 #define issafe(c)	(c == '\b' || c == BELL || c == '\r')
92 #define xtoa(c)		"0123456789abcdef"[c]
93 #define XTOA(c)		"0123456789ABCDEF"[c]
94 
95 #define MAXEXTRAS	9
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_GLOB) {						      \
110 		*e++ = '*';						      \
111 		*e++ = '?';						      \
112 		*e++ = '[';						      \
113 		*e++ = '#';						      \
114 	}								      \
115 	if (flag & VIS_SP) *e++ = ' ';					      \
116 	if (flag & VIS_TAB) *e++ = '\t';				      \
117 	if (flag & VIS_NL) *e++ = '\n';					      \
118 	if ((flag & VIS_NOSLASH) == 0) *e++ = '\\';			      \
119 	*e = '\0';							      \
120 } while (/*CONSTCOND*/0)
121 
122 /*
123  * This is do_hvis, for HTTP style (RFC 1808)
124  */
125 static char *
126 do_hvis(char *dst, size_t *dlen, int c, int flag, int nextc, const char *extra)
127 {
128 
129 	if ((isascii(c) && isalnum(c))
130 	    /* safe */
131 	    || c == '$' || c == '-' || c == '_' || c == '.' || c == '+'
132 	    /* extra */
133 	    || c == '!' || c == '*' || c == '\'' || c == '(' || c == ')'
134 	    || c == ',') {
135 		dst = do_svis(dst, dlen, c, flag, nextc, extra);
136 	} else {
137 		if (dlen) {
138 			if (*dlen < 3)
139 				return NULL;
140 			*dlen -= 3;
141 		}
142 		*dst++ = '%';
143 		*dst++ = xtoa(((unsigned int)c >> 4) & 0xf);
144 		*dst++ = xtoa((unsigned int)c & 0xf);
145 	}
146 
147 	return dst;
148 }
149 
150 /*
151  * This is do_mvis, for Quoted-Printable MIME (RFC 2045)
152  * NB: No handling of long lines or CRLF.
153  */
154 static char *
155 do_mvis(char *dst, size_t *dlen, int c, int flag, int nextc, const char *extra)
156 {
157 	if ((c != '\n') &&
158 	    /* Space at the end of the line */
159 	    ((isspace(c) && (nextc == '\r' || nextc == '\n')) ||
160 	    /* Out of range */
161 	    (!isspace(c) && (c < 33 || (c > 60 && c < 62) || c > 126)) ||
162 	    /* Specific char to be escaped */
163 	    strchr("#$@[\\]^`{|}~", c) != NULL)) {
164 		if (dlen) {
165 			if (*dlen < 3)
166 				return NULL;
167 			*dlen -= 3;
168 		}
169 		*dst++ = '=';
170 		*dst++ = XTOA(((unsigned int)c >> 4) & 0xf);
171 		*dst++ = XTOA((unsigned int)c & 0xf);
172 	} else {
173 		dst = do_svis(dst, dlen, c, flag, nextc, extra);
174 	}
175 	return dst;
176 }
177 
178 /*
179  * This is do_vis, the central code of vis.
180  * dst:	      Pointer to the destination buffer
181  * c:	      Character to encode
182  * flag:      Flag word
183  * nextc:     The character following 'c'
184  * extra:     Pointer to the list of extra characters to be
185  *	      backslash-protected.
186  */
187 static char *
188 do_svis(char *dst, size_t *dlen, int c, int flag, int nextc, const char *extra)
189 {
190 	int isextra;
191 	size_t odlen = dlen ? *dlen : 0;
192 
193 	isextra = strchr(extra, c) != NULL;
194 #define HAVE(x) \
195 	do { \
196 		if (dlen) { \
197 			if (*dlen < (x)) \
198 				goto out; \
199 			*dlen -= (x); \
200 		} \
201 	} while (/*CONSTCOND*/0)
202 	if (!isextra && isascii(c) && (isgraph(c) || iswhite(c) ||
203 	    ((flag & VIS_SAFE) && issafe(c)))) {
204 		HAVE(1);
205 		*dst++ = c;
206 		return dst;
207 	}
208 	if (flag & VIS_CSTYLE) {
209 		HAVE(2);
210 		switch (c) {
211 		case '\n':
212 			*dst++ = '\\'; *dst++ = 'n';
213 			return dst;
214 		case '\r':
215 			*dst++ = '\\'; *dst++ = 'r';
216 			return dst;
217 		case '\b':
218 			*dst++ = '\\'; *dst++ = 'b';
219 			return dst;
220 		case BELL:
221 			*dst++ = '\\'; *dst++ = 'a';
222 			return dst;
223 		case '\v':
224 			*dst++ = '\\'; *dst++ = 'v';
225 			return dst;
226 		case '\t':
227 			*dst++ = '\\'; *dst++ = 't';
228 			return dst;
229 		case '\f':
230 			*dst++ = '\\'; *dst++ = 'f';
231 			return dst;
232 		case ' ':
233 			*dst++ = '\\'; *dst++ = 's';
234 			return dst;
235 		case '\0':
236 			*dst++ = '\\'; *dst++ = '0';
237 			if (isoctal(nextc)) {
238 				HAVE(2);
239 				*dst++ = '0';
240 				*dst++ = '0';
241 			}
242 			return dst;
243 		default:
244 			if (isgraph(c)) {
245 				*dst++ = '\\'; *dst++ = c;
246 				return dst;
247 			}
248 			if (dlen)
249 				*dlen = odlen;
250 		}
251 	}
252 	if (isextra || ((c & 0177) == ' ') || (flag & VIS_OCTAL)) {
253 		HAVE(4);
254 		*dst++ = '\\';
255 		*dst++ = (u_char)(((u_int32_t)(u_char)c >> 6) & 03) + '0';
256 		*dst++ = (u_char)(((u_int32_t)(u_char)c >> 3) & 07) + '0';
257 		*dst++ =			     (c	      & 07) + '0';
258 	} else {
259 		if ((flag & VIS_NOSLASH) == 0) {
260 			HAVE(1);
261 			*dst++ = '\\';
262 		}
263 
264 		if (c & 0200) {
265 			HAVE(1);
266 			c &= 0177; *dst++ = 'M';
267 		}
268 
269 		if (iscntrl(c)) {
270 			HAVE(2);
271 			*dst++ = '^';
272 			if (c == 0177)
273 				*dst++ = '?';
274 			else
275 				*dst++ = c + '@';
276 		} else {
277 			HAVE(2);
278 			*dst++ = '-'; *dst++ = c;
279 		}
280 	}
281 	return dst;
282 out:
283 	*dlen = odlen;
284 	return NULL;
285 }
286 
287 typedef char *(*visfun_t)(char *, size_t *, int, int, int, const char *);
288 
289 /*
290  * Return the appropriate encoding function depending on the flags given.
291  */
292 static visfun_t
293 getvisfun(int flag)
294 {
295 	if (flag & VIS_HTTPSTYLE)
296 		return do_hvis;
297 	if (flag & VIS_MIMESTYLE)
298 		return do_mvis;
299 	return do_svis;
300 }
301 
302 /*
303  * isnvis - visually encode characters, also encoding the characters
304  *	  pointed to by `extra'
305  */
306 static char *
307 isnvis(char *dst, size_t *dlen, int c, int flag, int nextc, const char *extra)
308 {
309 	char *nextra = NULL;
310 	visfun_t f;
311 
312 	_DIAGASSERT(dst != NULL);
313 	_DIAGASSERT(extra != NULL);
314 	MAKEEXTRALIST(flag, nextra, extra);
315 	if (!nextra) {
316 		if (dlen && *dlen == 0) {
317 			errno = ENOSPC;
318 			return NULL;
319 		}
320 		*dst = '\0';		/* can't create nextra, return "" */
321 		return dst;
322 	}
323 	f = getvisfun(flag);
324 	dst = (*f)(dst, dlen, c, flag, nextc, nextra);
325 	free(nextra);
326 	if (dst == NULL || (dlen && *dlen == 0)) {
327 		errno = ENOSPC;
328 		return NULL;
329 	}
330 	*dst = '\0';
331 	return dst;
332 }
333 
334 char *
335 svis(char *dst, int c, int flag, int nextc, const char *extra)
336 {
337 	return isnvis(dst, NULL, c, flag, nextc, extra);
338 }
339 
340 char *
341 snvis(char *dst, size_t dlen, int c, int flag, int nextc, const char *extra)
342 {
343 	return isnvis(dst, &dlen, c, flag, nextc, extra);
344 }
345 
346 
347 /*
348  * strsvis, strsvisx - visually encode characters from src into dst
349  *
350  *	Extra is a pointer to a \0-terminated list of characters to
351  *	be encoded, too. These functions are useful e. g. to
352  *	encode strings in such a way so that they are not interpreted
353  *	by a shell.
354  *
355  *	Dst must be 4 times the size of src to account for possible
356  *	expansion.  The length of dst, not including the trailing NULL,
357  *	is returned.
358  *
359  *	Strsvisx encodes exactly len bytes from src into dst.
360  *	This is useful for encoding a block of data.
361  */
362 static int
363 istrsnvis(char *dst, size_t *dlen, const char *csrc, int flag, const char *extra)
364 {
365 	int c;
366 	char *start;
367 	char *nextra = NULL;
368 	const unsigned char *src = (const unsigned char *)csrc;
369 	visfun_t f;
370 
371 	_DIAGASSERT(dst != NULL);
372 	_DIAGASSERT(src != NULL);
373 	_DIAGASSERT(extra != NULL);
374 	MAKEEXTRALIST(flag, nextra, extra);
375 	if (!nextra) {
376 		*dst = '\0';		/* can't create nextra, return "" */
377 		return 0;
378 	}
379 	f = getvisfun(flag);
380 	for (start = dst; (c = *src++) != '\0'; /* empty */) {
381 		dst = (*f)(dst, dlen, c, flag, *src, nextra);
382 		if (dst == NULL) {
383 			errno = ENOSPC;
384 			return -1;
385 		}
386 	}
387 	free(nextra);
388 	if (dlen && *dlen == 0) {
389 		errno = ENOSPC;
390 		return -1;
391 	}
392 	*dst = '\0';
393 	return (int)(dst - start);
394 }
395 
396 int
397 strsvis(char *dst, const char *csrc, int flag, const char *extra)
398 {
399 	return istrsnvis(dst, NULL, csrc, flag, extra);
400 }
401 
402 int
403 strsnvis(char *dst, size_t dlen, const char *csrc, int flag, const char *extra)
404 {
405 	return istrsnvis(dst, &dlen, csrc, flag, extra);
406 }
407 
408 static int
409 istrsnvisx(char *dst, size_t *dlen, const char *csrc, size_t len, int flag,
410     const char *extra)
411 {
412 	unsigned char c;
413 	char *start;
414 	char *nextra = NULL;
415 	const unsigned char *src = (const unsigned char *)csrc;
416 	visfun_t f;
417 
418 	_DIAGASSERT(dst != NULL);
419 	_DIAGASSERT(src != NULL);
420 	_DIAGASSERT(extra != NULL);
421 	MAKEEXTRALIST(flag, nextra, extra);
422 	if (! nextra) {
423 		if (dlen && *dlen == 0) {
424 			errno = ENOSPC;
425 			return -1;
426 		}
427 		*dst = '\0';		/* can't create nextra, return "" */
428 		return 0;
429 	}
430 
431 	f = getvisfun(flag);
432 	for (start = dst; len > 0; len--) {
433 		c = *src++;
434 		dst = (*f)(dst, dlen, c, flag, len > 1 ? *src : '\0', nextra);
435 		if (dst == NULL) {
436 			errno = ENOSPC;
437 			return -1;
438 		}
439 	}
440 	free(nextra);
441 	if (dlen && *dlen == 0) {
442 		errno = ENOSPC;
443 		return -1;
444 	}
445 	*dst = '\0';
446 	return (int)(dst - start);
447 }
448 
449 int
450 strsvisx(char *dst, const char *csrc, size_t len, int flag, const char *extra)
451 {
452 	return istrsnvisx(dst, NULL, csrc, len, flag, extra);
453 }
454 
455 int
456 strsnvisx(char *dst, size_t dlen, const char *csrc, size_t len, int flag,
457     const char *extra)
458 {
459 	return istrsnvisx(dst, &dlen, csrc, len, flag, extra);
460 }
461 #endif
462 
463 #if !HAVE_VIS
464 /*
465  * vis - visually encode characters
466  */
467 static char *
468 invis(char *dst, size_t *dlen, int c, int flag, int nextc)
469 {
470 	char *extra = NULL;
471 	unsigned char uc = (unsigned char)c;
472 	visfun_t f;
473 
474 	_DIAGASSERT(dst != NULL);
475 
476 	MAKEEXTRALIST(flag, extra, "");
477 	if (! extra) {
478 		if (dlen && *dlen == 0) {
479 			errno = ENOSPC;
480 			return NULL;
481 		}
482 		*dst = '\0';		/* can't create extra, return "" */
483 		return dst;
484 	}
485 	f = getvisfun(flag);
486 	dst = (*f)(dst, dlen, uc, flag, nextc, extra);
487 	free(extra);
488 	if (dst == NULL || (dlen && *dlen == 0)) {
489 		errno = ENOSPC;
490 		return NULL;
491 	}
492 	*dst = '\0';
493 	return dst;
494 }
495 
496 char *
497 vis(char *dst, int c, int flag, int nextc)
498 {
499 	return invis(dst, NULL, c, flag, nextc);
500 }
501 
502 char *
503 nvis(char *dst, size_t dlen, int c, int flag, int nextc)
504 {
505 	return invis(dst, &dlen, c, flag, nextc);
506 }
507 
508 
509 /*
510  * strvis, strvisx - visually encode characters from src into dst
511  *
512  *	Dst must be 4 times the size of src to account for possible
513  *	expansion.  The length of dst, not including the trailing NULL,
514  *	is returned.
515  *
516  *	Strvisx encodes exactly len bytes from src into dst.
517  *	This is useful for encoding a block of data.
518  */
519 static int
520 istrnvis(char *dst, size_t *dlen, const char *src, int flag)
521 {
522 	char *extra = NULL;
523 	int rv;
524 
525 	MAKEEXTRALIST(flag, extra, "");
526 	if (!extra) {
527 		if (dlen && *dlen == 0) {
528 			errno = ENOSPC;
529 			return -1;
530 		}
531 		*dst = '\0';		/* can't create extra, return "" */
532 		return 0;
533 	}
534 	rv = istrsnvis(dst, dlen, src, flag, extra);
535 	free(extra);
536 	return rv;
537 }
538 
539 int
540 strvis(char *dst, const char *src, int flag)
541 {
542 	return istrnvis(dst, NULL, src, flag);
543 }
544 
545 int
546 strnvis(char *dst, size_t dlen, const char *src, int flag)
547 {
548 	return istrnvis(dst, &dlen, src, flag);
549 }
550 
551 static int
552 istrnvisx(char *dst, size_t *dlen, const char *src, size_t len, int flag)
553 {
554 	char *extra = NULL;
555 	int rv;
556 
557 	MAKEEXTRALIST(flag, extra, "");
558 	if (!extra) {
559 		if (dlen && *dlen == 0) {
560 			errno = ENOSPC;
561 			return -1;
562 		}
563 		*dst = '\0';		/* can't create extra, return "" */
564 		return 0;
565 	}
566 	rv = istrsnvisx(dst, dlen, src, len, flag, extra);
567 	free(extra);
568 	return rv;
569 }
570 
571 int
572 strvisx(char *dst, const char *src, size_t len, int flag)
573 {
574 	return istrnvisx(dst, NULL, src, len, flag);
575 }
576 
577 int
578 strnvisx(char *dst, size_t dlen, const char *src, size_t len, int flag)
579 {
580 	return istrnvisx(dst, &dlen, src, len, flag);
581 }
582 
583 #endif
584