1/*	$NetBSD: vis.c,v 1.37 2008/07/25 22:29:23 dsl 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#if 1
59#include <config.h>
60#include "roken.h"
61#ifndef _DIAGASSERT
62#define _DIAGASSERT(X)
63#endif
64#else /* heimdal */
65#include <sys/cdefs.h>
66#if defined(LIBC_SCCS) && !defined(lint)
67__RCSID("$NetBSD: vis.c,v 1.37 2008/07/25 22:29:23 dsl Exp $");
68#endif /* LIBC_SCCS and not lint */
69
70#include "namespace.h"
71#endif /* heimdal */
72
73#include <sys/types.h>
74
75#include <assert.h>
76#include <ctype.h>
77#include <limits.h>
78#include <stdio.h>
79#include <string.h>
80#include <vis.h>
81#include <stdlib.h>
82
83#if 0
84#ifdef __weak_alias
85__weak_alias(strsvis,_strsvis)
86__weak_alias(strsvisx,_strsvisx)
87__weak_alias(strvis,_strvis)
88__weak_alias(strvisx,_strvisx)
89__weak_alias(svis,_svis)
90__weak_alias(vis,_vis)
91#endif
92#endif
93
94#if !HAVE_VIS || !HAVE_SVIS
95#include <ctype.h>
96#include <limits.h>
97#include <stdio.h>
98#include <string.h>
99
100static char *do_svis(char *, int, int, int, const char *);
101
102#undef BELL
103#if defined(__STDC__)
104#define BELL '\a'
105#else
106#define BELL '\007'
107#endif
108
109ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL
110	rk_vis (char *, int, int, int);
111ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL
112	rk_svis (char *, int, int, int, const char *);
113ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
114	rk_strvis (char *, const char *, int);
115ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
116	rk_strsvis (char *, const char *, int, const char *);
117ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
118	rk_strvisx (char *, const char *, size_t, int);
119ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
120	rk_strsvisx (char *, const char *, size_t, int, const char *);
121
122
123#define isoctal(c)	(((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
124#define iswhite(c)	(c == ' ' || c == '\t' || c == '\n')
125#define issafe(c)	(c == '\b' || c == BELL || c == '\r')
126#define xtoa(c)		"0123456789abcdef"[c]
127
128#define MAXEXTRAS	5
129
130#define MAKEEXTRALIST(flag, extra, orig_str)				      \
131do {									      \
132	const char *orig = orig_str;					      \
133	const char *o = orig;						      \
134	char *e;							      \
135	while (*o++)							      \
136		continue;						      \
137	extra = malloc((size_t)((o - orig) + MAXEXTRAS));		      \
138	if (!extra) break;						      \
139	for (o = orig, e = extra; (*e++ = *o++) != '\0';)		      \
140		continue;						      \
141	e--;								      \
142	if (flag & VIS_SP) *e++ = ' ';					      \
143	if (flag & VIS_TAB) *e++ = '\t';				      \
144	if (flag & VIS_NL) *e++ = '\n';					      \
145	if ((flag & VIS_NOSLASH) == 0) *e++ = '\\';			      \
146	*e = '\0';							      \
147} while (/*CONSTCOND*/0)
148
149/*
150 * This is do_hvis, for HTTP style (RFC 1808)
151 */
152static char *
153do_hvis(char *dst, int c, int flag, int nextc, const char *extra)
154{
155	if (!isascii(c) || !isalnum(c) || strchr("$-_.+!*'(),", c) != NULL) {
156		*dst++ = '%';
157		*dst++ = xtoa(((unsigned int)c >> 4) & 0xf);
158		*dst++ = xtoa((unsigned int)c & 0xf);
159	} else {
160		dst = do_svis(dst, c, flag, nextc, extra);
161	}
162	return dst;
163}
164
165/*
166 * This is do_vis, the central code of vis.
167 * dst:	      Pointer to the destination buffer
168 * c:	      Character to encode
169 * flag:      Flag word
170 * nextc:     The character following 'c'
171 * extra:     Pointer to the list of extra characters to be
172 *	      backslash-protected.
173 */
174static char *
175do_svis(char *dst, int c, int flag, int nextc, const char *extra)
176{
177	int isextra;
178	isextra = strchr(extra, c) != NULL;
179	if (!isextra && isascii(c) && (isgraph(c) || iswhite(c) ||
180	    ((flag & VIS_SAFE) && issafe(c)))) {
181		*dst++ = c;
182		return dst;
183	}
184	if (flag & VIS_CSTYLE) {
185		switch (c) {
186		case '\n':
187			*dst++ = '\\'; *dst++ = 'n';
188			return dst;
189		case '\r':
190			*dst++ = '\\'; *dst++ = 'r';
191			return dst;
192		case '\b':
193			*dst++ = '\\'; *dst++ = 'b';
194			return dst;
195		case BELL:
196			*dst++ = '\\'; *dst++ = 'a';
197			return dst;
198		case '\v':
199			*dst++ = '\\'; *dst++ = 'v';
200			return dst;
201		case '\t':
202			*dst++ = '\\'; *dst++ = 't';
203			return dst;
204		case '\f':
205			*dst++ = '\\'; *dst++ = 'f';
206			return dst;
207		case ' ':
208			*dst++ = '\\'; *dst++ = 's';
209			return dst;
210		case '\0':
211			*dst++ = '\\'; *dst++ = '0';
212			if (isoctal(nextc)) {
213				*dst++ = '0';
214				*dst++ = '0';
215			}
216			return dst;
217		default:
218			if (isgraph(c)) {
219				*dst++ = '\\'; *dst++ = c;
220				return dst;
221			}
222		}
223	}
224	if (isextra || ((c & 0177) == ' ') || (flag & VIS_OCTAL)) {
225		*dst++ = '\\';
226		*dst++ = (u_char)(((unsigned int)(u_char)c >> 6) & 03) + '0';
227		*dst++ = (u_char)(((unsigned int)(u_char)c >> 3) & 07) + '0';
228		*dst++ = (u_char)(			 c       & 07) + '0';
229	} else {
230		if ((flag & VIS_NOSLASH) == 0) *dst++ = '\\';
231		if (c & 0200) {
232			c &= 0177; *dst++ = 'M';
233		}
234		if (iscntrl(c)) {
235			*dst++ = '^';
236			if (c == 0177)
237				*dst++ = '?';
238			else
239				*dst++ = c + '@';
240		} else {
241			*dst++ = '-'; *dst++ = c;
242		}
243	}
244	return dst;
245}
246
247
248/*
249 * svis - visually encode characters, also encoding the characters
250 *	  pointed to by `extra'
251 */
252ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL
253rk_svis(char *dst, int c, int flag, int nextc, const char *extra)
254{
255	char *nextra = NULL;
256
257	_DIAGASSERT(dst != NULL);
258	_DIAGASSERT(extra != NULL);
259	MAKEEXTRALIST(flag, nextra, extra);
260	if (!nextra) {
261		*dst = '\0';		/* can't create nextra, return "" */
262		return dst;
263	}
264	if (flag & VIS_HTTPSTYLE)
265		dst = do_hvis(dst, c, flag, nextc, nextra);
266	else
267		dst = do_svis(dst, c, flag, nextc, nextra);
268	free(nextra);
269	*dst = '\0';
270	return dst;
271}
272
273
274/*
275 * strsvis, strsvisx - visually encode characters from src into dst
276 *
277 *	Extra is a pointer to a \0-terminated list of characters to
278 *	be encoded, too. These functions are useful e. g. to
279 *	encode strings in such a way so that they are not interpreted
280 *	by a shell.
281 *
282 *	Dst must be 4 times the size of src to account for possible
283 *	expansion.  The length of dst, not including the trailing NULL,
284 *	is returned.
285 *
286 *	Strsvisx encodes exactly len bytes from src into dst.
287 *	This is useful for encoding a block of data.
288 */
289
290ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
291rk_strsvis(char *dst, const char *csrc, int flag, const char *extra)
292{
293	int c;
294	char *start;
295	char *nextra = NULL;
296	const unsigned char *src = (const unsigned char *)csrc;
297
298	_DIAGASSERT(dst != NULL);
299	_DIAGASSERT(src != NULL);
300	_DIAGASSERT(extra != NULL);
301	MAKEEXTRALIST(flag, nextra, extra);
302	if (!nextra) {
303		*dst = '\0';		/* can't create nextra, return "" */
304		return 0;
305	}
306	if (flag & VIS_HTTPSTYLE) {
307		for (start = dst; (c = *src++) != '\0'; /* empty */)
308			dst = do_hvis(dst, c, flag, *src, nextra);
309	} else {
310		for (start = dst; (c = *src++) != '\0'; /* empty */)
311			dst = do_svis(dst, c, flag, *src, nextra);
312	}
313	free(nextra);
314	*dst = '\0';
315	return (dst - start);
316}
317
318
319ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
320rk_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
327	_DIAGASSERT(dst != NULL);
328	_DIAGASSERT(src != NULL);
329	_DIAGASSERT(extra != NULL);
330	MAKEEXTRALIST(flag, nextra, extra);
331	if (! nextra) {
332		*dst = '\0';		/* can't create nextra, return "" */
333		return 0;
334	}
335
336	if (flag & VIS_HTTPSTYLE) {
337		for (start = dst; len > 0; len--) {
338			c = *src++;
339			dst = do_hvis(dst, c, flag, len ? *src : '\0', nextra);
340		}
341	} else {
342		for (start = dst; len > 0; len--) {
343			c = *src++;
344			dst = do_svis(dst, c, flag, len ? *src : '\0', nextra);
345		}
346	}
347	free(nextra);
348	*dst = '\0';
349	return (dst - start);
350}
351#endif
352
353#if !HAVE_VIS
354/*
355 * vis - visually encode characters
356 */
357ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL
358rk_vis(char *dst, int c, int flag, int nextc)
359{
360	char *extra = NULL;
361	unsigned char uc = (unsigned char)c;
362
363	_DIAGASSERT(dst != NULL);
364
365	MAKEEXTRALIST(flag, extra, "");
366	if (! extra) {
367		*dst = '\0';		/* can't create extra, return "" */
368		return dst;
369	}
370	if (flag & VIS_HTTPSTYLE)
371		dst = do_hvis(dst, uc, flag, nextc, extra);
372	else
373		dst = do_svis(dst, uc, flag, nextc, extra);
374	free(extra);
375	*dst = '\0';
376	return dst;
377}
378
379
380/*
381 * strvis, strvisx - visually encode characters from src into dst
382 *
383 *	Dst must be 4 times the size of src to account for possible
384 *	expansion.  The length of dst, not including the trailing NULL,
385 *	is returned.
386 *
387 *	Strvisx encodes exactly len bytes from src into dst.
388 *	This is useful for encoding a block of data.
389 */
390ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
391rk_strvis(char *dst, const char *src, int flag)
392{
393	char *extra = NULL;
394	int rv;
395
396	MAKEEXTRALIST(flag, extra, "");
397	if (!extra) {
398		*dst = '\0';		/* can't create extra, return "" */
399		return 0;
400	}
401	rv = strsvis(dst, src, flag, extra);
402	free(extra);
403	return rv;
404}
405
406
407ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
408rk_strvisx(char *dst, const char *src, size_t len, int flag)
409{
410	char *extra = NULL;
411	int rv;
412
413	MAKEEXTRALIST(flag, extra, "");
414	if (!extra) {
415		*dst = '\0';		/* can't create extra, return "" */
416		return 0;
417	}
418	rv = strsvisx(dst, src, len, flag, extra);
419	free(extra);
420	return rv;
421}
422#endif
423