1 /* $NetBSD: vis.c,v 1.37 2008/07/25 22:29:23 dsl Exp $ */
4 * Copyright (c) 1989, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
33 * Copyright (c) 1999, 2005 The NetBSD Foundation, Inc.
34 * All rights reserved.
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
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.
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.
58 #define _DEFAULT_SOURCE
65 #define _DIAGASSERT(X)
68 #include <sys/types.h>
81 #if !HAVE_VIS || !HAVE_SVIS
88 #if !HAVE_VIS || !HAVE_SVIS || TEST
90 * We use makextralist() in main(), so we need it even if we have all the VIS
91 * routines in the host's C libraries.
94 /* 5 is for VIS_SP, VIS_TAB, VIS_NL, VIS_DQ, and VIS_NOSLASH */
95 #define MAXEXTRAS (sizeof(char_glob) - 1 + sizeof(char_shell) - 1 + 5)
98 #define VIS_SHELL 0x2000
101 #define VIS_GLOB 0x0100
105 #define VIS_SP 0x0004 /* also encode space */
108 #define VIS_TAB 0x0008 /* also encode tab */
111 #define VIS_NL 0x0010 /* also encode newline */
114 #define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL)
117 #define VIS_SAFE 0x0020 /* only encode "unsafe" characters */
120 #define VIS_DQ 0x8000 /* also encode double quotes */
125 * Expand list of extra characters to not visually encode.
128 makeextralist(int flags
, const char *src
)
130 static const char char_glob
[] = "*?[#";
131 static const char char_shell
[] = "'`\";&<>()|{}]\\$!^~";
136 if ((dst
= d
= calloc(1, len
+ MAXEXTRAS
+ 1)) == NULL
)
139 memcpy(dst
, src
, len
);
142 if (flags
& VIS_GLOB
) {
143 memcpy(d
, char_glob
, sizeof(char_glob
) - 1);
144 d
+= sizeof(char_glob
) - 1;
146 if (flags
& VIS_SHELL
) {
147 memcpy(d
, char_shell
, sizeof(char_shell
) - 1);
148 d
+= sizeof(char_shell
) - 1;
151 if (flags
& VIS_SP
) *d
++ = ' ';
152 if (flags
& VIS_TAB
) *d
++ = '\t';
153 if (flags
& VIS_NL
) *d
++ = '\n';
154 if (flags
& VIS_DQ
) *d
++ = '"';
155 if ((flags
& VIS_NOSLASH
) == 0) *d
++ = '\\';
161 #if !HAVE_VIS || !HAVE_SVIS
162 static char *do_svis(char *, int, int, int, const char *);
165 #if defined(__STDC__)
171 ROKEN_LIB_FUNCTION
char * ROKEN_LIB_CALL
172 rk_vis (char *, int, int, int);
173 ROKEN_LIB_FUNCTION
char * ROKEN_LIB_CALL
174 rk_svis (char *, int, int, int, const char *);
175 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
176 rk_strvis (char *, const char *, int);
177 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
178 rk_strsvis (char *, const char *, int, const char *);
179 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
180 rk_strvisx (char *, const char *, size_t, int);
181 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
182 rk_strsvisx (char *, const char *, size_t, int, const char *);
184 #define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
185 #define iswhite(c) (c == ' ' || c == '\t' || c == '\n')
186 #define issafe(c) (c == '\b' || c == BELL || c == '\r')
187 #define xtoa(c) "0123456789abcdef"[c]
190 * This is do_hvis, for HTTP style (RFC 1808)
193 do_hvis(char *dst
, int c
, int flag
, int nextc
, const char *extra
)
195 if (!isascii(c
) || !isalnum(c
) || strchr("$-_.+!*'(),", c
) != NULL
||
198 *dst
++ = xtoa(((unsigned int)c
>> 4) & 0xf);
199 *dst
++ = xtoa((unsigned int)c
& 0xf);
201 dst
= do_svis(dst
, c
, flag
, nextc
, extra
);
207 * This is do_vis, the central code of vis.
208 * dst: Pointer to the destination buffer
209 * c: Character to encode
211 * nextc: The character following 'c'
212 * extra: Pointer to the list of extra characters to be
213 * backslash-protected.
216 do_svis(char *dst
, int c
, int flag
, int nextc
, const char *extra
)
219 isextra
= strchr(extra
, c
) != NULL
;
220 if (!isextra
&& isascii(c
) && (isgraph(c
) || iswhite(c
) ||
221 ((flag
& VIS_SAFE
) && issafe(c
)))) {
225 if (flag
& VIS_CSTYLE
) {
228 *dst
++ = '\\'; *dst
++ = 'n';
231 *dst
++ = '\\'; *dst
++ = 'r';
234 *dst
++ = '\\'; *dst
++ = 'b';
237 *dst
++ = '\\'; *dst
++ = 'a';
240 *dst
++ = '\\'; *dst
++ = 'v';
243 *dst
++ = '\\'; *dst
++ = 't';
246 *dst
++ = '\\'; *dst
++ = 'f';
249 *dst
++ = '\\'; *dst
++ = 's';
252 *dst
++ = '\\'; *dst
++ = '0';
253 if (isoctal(nextc
)) {
260 *dst
++ = '\\'; *dst
++ = c
;
265 if (isextra
|| ((c
& 0177) == ' ') || (flag
& VIS_OCTAL
)) {
267 *dst
++ = (u_char
)(((unsigned int)(u_char
)c
>> 6) & 03) + '0';
268 *dst
++ = (u_char
)(((unsigned int)(u_char
)c
>> 3) & 07) + '0';
269 *dst
++ = (u_char
)( c
& 07) + '0';
271 if ((flag
& VIS_NOSLASH
) == 0) *dst
++ = '\\';
273 c
&= 0177; *dst
++ = 'M';
282 *dst
++ = '-'; *dst
++ = c
;
290 * svis - visually encode characters, also encoding the characters
291 * pointed to by `extra'
293 ROKEN_LIB_FUNCTION
char * ROKEN_LIB_CALL
294 rk_svis(char *dst
, int c
, int flag
, int nextc
, const char *extra
)
298 _DIAGASSERT(dst
!= NULL
);
299 _DIAGASSERT(extra
!= NULL
);
300 nextra
= makeextralist(flag
, extra
);
302 *dst
= '\0'; /* can't create nextra, return "" */
305 if (flag
& VIS_HTTPSTYLE
)
306 dst
= do_hvis(dst
, c
, flag
, nextc
, nextra
);
308 dst
= do_svis(dst
, c
, flag
, nextc
, nextra
);
316 * strsvis, strsvisx - visually encode characters from src into dst
318 * Extra is a pointer to a \0-terminated list of characters to
319 * be encoded, too. These functions are useful e. g. to
320 * encode strings in such a way so that they are not interpreted
323 * Dst must be 4 times the size of src to account for possible
324 * expansion. The length of dst, not including the trailing NULL,
327 * Strsvisx encodes exactly len bytes from src into dst.
328 * This is useful for encoding a block of data.
331 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
332 rk_strsvis(char *dst
, const char *csrc
, int flag
, const char *extra
)
337 const unsigned char *src
= (const unsigned char *)csrc
;
339 _DIAGASSERT(dst
!= NULL
);
340 _DIAGASSERT(src
!= NULL
);
341 _DIAGASSERT(extra
!= NULL
);
342 nextra
= makeextralist(flag
, extra
);
344 *dst
= '\0'; /* can't create nextra, return "" */
347 if (flag
& VIS_HTTPSTYLE
) {
348 for (start
= dst
; (c
= *src
++) != '\0'; /* empty */)
349 dst
= do_hvis(dst
, c
, flag
, *src
, nextra
);
351 for (start
= dst
; (c
= *src
++) != '\0'; /* empty */)
352 dst
= do_svis(dst
, c
, flag
, *src
, nextra
);
356 return (dst
- start
);
360 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
361 rk_strsvisx(char *dst
, const char *csrc
, size_t len
, int flag
, const char *extra
)
366 const unsigned char *src
= (const unsigned char *)csrc
;
368 _DIAGASSERT(dst
!= NULL
);
369 _DIAGASSERT(src
!= NULL
);
370 _DIAGASSERT(extra
!= NULL
);
371 nextra
= makeextralist(flag
, extra
);
373 *dst
= '\0'; /* can't create nextra, return "" */
377 if (flag
& VIS_HTTPSTYLE
) {
378 for (start
= dst
; len
> 0; len
--) {
380 dst
= do_hvis(dst
, c
, flag
, *src
, nextra
);
383 for (start
= dst
; len
> 0; len
--) {
385 dst
= do_svis(dst
, c
, flag
, *src
, nextra
);
390 return (dst
- start
);
395 * Heimdal innovations: functions that allocate or reallocate a destination
396 * buffer as needed. Based on OpenBSD's stravis().
399 #include <vis-extras.h>
401 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
402 rk_strasvis(char **out
, const char *csrc
, int flag
, const char *extra
)
404 return rk_strasvisx(out
, csrc
, strlen(csrc
), flag
, extra
);
407 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
408 rk_strrasvis(char **out
, size_t *outsz
, const char *csrc
, int flag
, const char *extra
)
410 return rk_strrasvisx(out
, outsz
, csrc
, strlen(csrc
), flag
, extra
);
413 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
414 rk_strasvisx(char **out
, const char *csrc
, size_t len
, int flag
, const char *extra
)
419 return rk_strrasvisx(out
, &sz
, csrc
, strlen(csrc
), flag
, extra
);
422 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
423 rk_strrasvisx(char **out
,
430 size_t want
= 4 * len
+ 4;
431 size_t have
= *outsz
;
435 _DIAGASSERT(dst
!= NULL
);
436 _DIAGASSERT(src
!= NULL
);
437 _DIAGASSERT(extra
!= NULL
);
438 if (want
< len
|| want
> INT_MAX
) {
443 if ((s
= realloc(s
, want
)) == NULL
)
452 **out
= '\0'; /* Makes source debugging nicer, that's all */
453 r
= strsvisx(*out
, csrc
, len
, flag
, extra
);
459 * vis - visually encode characters
461 ROKEN_LIB_FUNCTION
char * ROKEN_LIB_CALL
462 rk_vis(char *dst
, int c
, int flag
, int nextc
)
465 unsigned char uc
= (unsigned char)c
;
467 _DIAGASSERT(dst
!= NULL
);
469 extra
= makeextralist(flag
, "");
471 *dst
= '\0'; /* can't create extra, return "" */
474 if (flag
& VIS_HTTPSTYLE
)
475 dst
= do_hvis(dst
, uc
, flag
, nextc
, extra
);
477 dst
= do_svis(dst
, uc
, flag
, nextc
, extra
);
485 * strvis, strvisx - visually encode characters from src into dst
487 * Dst must be 4 times the size of src to account for possible
488 * expansion. The length of dst, not including the trailing NULL,
491 * Strvisx encodes exactly len bytes from src into dst.
492 * This is useful for encoding a block of data.
494 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
495 rk_strvis(char *dst
, const char *src
, int flag
)
500 extra
= makeextralist(flag
, "");
502 *dst
= '\0'; /* can't create extra, return "" */
505 rv
= strsvis(dst
, src
, flag
, extra
);
511 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
512 rk_strvisx(char *dst
, const char *src
, size_t len
, int flag
)
517 extra
= makeextralist(flag
, "");
519 *dst
= '\0'; /* can't create extra, return "" */
522 rv
= strsvisx(dst
, src
, len
, flag
, extra
);
529 static const char *extra_arg
= "";
530 static int cstyle_flag
;
531 static int glob_flag
;
532 static int help_flag
;
533 static int http_flag
;
534 static int httponly_flag
;
535 static int line_flag
;
536 static int octal_flag
;
537 static int safe_flag
;
538 static int shell_flag
;
539 static int stdin_flag
;
541 static int whitespace_flag
;
544 * The short options are compatible with a subset of the FreeBSD contrib
545 * vis(1). Heimdal additions have long option names only.
547 static struct getargs args
[] = {
548 { "c", 'C', arg_flag
, &cstyle_flag
, "C style", "C style" },
549 { "extra", 'e', arg_string
, &extra_arg
, "also encode extra", "also encode extra"},
550 { "glob", 'g', arg_flag
, &glob_flag
, "escape glob specials", "escape glob specials" },
551 { "help", 0, arg_flag
, &help_flag
, "help", "help"},
552 { "line", 0, arg_flag
, &line_flag
, "read and escape stdin without escaping newlines", NULL
},
553 { "octal", 'o', arg_flag
, &octal_flag
, "octal escape", "octal escape" },
554 { "safe", 's', arg_flag
, &safe_flag
, "only encode \"unsafe\" characters", "only encode \"unsafe\" characters" },
555 { "shell", 'S', arg_flag
, &shell_flag
, "encode shell meta-characters", "encode shell meta-characters" },
556 { "stdin", 0, arg_flag
, &stdin_flag
, "read and escape stdin", NULL
},
557 { "tab", 't', arg_flag
, &tab_flag
, "encode tabs", "encode tabs" },
558 { "url", 'h', arg_flag
, &http_flag
, "url escape", "url escape" },
559 { "url-only", 0, arg_flag
, &httponly_flag
, "url escape", "url escape" },
560 { "whitespace", 'w', arg_flag
, &whitespace_flag
, "encode whitespace", "encode whitespace" },
563 static size_t num_args
= sizeof(args
)/sizeof(args
[0]);
566 main(int argc
, char **argv
)
575 if (getarg(args
, num_args
, argc
, argv
, &goptind
) || help_flag
) {
576 arg_printusage(args
, num_args
, NULL
, "strings...");
577 return help_flag
? 0 : 1;
583 if (argc
== 0 && !stdin_flag
&& !line_flag
) {
584 arg_printusage(args
, num_args
, NULL
, "strings...");
588 if (http_flag
&& cstyle_flag
)
589 errx(1, "--http and --cstyle are mutually exclusive");
591 flags
|= cstyle_flag
? VIS_CSTYLE
: 0;
592 flags
|= http_flag
? VIS_HTTPSTYLE
: 0;
593 flags
|= httponly_flag
? VIS_HTTPSTYLE
| VIS_NOESCAPE
: 0;
594 flags
|= octal_flag
? VIS_OCTAL
: 0;
595 flags
|= safe_flag
? VIS_SAFE
: 0;
596 flags
|= tab_flag
? VIS_TAB
: 0;
597 flags
|= whitespace_flag
? VIS_WHITE
: 0;
599 if ((nextra
= makeextralist(flags
, extra_arg
)) == NULL
)
600 err(1, "Out of memory");
603 if (rk_strrasvis(&s
, &sz
, argv
[0], flags
, nextra
) < 0)
604 err(2, "Out of memory");
613 while (!feof(stdin
) &&
614 (nbytes
= getline(&line
, &linesz
, stdin
)) > 0) {
617 if (line
[nbytes
- 1] == '\n') {
618 line
[--nbytes
] = '\0';
622 if (rk_strrasvisx(&s
, &sz
, line
, nbytes
, flags
, nextra
) < 0)
623 err(2, "Out of memory");
624 printf("%s%s", s
, nl
);
628 errx(2, "I/O error");
629 } else if (stdin_flag
) {
632 char vbuf
[4 * (sizeof(buf
) - 1) + 1];
634 while (!feof(stdin
) &&
635 (nbytes
= fread(buf
, 1, sizeof(buf
) - 1, stdin
))) {
637 strsvis(vbuf
, buf
, flags
, nextra
);
642 errx(2, "I/O error");