vis.c revision 244401
1244230Sbrooks/*	$NetBSD: vis.c,v 1.45 2012/12/14 21:38:18 christos Exp $	*/
2241236Sbrooks
3241236Sbrooks/*-
4241236Sbrooks * Copyright (c) 1989, 1993
5241236Sbrooks *	The Regents of the University of California.  All rights reserved.
6241236Sbrooks *
7241236Sbrooks * Redistribution and use in source and binary forms, with or without
8241236Sbrooks * modification, are permitted provided that the following conditions
9241236Sbrooks * are met:
10241236Sbrooks * 1. Redistributions of source code must retain the above copyright
11241236Sbrooks *    notice, this list of conditions and the following disclaimer.
12241236Sbrooks * 2. Redistributions in binary form must reproduce the above copyright
13241236Sbrooks *    notice, this list of conditions and the following disclaimer in the
14241236Sbrooks *    documentation and/or other materials provided with the distribution.
15241236Sbrooks * 3. Neither the name of the University nor the names of its contributors
16241236Sbrooks *    may be used to endorse or promote products derived from this software
17241236Sbrooks *    without specific prior written permission.
18241236Sbrooks *
19241236Sbrooks * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20241236Sbrooks * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21241236Sbrooks * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22241236Sbrooks * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23241236Sbrooks * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24241236Sbrooks * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25241236Sbrooks * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26241236Sbrooks * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27241236Sbrooks * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28241236Sbrooks * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29241236Sbrooks * SUCH DAMAGE.
30241236Sbrooks */
31241236Sbrooks
32241236Sbrooks/*-
33241236Sbrooks * Copyright (c) 1999, 2005 The NetBSD Foundation, Inc.
34241236Sbrooks * All rights reserved.
35241236Sbrooks *
36241236Sbrooks * Redistribution and use in source and binary forms, with or without
37241236Sbrooks * modification, are permitted provided that the following conditions
38241236Sbrooks * are met:
39241236Sbrooks * 1. Redistributions of source code must retain the above copyright
40241236Sbrooks *    notice, this list of conditions and the following disclaimer.
41241236Sbrooks * 2. Redistributions in binary form must reproduce the above copyright
42241236Sbrooks *    notice, this list of conditions and the following disclaimer in the
43241236Sbrooks *    documentation and/or other materials provided with the distribution.
44241236Sbrooks *
45241236Sbrooks * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
46241236Sbrooks * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
47241236Sbrooks * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
48241236Sbrooks * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
49241236Sbrooks * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
50241236Sbrooks * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
51241236Sbrooks * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
52241236Sbrooks * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
53241236Sbrooks * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
54241236Sbrooks * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
55241236Sbrooks * POSSIBILITY OF SUCH DAMAGE.
56241236Sbrooks */
57241236Sbrooks
58241236Sbrooks#include <sys/cdefs.h>
59241236Sbrooks#if defined(LIBC_SCCS) && !defined(lint)
60244230Sbrooks__RCSID("$NetBSD: vis.c,v 1.45 2012/12/14 21:38:18 christos Exp $");
61241236Sbrooks#endif /* LIBC_SCCS and not lint */
62244401Sbrooks__FBSDID("$FreeBSD: head/contrib/libc-vis/vis.c 244401 2012-12-18 16:37:24Z brooks $");
63241236Sbrooks
64241236Sbrooks#include "namespace.h"
65241236Sbrooks#include <sys/types.h>
66241236Sbrooks
67241236Sbrooks#include <assert.h>
68241236Sbrooks#include <vis.h>
69241236Sbrooks#include <errno.h>
70241236Sbrooks#include <stdlib.h>
71241236Sbrooks
72244401Sbrooks#define	_DIAGASSERT(x)	assert(x)
73244401Sbrooks
74241236Sbrooks#ifdef __weak_alias
75241236Sbrooks__weak_alias(strvisx,_strvisx)
76241236Sbrooks#endif
77241236Sbrooks
78241236Sbrooks#if !HAVE_VIS || !HAVE_SVIS
79241236Sbrooks#include <ctype.h>
80241236Sbrooks#include <limits.h>
81241236Sbrooks#include <stdio.h>
82241236Sbrooks#include <string.h>
83241236Sbrooks
84241236Sbrooksstatic char *do_svis(char *, size_t *, int, int, int, const char *);
85241236Sbrooks
86241236Sbrooks#undef BELL
87241236Sbrooks#define BELL '\a'
88241236Sbrooks
89241236Sbrooks#define isoctal(c)	(((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
90241236Sbrooks#define iswhite(c)	(c == ' ' || c == '\t' || c == '\n')
91241236Sbrooks#define issafe(c)	(c == '\b' || c == BELL || c == '\r')
92241236Sbrooks#define xtoa(c)		"0123456789abcdef"[c]
93241236Sbrooks#define XTOA(c)		"0123456789ABCDEF"[c]
94241236Sbrooks
95244230Sbrooks#define MAXEXTRAS	9
96241236Sbrooks
97241236Sbrooks#define MAKEEXTRALIST(flag, extra, orig_str)				      \
98241236Sbrooksdo {									      \
99241236Sbrooks	const char *orig = orig_str;					      \
100241236Sbrooks	const char *o = orig;						      \
101241236Sbrooks	char *e;							      \
102241236Sbrooks	while (*o++)							      \
103241236Sbrooks		continue;						      \
104241236Sbrooks	extra = malloc((size_t)((o - orig) + MAXEXTRAS));		      \
105241236Sbrooks	if (!extra) break;						      \
106241236Sbrooks	for (o = orig, e = extra; (*e++ = *o++) != '\0';)		      \
107241236Sbrooks		continue;						      \
108241236Sbrooks	e--;								      \
109244230Sbrooks	if (flag & VIS_GLOB) {						      \
110244230Sbrooks		*e++ = '*';						      \
111244230Sbrooks		*e++ = '?';						      \
112244230Sbrooks		*e++ = '[';						      \
113244230Sbrooks		*e++ = '#';						      \
114244230Sbrooks	}								      \
115241236Sbrooks	if (flag & VIS_SP) *e++ = ' ';					      \
116241236Sbrooks	if (flag & VIS_TAB) *e++ = '\t';				      \
117241236Sbrooks	if (flag & VIS_NL) *e++ = '\n';					      \
118241236Sbrooks	if ((flag & VIS_NOSLASH) == 0) *e++ = '\\';			      \
119241236Sbrooks	*e = '\0';							      \
120241236Sbrooks} while (/*CONSTCOND*/0)
121241236Sbrooks
122241236Sbrooks/*
123241236Sbrooks * This is do_hvis, for HTTP style (RFC 1808)
124241236Sbrooks */
125241236Sbrooksstatic char *
126241236Sbrooksdo_hvis(char *dst, size_t *dlen, int c, int flag, int nextc, const char *extra)
127241236Sbrooks{
128241236Sbrooks
129241236Sbrooks	if ((isascii(c) && isalnum(c))
130241236Sbrooks	    /* safe */
131241236Sbrooks	    || c == '$' || c == '-' || c == '_' || c == '.' || c == '+'
132241236Sbrooks	    /* extra */
133241236Sbrooks	    || c == '!' || c == '*' || c == '\'' || c == '(' || c == ')'
134241236Sbrooks	    || c == ',') {
135241236Sbrooks		dst = do_svis(dst, dlen, c, flag, nextc, extra);
136241236Sbrooks	} else {
137241236Sbrooks		if (dlen) {
138241236Sbrooks			if (*dlen < 3)
139241236Sbrooks				return NULL;
140241236Sbrooks			*dlen -= 3;
141241236Sbrooks		}
142241236Sbrooks		*dst++ = '%';
143241236Sbrooks		*dst++ = xtoa(((unsigned int)c >> 4) & 0xf);
144241236Sbrooks		*dst++ = xtoa((unsigned int)c & 0xf);
145241236Sbrooks	}
146241236Sbrooks
147241236Sbrooks	return dst;
148241236Sbrooks}
149241236Sbrooks
150241236Sbrooks/*
151241236Sbrooks * This is do_mvis, for Quoted-Printable MIME (RFC 2045)
152241236Sbrooks * NB: No handling of long lines or CRLF.
153241236Sbrooks */
154241236Sbrooksstatic char *
155241236Sbrooksdo_mvis(char *dst, size_t *dlen, int c, int flag, int nextc, const char *extra)
156241236Sbrooks{
157241236Sbrooks	if ((c != '\n') &&
158241236Sbrooks	    /* Space at the end of the line */
159241236Sbrooks	    ((isspace(c) && (nextc == '\r' || nextc == '\n')) ||
160241236Sbrooks	    /* Out of range */
161241236Sbrooks	    (!isspace(c) && (c < 33 || (c > 60 && c < 62) || c > 126)) ||
162241236Sbrooks	    /* Specific char to be escaped */
163241236Sbrooks	    strchr("#$@[\\]^`{|}~", c) != NULL)) {
164241236Sbrooks		if (dlen) {
165241236Sbrooks			if (*dlen < 3)
166241236Sbrooks				return NULL;
167241236Sbrooks			*dlen -= 3;
168241236Sbrooks		}
169241236Sbrooks		*dst++ = '=';
170241236Sbrooks		*dst++ = XTOA(((unsigned int)c >> 4) & 0xf);
171241236Sbrooks		*dst++ = XTOA((unsigned int)c & 0xf);
172241236Sbrooks	} else {
173241236Sbrooks		dst = do_svis(dst, dlen, c, flag, nextc, extra);
174241236Sbrooks	}
175241236Sbrooks	return dst;
176241236Sbrooks}
177241236Sbrooks
178241236Sbrooks/*
179241236Sbrooks * This is do_vis, the central code of vis.
180241236Sbrooks * dst:	      Pointer to the destination buffer
181241236Sbrooks * c:	      Character to encode
182241236Sbrooks * flag:      Flag word
183241236Sbrooks * nextc:     The character following 'c'
184241236Sbrooks * extra:     Pointer to the list of extra characters to be
185241236Sbrooks *	      backslash-protected.
186241236Sbrooks */
187241236Sbrooksstatic char *
188241236Sbrooksdo_svis(char *dst, size_t *dlen, int c, int flag, int nextc, const char *extra)
189241236Sbrooks{
190241236Sbrooks	int isextra;
191241236Sbrooks	size_t odlen = dlen ? *dlen : 0;
192241236Sbrooks
193241236Sbrooks	isextra = strchr(extra, c) != NULL;
194241236Sbrooks#define HAVE(x) \
195241236Sbrooks	do { \
196241236Sbrooks		if (dlen) { \
197241236Sbrooks			if (*dlen < (x)) \
198241236Sbrooks				goto out; \
199241236Sbrooks			*dlen -= (x); \
200241236Sbrooks		} \
201241236Sbrooks	} while (/*CONSTCOND*/0)
202241236Sbrooks	if (!isextra && isascii(c) && (isgraph(c) || iswhite(c) ||
203241236Sbrooks	    ((flag & VIS_SAFE) && issafe(c)))) {
204241236Sbrooks		HAVE(1);
205241236Sbrooks		*dst++ = c;
206241236Sbrooks		return dst;
207241236Sbrooks	}
208241236Sbrooks	if (flag & VIS_CSTYLE) {
209241236Sbrooks		HAVE(2);
210241236Sbrooks		switch (c) {
211241236Sbrooks		case '\n':
212241236Sbrooks			*dst++ = '\\'; *dst++ = 'n';
213241236Sbrooks			return dst;
214241236Sbrooks		case '\r':
215241236Sbrooks			*dst++ = '\\'; *dst++ = 'r';
216241236Sbrooks			return dst;
217241236Sbrooks		case '\b':
218241236Sbrooks			*dst++ = '\\'; *dst++ = 'b';
219241236Sbrooks			return dst;
220241236Sbrooks		case BELL:
221241236Sbrooks			*dst++ = '\\'; *dst++ = 'a';
222241236Sbrooks			return dst;
223241236Sbrooks		case '\v':
224241236Sbrooks			*dst++ = '\\'; *dst++ = 'v';
225241236Sbrooks			return dst;
226241236Sbrooks		case '\t':
227241236Sbrooks			*dst++ = '\\'; *dst++ = 't';
228241236Sbrooks			return dst;
229241236Sbrooks		case '\f':
230241236Sbrooks			*dst++ = '\\'; *dst++ = 'f';
231241236Sbrooks			return dst;
232241236Sbrooks		case ' ':
233241236Sbrooks			*dst++ = '\\'; *dst++ = 's';
234241236Sbrooks			return dst;
235241236Sbrooks		case '\0':
236241236Sbrooks			*dst++ = '\\'; *dst++ = '0';
237241236Sbrooks			if (isoctal(nextc)) {
238241236Sbrooks				HAVE(2);
239241236Sbrooks				*dst++ = '0';
240241236Sbrooks				*dst++ = '0';
241241236Sbrooks			}
242241236Sbrooks			return dst;
243241236Sbrooks		default:
244241236Sbrooks			if (isgraph(c)) {
245241236Sbrooks				*dst++ = '\\'; *dst++ = c;
246241236Sbrooks				return dst;
247241236Sbrooks			}
248241236Sbrooks			if (dlen)
249241236Sbrooks				*dlen = odlen;
250241236Sbrooks		}
251241236Sbrooks	}
252241236Sbrooks	if (isextra || ((c & 0177) == ' ') || (flag & VIS_OCTAL)) {
253241236Sbrooks		HAVE(4);
254241236Sbrooks		*dst++ = '\\';
255241236Sbrooks		*dst++ = (u_char)(((u_int32_t)(u_char)c >> 6) & 03) + '0';
256241236Sbrooks		*dst++ = (u_char)(((u_int32_t)(u_char)c >> 3) & 07) + '0';
257241236Sbrooks		*dst++ =			     (c	      & 07) + '0';
258241236Sbrooks	} else {
259241236Sbrooks		if ((flag & VIS_NOSLASH) == 0) {
260241236Sbrooks			HAVE(1);
261241236Sbrooks			*dst++ = '\\';
262241236Sbrooks		}
263241236Sbrooks
264241236Sbrooks		if (c & 0200) {
265241236Sbrooks			HAVE(1);
266241236Sbrooks			c &= 0177; *dst++ = 'M';
267241236Sbrooks		}
268241236Sbrooks
269241236Sbrooks		if (iscntrl(c)) {
270241236Sbrooks			HAVE(2);
271241236Sbrooks			*dst++ = '^';
272241236Sbrooks			if (c == 0177)
273241236Sbrooks				*dst++ = '?';
274241236Sbrooks			else
275241236Sbrooks				*dst++ = c + '@';
276241236Sbrooks		} else {
277241236Sbrooks			HAVE(2);
278241236Sbrooks			*dst++ = '-'; *dst++ = c;
279241236Sbrooks		}
280241236Sbrooks	}
281241236Sbrooks	return dst;
282241236Sbrooksout:
283241236Sbrooks	*dlen = odlen;
284241236Sbrooks	return NULL;
285241236Sbrooks}
286241236Sbrooks
287241236Sbrookstypedef char *(*visfun_t)(char *, size_t *, int, int, int, const char *);
288241236Sbrooks
289241236Sbrooks/*
290241236Sbrooks * Return the appropriate encoding function depending on the flags given.
291241236Sbrooks */
292241236Sbrooksstatic visfun_t
293241236Sbrooksgetvisfun(int flag)
294241236Sbrooks{
295241236Sbrooks	if (flag & VIS_HTTPSTYLE)
296241236Sbrooks		return do_hvis;
297241236Sbrooks	if (flag & VIS_MIMESTYLE)
298241236Sbrooks		return do_mvis;
299241236Sbrooks	return do_svis;
300241236Sbrooks}
301241236Sbrooks
302241236Sbrooks/*
303241236Sbrooks * isnvis - visually encode characters, also encoding the characters
304241236Sbrooks *	  pointed to by `extra'
305241236Sbrooks */
306241236Sbrooksstatic char *
307241236Sbrooksisnvis(char *dst, size_t *dlen, int c, int flag, int nextc, const char *extra)
308241236Sbrooks{
309241236Sbrooks	char *nextra = NULL;
310241236Sbrooks	visfun_t f;
311241236Sbrooks
312241236Sbrooks	_DIAGASSERT(dst != NULL);
313241236Sbrooks	_DIAGASSERT(extra != NULL);
314241236Sbrooks	MAKEEXTRALIST(flag, nextra, extra);
315241236Sbrooks	if (!nextra) {
316241236Sbrooks		if (dlen && *dlen == 0) {
317241236Sbrooks			errno = ENOSPC;
318241236Sbrooks			return NULL;
319241236Sbrooks		}
320241236Sbrooks		*dst = '\0';		/* can't create nextra, return "" */
321241236Sbrooks		return dst;
322241236Sbrooks	}
323241236Sbrooks	f = getvisfun(flag);
324241236Sbrooks	dst = (*f)(dst, dlen, c, flag, nextc, nextra);
325241236Sbrooks	free(nextra);
326241236Sbrooks	if (dst == NULL || (dlen && *dlen == 0)) {
327241236Sbrooks		errno = ENOSPC;
328241236Sbrooks		return NULL;
329241236Sbrooks	}
330241236Sbrooks	*dst = '\0';
331241236Sbrooks	return dst;
332241236Sbrooks}
333241236Sbrooks
334241236Sbrookschar *
335241236Sbrookssvis(char *dst, int c, int flag, int nextc, const char *extra)
336241236Sbrooks{
337241236Sbrooks	return isnvis(dst, NULL, c, flag, nextc, extra);
338241236Sbrooks}
339241236Sbrooks
340241236Sbrookschar *
341241236Sbrookssnvis(char *dst, size_t dlen, int c, int flag, int nextc, const char *extra)
342241236Sbrooks{
343241236Sbrooks	return isnvis(dst, &dlen, c, flag, nextc, extra);
344241236Sbrooks}
345241236Sbrooks
346241236Sbrooks
347241236Sbrooks/*
348241236Sbrooks * strsvis, strsvisx - visually encode characters from src into dst
349241236Sbrooks *
350241236Sbrooks *	Extra is a pointer to a \0-terminated list of characters to
351241236Sbrooks *	be encoded, too. These functions are useful e. g. to
352241236Sbrooks *	encode strings in such a way so that they are not interpreted
353241236Sbrooks *	by a shell.
354241236Sbrooks *
355241236Sbrooks *	Dst must be 4 times the size of src to account for possible
356241236Sbrooks *	expansion.  The length of dst, not including the trailing NULL,
357241236Sbrooks *	is returned.
358241236Sbrooks *
359241236Sbrooks *	Strsvisx encodes exactly len bytes from src into dst.
360241236Sbrooks *	This is useful for encoding a block of data.
361241236Sbrooks */
362241236Sbrooksstatic int
363241236Sbrooksistrsnvis(char *dst, size_t *dlen, const char *csrc, int flag, const char *extra)
364241236Sbrooks{
365241236Sbrooks	int c;
366241236Sbrooks	char *start;
367241236Sbrooks	char *nextra = NULL;
368241236Sbrooks	const unsigned char *src = (const unsigned char *)csrc;
369241236Sbrooks	visfun_t f;
370241236Sbrooks
371241236Sbrooks	_DIAGASSERT(dst != NULL);
372241236Sbrooks	_DIAGASSERT(src != NULL);
373241236Sbrooks	_DIAGASSERT(extra != NULL);
374241236Sbrooks	MAKEEXTRALIST(flag, nextra, extra);
375241236Sbrooks	if (!nextra) {
376241236Sbrooks		*dst = '\0';		/* can't create nextra, return "" */
377241236Sbrooks		return 0;
378241236Sbrooks	}
379241236Sbrooks	f = getvisfun(flag);
380241236Sbrooks	for (start = dst; (c = *src++) != '\0'; /* empty */) {
381241236Sbrooks		dst = (*f)(dst, dlen, c, flag, *src, nextra);
382241236Sbrooks		if (dst == NULL) {
383241236Sbrooks			errno = ENOSPC;
384241236Sbrooks			return -1;
385241236Sbrooks		}
386241236Sbrooks	}
387241236Sbrooks	free(nextra);
388241236Sbrooks	if (dlen && *dlen == 0) {
389241236Sbrooks		errno = ENOSPC;
390241236Sbrooks		return -1;
391241236Sbrooks	}
392241236Sbrooks	*dst = '\0';
393241236Sbrooks	return (int)(dst - start);
394241236Sbrooks}
395241236Sbrooks
396241236Sbrooksint
397241236Sbrooksstrsvis(char *dst, const char *csrc, int flag, const char *extra)
398241236Sbrooks{
399241236Sbrooks	return istrsnvis(dst, NULL, csrc, flag, extra);
400241236Sbrooks}
401241236Sbrooks
402241236Sbrooksint
403241236Sbrooksstrsnvis(char *dst, size_t dlen, const char *csrc, int flag, const char *extra)
404241236Sbrooks{
405241236Sbrooks	return istrsnvis(dst, &dlen, csrc, flag, extra);
406241236Sbrooks}
407241236Sbrooks
408241236Sbrooksstatic int
409241236Sbrooksistrsnvisx(char *dst, size_t *dlen, const char *csrc, size_t len, int flag,
410241236Sbrooks    const char *extra)
411241236Sbrooks{
412241236Sbrooks	unsigned char c;
413241236Sbrooks	char *start;
414241236Sbrooks	char *nextra = NULL;
415241236Sbrooks	const unsigned char *src = (const unsigned char *)csrc;
416241236Sbrooks	visfun_t f;
417241236Sbrooks
418241236Sbrooks	_DIAGASSERT(dst != NULL);
419241236Sbrooks	_DIAGASSERT(src != NULL);
420241236Sbrooks	_DIAGASSERT(extra != NULL);
421241236Sbrooks	MAKEEXTRALIST(flag, nextra, extra);
422241236Sbrooks	if (! nextra) {
423241236Sbrooks		if (dlen && *dlen == 0) {
424241236Sbrooks			errno = ENOSPC;
425241236Sbrooks			return -1;
426241236Sbrooks		}
427241236Sbrooks		*dst = '\0';		/* can't create nextra, return "" */
428241236Sbrooks		return 0;
429241236Sbrooks	}
430241236Sbrooks
431241236Sbrooks	f = getvisfun(flag);
432241236Sbrooks	for (start = dst; len > 0; len--) {
433241236Sbrooks		c = *src++;
434241236Sbrooks		dst = (*f)(dst, dlen, c, flag, len > 1 ? *src : '\0', nextra);
435241236Sbrooks		if (dst == NULL) {
436241236Sbrooks			errno = ENOSPC;
437241236Sbrooks			return -1;
438241236Sbrooks		}
439241236Sbrooks	}
440241236Sbrooks	free(nextra);
441241236Sbrooks	if (dlen && *dlen == 0) {
442241236Sbrooks		errno = ENOSPC;
443241236Sbrooks		return -1;
444241236Sbrooks	}
445241236Sbrooks	*dst = '\0';
446241236Sbrooks	return (int)(dst - start);
447241236Sbrooks}
448241236Sbrooks
449241236Sbrooksint
450241236Sbrooksstrsvisx(char *dst, const char *csrc, size_t len, int flag, const char *extra)
451241236Sbrooks{
452241236Sbrooks	return istrsnvisx(dst, NULL, csrc, len, flag, extra);
453241236Sbrooks}
454241236Sbrooks
455241236Sbrooksint
456241236Sbrooksstrsnvisx(char *dst, size_t dlen, const char *csrc, size_t len, int flag,
457241236Sbrooks    const char *extra)
458241236Sbrooks{
459241236Sbrooks	return istrsnvisx(dst, &dlen, csrc, len, flag, extra);
460241236Sbrooks}
461241236Sbrooks#endif
462241236Sbrooks
463241236Sbrooks#if !HAVE_VIS
464241236Sbrooks/*
465241236Sbrooks * vis - visually encode characters
466241236Sbrooks */
467241236Sbrooksstatic char *
468241236Sbrooksinvis(char *dst, size_t *dlen, int c, int flag, int nextc)
469241236Sbrooks{
470241236Sbrooks	char *extra = NULL;
471241236Sbrooks	unsigned char uc = (unsigned char)c;
472241236Sbrooks	visfun_t f;
473241236Sbrooks
474241236Sbrooks	_DIAGASSERT(dst != NULL);
475241236Sbrooks
476241236Sbrooks	MAKEEXTRALIST(flag, extra, "");
477241236Sbrooks	if (! extra) {
478241236Sbrooks		if (dlen && *dlen == 0) {
479241236Sbrooks			errno = ENOSPC;
480241236Sbrooks			return NULL;
481241236Sbrooks		}
482241236Sbrooks		*dst = '\0';		/* can't create extra, return "" */
483241236Sbrooks		return dst;
484241236Sbrooks	}
485241236Sbrooks	f = getvisfun(flag);
486241236Sbrooks	dst = (*f)(dst, dlen, uc, flag, nextc, extra);
487241236Sbrooks	free(extra);
488241236Sbrooks	if (dst == NULL || (dlen && *dlen == 0)) {
489241236Sbrooks		errno = ENOSPC;
490241236Sbrooks		return NULL;
491241236Sbrooks	}
492241236Sbrooks	*dst = '\0';
493241236Sbrooks	return dst;
494241236Sbrooks}
495241236Sbrooks
496241236Sbrookschar *
497241236Sbrooksvis(char *dst, int c, int flag, int nextc)
498241236Sbrooks{
499241236Sbrooks	return invis(dst, NULL, c, flag, nextc);
500241236Sbrooks}
501241236Sbrooks
502241236Sbrookschar *
503241236Sbrooksnvis(char *dst, size_t dlen, int c, int flag, int nextc)
504241236Sbrooks{
505241236Sbrooks	return invis(dst, &dlen, c, flag, nextc);
506241236Sbrooks}
507241236Sbrooks
508241236Sbrooks
509241236Sbrooks/*
510241236Sbrooks * strvis, strvisx - visually encode characters from src into dst
511241236Sbrooks *
512241236Sbrooks *	Dst must be 4 times the size of src to account for possible
513241236Sbrooks *	expansion.  The length of dst, not including the trailing NULL,
514241236Sbrooks *	is returned.
515241236Sbrooks *
516241236Sbrooks *	Strvisx encodes exactly len bytes from src into dst.
517241236Sbrooks *	This is useful for encoding a block of data.
518241236Sbrooks */
519241236Sbrooksstatic int
520241236Sbrooksistrnvis(char *dst, size_t *dlen, const char *src, int flag)
521241236Sbrooks{
522241236Sbrooks	char *extra = NULL;
523241236Sbrooks	int rv;
524241236Sbrooks
525241236Sbrooks	MAKEEXTRALIST(flag, extra, "");
526241236Sbrooks	if (!extra) {
527241236Sbrooks		if (dlen && *dlen == 0) {
528241236Sbrooks			errno = ENOSPC;
529241236Sbrooks			return -1;
530241236Sbrooks		}
531241236Sbrooks		*dst = '\0';		/* can't create extra, return "" */
532241236Sbrooks		return 0;
533241236Sbrooks	}
534241236Sbrooks	rv = istrsnvis(dst, dlen, src, flag, extra);
535241236Sbrooks	free(extra);
536241236Sbrooks	return rv;
537241236Sbrooks}
538241236Sbrooks
539241236Sbrooksint
540241236Sbrooksstrvis(char *dst, const char *src, int flag)
541241236Sbrooks{
542241236Sbrooks	return istrnvis(dst, NULL, src, flag);
543241236Sbrooks}
544241236Sbrooks
545241236Sbrooksint
546241236Sbrooksstrnvis(char *dst, size_t dlen, const char *src, int flag)
547241236Sbrooks{
548241236Sbrooks	return istrnvis(dst, &dlen, src, flag);
549241236Sbrooks}
550241236Sbrooks
551241236Sbrooksstatic int
552241236Sbrooksistrnvisx(char *dst, size_t *dlen, const char *src, size_t len, int flag)
553241236Sbrooks{
554241236Sbrooks	char *extra = NULL;
555241236Sbrooks	int rv;
556241236Sbrooks
557241236Sbrooks	MAKEEXTRALIST(flag, extra, "");
558241236Sbrooks	if (!extra) {
559241236Sbrooks		if (dlen && *dlen == 0) {
560241236Sbrooks			errno = ENOSPC;
561241236Sbrooks			return -1;
562241236Sbrooks		}
563241236Sbrooks		*dst = '\0';		/* can't create extra, return "" */
564241236Sbrooks		return 0;
565241236Sbrooks	}
566241236Sbrooks	rv = istrsnvisx(dst, dlen, src, len, flag, extra);
567241236Sbrooks	free(extra);
568241236Sbrooks	return rv;
569241236Sbrooks}
570241236Sbrooks
571241236Sbrooksint
572241236Sbrooksstrvisx(char *dst, const char *src, size_t len, int flag)
573241236Sbrooks{
574241236Sbrooks	return istrnvisx(dst, NULL, src, len, flag);
575241236Sbrooks}
576241236Sbrooks
577241236Sbrooksint
578241236Sbrooksstrnvisx(char *dst, size_t dlen, const char *src, size_t len, int flag)
579241236Sbrooks{
580241236Sbrooks	return istrnvisx(dst, &dlen, src, len, flag);
581241236Sbrooks}
582241236Sbrooks
583241236Sbrooks#endif
584