Update vis/unvis modules from NetBSD

This commit is contained in:
Guillem Jover 2018-05-21 03:09:05 +02:00
parent 3efad64155
commit 2d7de186e9
7 changed files with 1607 additions and 538 deletions

11
COPYING
View File

@ -122,7 +122,6 @@ Files:
src/strmode.c src/strmode.c
src/strnstr.c src/strnstr.c
src/unvis.c src/unvis.c
src/vis.c
Copyright: Copyright:
Copyright © 1980, 1982, 1986, 1989-1994 Copyright © 1980, 1982, 1986, 1989-1994
The Regents of the University of California. All rights reserved. The Regents of the University of California. All rights reserved.
@ -315,6 +314,16 @@ License: BSD-2-clause-NetBSD
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. POSSIBILITY OF SUCH DAMAGE.
Files:
src/vis.c
Copyright:
Copyright © 1989, 1993
The Regents of the University of California. All rights reserved.
.
Copyright © 1999, 2005 The NetBSD Foundation, Inc.
All rights reserved.
License: BSD-3-clause-Regents and BSD-2-clause-NetBSD
Files: Files:
include/bsd/sys/endian.h include/bsd/sys/endian.h
man/byteorder.3bsd man/byteorder.3bsd

View File

@ -1,3 +1,5 @@
/* $NetBSD: vis.h,v 1.25 2017/04/23 01:57:36 christos Exp $ */
/*- /*-
* Copyright (c) 1990, 1993 * Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved. * The Regents of the University of California. All rights reserved.
@ -27,7 +29,6 @@
* SUCH DAMAGE. * SUCH DAMAGE.
* *
* @(#)vis.h 8.1 (Berkeley) 6/2/93 * @(#)vis.h 8.1 (Berkeley) 6/2/93
* $FreeBSD: src/include/vis.h,v 1.11 2003/10/30 10:40:49 phk Exp $
*/ */
#ifndef LIBBSD_VIS_H #ifndef LIBBSD_VIS_H
@ -38,25 +39,34 @@
/* /*
* to select alternate encoding format * to select alternate encoding format
*/ */
#define VIS_OCTAL 0x01 /* use octal \ddd format */ #define VIS_OCTAL 0x0001 /* use octal \ddd format */
#define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropriate */ #define VIS_CSTYLE 0x0002 /* use \[nrft0..] where appropriate */
/* /*
* to alter set of characters encoded (default is to encode all * to alter set of characters encoded (default is to encode all
* non-graphic except space, tab, and newline). * non-graphic except space, tab, and newline).
*/ */
#define VIS_SP 0x04 /* also encode space */ #define VIS_SP 0x0004 /* also encode space */
#define VIS_TAB 0x08 /* also encode tab */ #define VIS_TAB 0x0008 /* also encode tab */
#define VIS_NL 0x10 /* also encode newline */ #define VIS_NL 0x0010 /* also encode newline */
#define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL) #define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL)
#define VIS_SAFE 0x20 /* only encode "unsafe" characters */ #define VIS_SAFE 0x0020 /* only encode "unsafe" characters */
#define VIS_DQ 0x8000 /* also encode double quotes */
/* /*
* other * other
*/ */
#define VIS_NOSLASH 0x40 /* inhibit printing '\' */ #define VIS_NOSLASH 0x0040 /* inhibit printing '\' */
#define VIS_HTTPSTYLE 0x80 /* http-style escape % HEX HEX */ #define VIS_HTTP1808 0x0080 /* http-style escape % hex hex */
#define VIS_GLOB 0x100 /* encode glob(3) magics */ #define VIS_HTTPSTYLE 0x0080 /* http-style escape % hex hex */
#define VIS_MIMESTYLE 0x0100 /* mime-style escape = HEX HEX */
#define VIS_HTTP1866 0x0200 /* http-style &#num; or &string; */
#define VIS_NOESCAPE 0x0400 /* don't decode `\' */
#define _VIS_END 0x0800 /* for unvis */
#define VIS_GLOB 0x1000 /* encode glob(3) magic characters */
#define VIS_SHELL 0x2000 /* encode shell special characters [not glob] */
#define VIS_META (VIS_WHITE | VIS_GLOB | VIS_SHELL)
#define VIS_NOLOCALE 0x4000 /* encode using the C locale */
/* /*
* unvis return codes * unvis return codes
@ -70,7 +80,7 @@
/* /*
* unvis flags * unvis flags
*/ */
#define UNVIS_END 1 /* no more characters */ #define UNVIS_END _VIS_END /* no more characters */
#ifdef LIBBSD_OVERLAY #ifdef LIBBSD_OVERLAY
#include <sys/cdefs.h> #include <sys/cdefs.h>
@ -80,12 +90,33 @@
__BEGIN_DECLS __BEGIN_DECLS
char *vis(char *, int, int, int); char *vis(char *, int, int, int);
char *nvis(char *, size_t, int, int, int);
char *svis(char *, int, int, int, const char *);
char *snvis(char *, size_t, int, int, int, const char *);
int strvis(char *, const char *, int); int strvis(char *, const char *, int);
int stravis(char **, const char *, int);
int strnvis(char *, size_t, const char *, int);
int strsvis(char *, const char *, int, const char *);
int strsnvis(char *, size_t, const char *, int, const char *);
int strvisx(char *, const char *, size_t, int); int strvisx(char *, const char *, size_t, int);
int strnvis(char *, const char *, size_t, int); int strnvisx(char *, size_t, const char *, size_t, int);
int strenvisx(char *, size_t, const char *, size_t, int, int *);
int strsvisx(char *, const char *, size_t, int, const char *);
int strsnvisx(char *, size_t, const char *, size_t, int, const char *);
int strsenvisx(char *, size_t, const char *, size_t , int, const char *,
int *);
int strunvis(char *, const char *); int strunvis(char *, const char *);
int strnunvis(char *, size_t, const char *);
int strunvisx(char *, const char *, int); int strunvisx(char *, const char *, int);
ssize_t strnunvis(char *, const char *, size_t); int strnunvisx(char *, size_t, const char *, int);
int unvis(char *, int, int *, int); int unvis(char *, int, int *, int);
__END_DECLS __END_DECLS

View File

@ -1,4 +1,4 @@
.\" $OpenBSD: unvis.3,v 1.15 2005/07/22 03:16:58 jaredy Exp $ .\" $NetBSD: unvis.3,v 1.29 2017/10/24 19:14:55 abhinav Exp $
.\" .\"
.\" Copyright (c) 1989, 1991, 1993 .\" Copyright (c) 1989, 1991, 1993
.\" The Regents of the University of California. All rights reserved. .\" The Regents of the University of California. All rights reserved.
@ -27,13 +27,17 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE. .\" SUCH DAMAGE.
.\" .\"
.Dd $Mdocdate: May 31 2007 $ .\" @(#)unvis.3 8.2 (Berkeley) 12/11/93
.\"
.Dd March 12, 2011
.Dt UNVIS 3bsd .Dt UNVIS 3bsd
.Os .Os
.Sh NAME .Sh NAME
.Nm unvis , .Nm unvis ,
.Nm strunvis , .Nm strunvis ,
.Nm strnunvis .Nm strnunvis ,
.Nm strunvisx ,
.Nm strnunvisx
.Nd decode a visual representation of characters .Nd decode a visual representation of characters
.Sh LIBRARY .Sh LIBRARY
.ds str-Lb-libbsd Utility functions from BSD systems (libbsd, \-lbsd) .ds str-Lb-libbsd Utility functions from BSD systems (libbsd, \-lbsd)
@ -44,88 +48,91 @@
.Xr libbsd 7 .Xr libbsd 7
for include usage.) for include usage.)
.Ft int .Ft int
.Fn unvis "char *cp" "char c" "int *astate" "int flag" .Fn unvis "char *cp" "int c" "int *astate" "int flag"
.Ft int .Ft int
.Fn strunvis "char *dst" "char *src" .Fn strunvis "char *dst" "const char *src"
.Ft ssize_t .Ft int
.Fn strnunvis "char *dst" "char *src" "size_t size" .Fn strnunvis "char *dst" "size_t dlen" "const char *src"
.Ft int
.Fn strunvisx "char *dst" "const char *src" "int flag"
.Ft int
.Fn strnunvisx "char *dst" "size_t dlen" "const char *src" "int flag"
.Sh DESCRIPTION .Sh DESCRIPTION
The The
.Fn unvis , .Fn unvis ,
.Fn strunvis .Fn strunvis
and and
.Fn strnunvis .Fn strunvisx
functions are used to decode a visual representation of characters, functions
as produced by the are used to decode a visual representation of characters, as produced
by the
.Xr vis 3bsd .Xr vis 3bsd
function, back into the original form. function, back into
the original form.
.Pp
The
.Fn unvis .Fn unvis
is called with successive characters in function is called with successive characters in
.Fa c .Ar c
until a valid until a valid sequence is recognized, at which time the decoded
sequence is recognized, at which time the decoded character is character is available at the character pointed to by
available at the character pointed to by .Ar cp .
.Fa cp .
.Pp .Pp
The
.Fn strunvis .Fn strunvis
decodes the characters pointed to by function decodes the characters pointed to by
.Fa src .Ar src
into the buffer pointed to by into the buffer pointed to by
.Fa dst . .Ar dst .
.Pp
.Fn strnunvis
decodes the characters pointed to by
.Fa src
into the buffer pointed to by
.Fa dst ,
writing a maximum of
.Fa size
bytes.
The The
.Fn strunvis .Fn strunvis
function simply copies function simply copies
.Fa src .Ar src
to to
.Fa dst , .Ar dst ,
decoding any escape sequences along the way, decoding any escape sequences along the way,
and returns the number of characters placed into and returns the number of characters placed into
.Fa dst , .Ar dst ,
or \-1 if an or \-1 if an
invalid escape sequence was detected. invalid escape sequence was detected.
The size of The size of
.Fa dst .Ar dst
should be should be equal to the size of
equal to the size of .Ar src
.Fa src
(that is, no expansion takes place during decoding). (that is, no expansion takes place during decoding).
.Pp
The
.Fn strunvisx
function does the same as the
.Fn strunvis .Fn strunvis
terminates the destination string with a trailing NUL byte; function,
.Fn strnunvis but it allows you to add a flag that specifies the style the string
does so if .Ar src
.Fa size is encoded with.
is larger than 0. Currently, the supported flags are:
.Dv VIS_HTTPSTYLE
and
.Dv VIS_MIMESTYLE .
.Pp .Pp
The The
.Fn unvis .Fn unvis
function implements a state machine that can be used to decode an arbitrary function implements a state machine that can be used to decode an
stream of bytes. arbitrary stream of bytes.
All state associated with the bytes being decoded is stored outside the All state associated with the bytes being decoded is stored outside the
.Fn unvis .Fn unvis
function (that is, a pointer to the state is passed in), so function (that is, a pointer to the state is passed in), so
calls decoding different streams can be freely intermixed. calls decoding different streams can be freely intermixed.
To start decoding a stream of bytes, first initialize an integer To start decoding a stream of bytes, first initialize an integer to zero.
to zero.
Call Call
.Fn unvis .Fn unvis
with each successive byte, along with a pointer with each successive byte, along with a pointer
to this integer, and a pointer to a destination character. to this integer, and a pointer to a destination character.
.Sh RETURN VALUES
The The
.Fn unvis .Fn unvis
function has several return codes that must be handled properly. function has several return codes that must be handled properly.
They are: They are:
.Bl -tag -width UNVIS_VALIDPUSH .Bl -tag -width UNVIS_VALIDPUSH
.It Li \&0 (zero) .It Li \&0 No (zero)
Another character is necessary; nothing has been recognized yet. Another character is necessary; nothing has been recognized yet.
.It Dv UNVIS_VALID .It Dv UNVIS_VALID
A valid character has been recognized and is available at the location A valid character has been recognized and is available at the location
@ -140,30 +147,41 @@ however, the character currently passed in should be passed in again.
A valid sequence was detected, but no character was produced. A valid sequence was detected, but no character was produced.
This return code is necessary to indicate a logical break between characters. This return code is necessary to indicate a logical break between characters.
.It Dv UNVIS_SYNBAD .It Dv UNVIS_SYNBAD
An invalid escape sequence was detected, or the decoder is in an An invalid escape sequence was detected, or the decoder is in an unknown state.
unknown state.
The decoder is placed into the starting state. The decoder is placed into the starting state.
.El .El
.Pp .Pp
When all bytes in the stream have been processed, call When all bytes in the stream have been processed, call
.Fn unvis .Fn unvis
one more time with one more time with flag set to
.Fa flag
set to
.Dv UNVIS_END .Dv UNVIS_END
to extract any remaining character (the character passed in is ignored). to extract any remaining character (the character passed in is ignored).
.Pp .Pp
The The
.Fn strunvis .Fa flag
function returns the number of bytes written (not counting argument is also used to specify the encoding style of the source.
the trailing NUL byte) or \-1 if an error occurred. If set to
.Dv VIS_HTTPSTYLE
or
.Dv VIS_HTTP1808 ,
.Fn unvis
will decode URI strings as specified in RFC 1808.
If set to
.Dv VIS_HTTP1866 ,
.Fn unvis
will decode entity references and numeric character references
as specified in RFC 1866.
If set to
.Dv VIS_MIMESTYLE ,
.Fn unvis
will decode MIME Quoted-Printable strings as specified in RFC 2045.
If set to
.Dv VIS_NOESCAPE ,
.Fn unvis
will not decode
.Ql \e
quoted characters.
.Pp .Pp
The
.Fn strnunvis
function returns the number of bytes (not counting the trailing NUL byte)
that would be needed to fully convert the input string, or \-1 if an
error occurred.
.Sh EXAMPLES
The following code fragment illustrates a proper use of The following code fragment illustrates a proper use of
.Fn unvis . .Fn unvis .
.Bd -literal -offset indent .Bd -literal -offset indent
@ -183,19 +201,66 @@ again:
(void)putchar(out); (void)putchar(out);
goto again; goto again;
case UNVIS_SYNBAD: case UNVIS_SYNBAD:
(void)fprintf(stderr, "bad sequence!\en"); errx(EXIT_FAILURE, "Bad character sequence!");
exit(1);
} }
} }
if (unvis(&out, (char)0, &state, UNVIS_END) == UNVIS_VALID) if (unvis(&out, '\e0', &state, UNVIS_END) == UNVIS_VALID)
(void)putchar(out); (void)putchar(out);
.Ed .Ed
.Sh ERRORS
The functions
.Fn strunvis ,
.Fn strnunvis ,
.Fn strunvisx ,
and
.Fn strnunvisx
will return \-1 on error and set
.Va errno
to:
.Bl -tag -width Er
.It Bq Er EINVAL
An invalid escape sequence was detected, or the decoder is in an unknown state.
.El
.Pp
In addition the functions
.Fn strnunvis
and
.Fn strnunvisx
will can also set
.Va errno
on error to:
.Bl -tag -width Er
.It Bq Er ENOSPC
Not enough space to perform the conversion.
.El
.Sh SEE ALSO .Sh SEE ALSO
.Xr unvis 1 , .Xr unvis 1 ,
.Xr vis 1 , .Xr vis 1 ,
.Xr vis 3bsd .Xr vis 3bsd
.Rs
.%A R. Fielding
.%T Relative Uniform Resource Locators
.%O RFC1808
.Re
.Sh HISTORY .Sh HISTORY
The The
.Fn unvis .Fn unvis
function first appeared in function
first appeared in
.Bx 4.4 . .Bx 4.4 .
The
.Fn strnunvis
and
.Fn strnunvisx
functions appeared in
.Nx 6.0 .
.Sh BUGS
The names
.Dv VIS_HTTP1808
and
.Dv VIS_HTTP1866
are wrong.
Percent-encoding was defined in RFC 1738, the original RFC for URL.
RFC 1866 defines HTML 2.0, an application of SGML, from which it
inherits concepts of numeric character references and entity
references.

View File

@ -1,4 +1,4 @@
.\" $OpenBSD: vis.3,v 1.23 2005/08/28 19:51:27 millert Exp $ .\" $NetBSD: vis.3,v 1.49 2017/08/05 20:22:29 wiz Exp $
.\" .\"
.\" Copyright (c) 1989, 1991, 1993 .\" Copyright (c) 1989, 1991, 1993
.\" The Regents of the University of California. All rights reserved. .\" The Regents of the University of California. All rights reserved.
@ -27,53 +27,87 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE. .\" SUCH DAMAGE.
.\" .\"
.Dd $Mdocdate: May 31 2007 $ .\" @(#)vis.3 8.1 (Berkeley) 6/9/93
.\"
.Dd April 22, 2017
.Dt VIS 3bsd .Dt VIS 3bsd
.Os .Os
.Sh NAME .Sh NAME
.Nm vis , .Nm vis ,
.Nm nvis ,
.Nm strvis , .Nm strvis ,
.Nm stravis ,
.Nm strnvis , .Nm strnvis ,
.Nm strvisx .Nm strvisx ,
.Nm strnvisx ,
.Nm strenvisx ,
.Nm svis ,
.Nm snvis ,
.Nm strsvis ,
.Nm strsnvis ,
.Nm strsvisx ,
.Nm strsnvisx ,
.Nm strsenvisx
.Nd visually encode characters .Nd visually encode characters
.Sh LIBRARY .Sh LIBRARY
.ds str-Lb-libbsd Utility functions from BSD systems (libbsd, \-lbsd) .ds str-Lb-libbsd Utility functions from BSD systems (libbsd, \-lbsd)
.Lb libbsd .Lb libbsd
.Sh SYNOPSIS .Sh SYNOPSIS
.In stdlib.h
.In vis.h .In vis.h
(See (See
.Xr libbsd 7 .Xr libbsd 7
for include usage.) for include usage.)
.Ft char * .Ft char *
.Fn vis "char *dst" "int c" "int flag" "int nextc" .Fn vis "char *dst" "int c" "int flag" "int nextc"
.Ft char *
.Fn nvis "char *dst" "size_t dlen" "int c" "int flag" "int nextc"
.Ft int .Ft int
.Fn strvis "char *dst" "const char *src" "int flag" .Fn strvis "char *dst" "const char *src" "int flag"
.Ft int .Ft int
.Fn strnvis "char *dst" "const char *src" "size_t size" "int flag" .Fn stravis "char **dst" "const char *src" "int flag"
.Ft int
.Fn strnvis "char *dst" "size_t dlen" "const char *src" "int flag"
.Ft int .Ft int
.Fn strvisx "char *dst" "const char *src" "size_t len" "int flag" .Fn strvisx "char *dst" "const char *src" "size_t len" "int flag"
.Ft int
.Fn strnvisx "char *dst" "size_t dlen" "const char *src" "size_t len" "int flag"
.Ft int
.Fn strenvisx "char *dst" "size_t dlen" "const char *src" "size_t len" "int flag" "int *cerr_ptr"
.Ft char *
.Fn svis "char *dst" "int c" "int flag" "int nextc" "const char *extra"
.Ft char *
.Fn snvis "char *dst" "size_t dlen" "int c" "int flag" "int nextc" "const char *extra"
.Ft int
.Fn strsvis "char *dst" "const char *src" "int flag" "const char *extra"
.Ft int
.Fn strsnvis "char *dst" "size_t dlen" "const char *src" "int flag" "const char *extra"
.Ft int
.Fn strsvisx "char *dst" "const char *src" "size_t len" "int flag" "const char *extra"
.Ft int
.Fn strsnvisx "char *dst" "size_t dlen" "const char *src" "size_t len" "int flag" "const char *extra"
.Ft int
.Fn strsenvisx "char *dst" "size_t dlen" "const char *src" "size_t len" "int flag" "const char *extra" "int *cerr_ptr"
.Sh DESCRIPTION .Sh DESCRIPTION
The The
.Fn vis .Fn vis
function copies into function
copies into
.Fa dst .Fa dst
a string which represents the character a string which represents the character
.Fa c . .Fa c .
If If
.Fa c .Fa c
needs no encoding, it is copied in unaltered. needs no encoding, it is copied in unaltered.
The string is NUL terminated and a pointer to the end of the string is The string is null terminated, and a pointer to the end of the string is
returned. returned.
The maximum length of any encoding is four The maximum length of any encoding is four
characters (not including the trailing NUL); bytes (not including the trailing
.Dv NUL ) ;
thus, when thus, when
encoding a set of characters into a buffer, the size of the buffer should encoding a set of characters into a buffer, the size of the buffer should
be four times the number of characters encoded, plus one for the trailing be four times the number of bytes encoded, plus one for the trailing
NUL. .Dv NUL .
The The flag parameter is used for altering the default range of
.Fa flag
parameter is used for altering the default range of
characters considered for encoding and for altering the visual characters considered for encoding and for altering the visual
representation. representation.
The additional character, The additional character,
@ -84,9 +118,11 @@ encoding format (explained below).
.Pp .Pp
The The
.Fn strvis , .Fn strvis ,
.Fn strnvis .Fn stravis ,
.Fn strnvis ,
.Fn strvisx ,
and and
.Fn strvisx .Fn strnvisx
functions copy into functions copy into
.Fa dst .Fa dst
a visual representation of a visual representation of
@ -94,89 +130,153 @@ the string
.Fa src . .Fa src .
The The
.Fn strvis .Fn strvis
function encodes characters from and
.Fa src
up to the first NUL.
The
.Fn strnvis .Fn strnvis
function encodes characters from functions encode characters from
.Fa src .Fa src
up to the first NUL or the end of up to the
.Fa dst , first
as indicated by .Dv NUL .
.Fa size .
The The
.Fn strvisx .Fn strvisx
function encodes exactly and
.Fn strnvisx
functions encode exactly
.Fa len .Fa len
characters from characters from
.Fa src .Fa src
(this (this
is useful for encoding a block of data that may contain NULs). is useful for encoding a block of data that may contain
All three forms NUL terminate .Dv NUL Ns 's ) .
.Fa dst , Both forms
except for .Dv NUL
.Fn strnvis terminate
when .Fa dst .
.Fa size The size of
is zero, in which case
.Fa dst
is not touched.
For
.Fn strvis
and
.Fn strvisx ,
the size of
.Fa dst .Fa dst
must be four times the number must be four times the number
of characters encoded from of bytes encoded from
.Fa src .Fa src
(plus one for the NUL). (plus one for the
.Fn strvis .Dv NUL ) .
Both
forms return the number of characters in
.Fa dst
(not including the trailing
.Dv NUL ) .
The
.Fn stravis
function allocates space dynamically to hold the string.
The
.Dq Nm n
versions of the functions also take an additional argument
.Fa dlen
that indicates the length of the
.Fa dst
buffer.
If
.Fa dlen
is not large enough to fit the converted string then the
.Fn strnvis
and and
.Fn strvisx .Fn strnvisx
return the number of characters in functions return \-1 and set
.Fa dst .Va errno
(not including the trailing NUL). to
.Fn strnvis .Dv ENOSPC .
returns the length that The
.Fa dst .Fn strenvisx
would become if it were of unlimited size (similar to function takes an additional argument,
.Xr snprintf 3 .Fa cerr_ptr ,
or that is used to pass in and out a multibyte conversion error flag.
.Xr strlcpy 3bsd ) . This is useful when processing single characters at a time when
This can be used to detect truncation but it also means that it is possible that the locale may be set to something other
the return value of than the locale of the characters in the input data.
.Fn strnvis .Pp
must not be used without checking it against The functions
.Fa size . .Fn svis ,
.Fn snvis ,
.Fn strsvis ,
.Fn strsnvis ,
.Fn strsvisx ,
.Fn strsnvisx ,
and
.Fn strsenvisx
correspond to
.Fn vis ,
.Fn nvis ,
.Fn strvis ,
.Fn strnvis ,
.Fn strvisx ,
.Fn strnvisx ,
and
.Fn strenvisx
but have an additional argument
.Fa extra ,
pointing to a
.Dv NUL
terminated list of characters.
These characters will be copied encoded or backslash-escaped into
.Fa dst .
These functions are useful e.g. to remove the special meaning
of certain characters to shells.
.Pp .Pp
The encoding is a unique, invertible representation composed entirely of The encoding is a unique, invertible representation composed entirely of
graphic characters; it can be decoded back into the original form using graphic characters; it can be decoded back into the original form using
the the
.Xr unvis 3bsd .Xr unvis 3bsd ,
or
.Xr strunvis 3bsd .Xr strunvis 3bsd
or
.Xr strnunvis 3bsd
functions. functions.
.Pp .Pp
There are two parameters that can be controlled: the range of There are two parameters that can be controlled: the range of
characters that are encoded, and the type characters that are encoded (applies only to
of representation used. .Fn vis ,
By default, all non-graphic characters .Fn nvis ,
except space, tab, and newline are encoded .Fn strvis ,
(see .Fn strnvis ,
.Fn strvisx ,
and
.Fn strnvisx ) ,
and the type of representation used.
By default, all non-graphic characters,
except space, tab, and newline are encoded (see
.Xr isgraph 3 ) . .Xr isgraph 3 ) .
The following flags The following flags
alter this: alter this:
.Bl -tag -width VIS_WHITEX .Bl -tag -width VIS_WHITEX
.It Dv VIS_DQ
Also encode double quotes
.It Dv VIS_GLOB .It Dv VIS_GLOB
Also encode magic characters recognized by Also encode the magic characters
.Xr glob 3 .Ql ( * ,
.Pf ( Ql * ,
.Ql \&? , .Ql \&? ,
.Ql \&[ ) .Ql \&[ ,
and and
.Ql # . .Ql # )
recognized by
.Xr glob 3 .
.It Dv VIS_SHELL
Also encode the meta characters used by shells (in addition to the glob
characters):
.Ql ( ' ,
.Ql ` ,
.Ql \&" ,
.Ql \&; ,
.Ql & ,
.Ql < ,
.Ql > ,
.Ql \&( ,
.Ql \&) ,
.Ql \&| ,
.Ql \&] ,
.Ql \e ,
.Ql $ ,
.Ql \&! ,
.Ql \&^ ,
and
.Ql ~ ) .
.It Dv VIS_SP .It Dv VIS_SP
Also encode space. Also encode space.
.It Dv VIS_TAB .It Dv VIS_TAB
@ -185,34 +285,56 @@ Also encode tab.
Also encode newline. Also encode newline.
.It Dv VIS_WHITE .It Dv VIS_WHITE
Synonym for Synonym for
.Dv VIS_SP .Dv VIS_SP | VIS_TAB | VIS_NL .
\&| .It Dv VIS_META
.Dv VIS_TAB Synonym for
\&| .Dv VIS_WHITE | VIS_GLOB | VIS_SHELL .
.Dv VIS_NL .
.It Dv VIS_SAFE .It Dv VIS_SAFE
Only encode Only encode
.Dq unsafe .Dq unsafe
characters. characters.
These are control characters which may cause common terminals to perform Unsafe means control characters which may cause common terminals to perform
unexpected functions. unexpected functions.
Currently this form allows space, Currently this form allows space, tab, newline, backspace, bell, and
tab, newline, backspace, bell, and return -- in addition return \(em in addition to all graphic characters \(em unencoded.
to all graphic characters -- unencoded.
.El .El
.Pp .Pp
There are three forms of encoding. (The above flags have no effect for
All forms use the backslash .Fn svis ,
.Fn snvis ,
.Fn strsvis ,
.Fn strsnvis ,
.Fn strsvisx ,
and
.Fn strsnvisx .
When using these functions, place all graphic characters to be
encoded in an array pointed to by
.Fa extra .
In general, the backslash character should be included in this array, see the
warning on the use of the
.Dv VIS_NOSLASH
flag below).
.Pp
There are six forms of encoding.
All forms use the backslash character
.Ql \e .Ql \e
character to introduce a special to introduce a special
sequence; two backslashes are used to represent a real backslash. sequence; two backslashes are used to represent a real backslash,
except
.Dv VIS_HTTPSTYLE
that uses
.Ql % ,
or
.Dv VIS_MIMESTYLE
that uses
.Ql = .
These are the visual formats: These are the visual formats:
.Bl -tag -width VIS_CSTYLE .Bl -tag -width VIS_CSTYLE
.It (default) .It (default)
Use an Use an
.Ql M .Ql M
to represent meta characters (characters with the 8th to represent meta characters (characters with the 8th
bit set), and use a caret bit set), and use caret
.Ql ^ .Ql ^
to represent control characters (see to represent control characters (see
.Xr iscntrl 3 ) . .Xr iscntrl 3 ) .
@ -256,27 +378,27 @@ space.
.It Dv \e240 .It Dv \e240
Represents Meta-space. Represents Meta-space.
.El .El
.Pp
.It Dv VIS_CSTYLE .It Dv VIS_CSTYLE
Use C-style backslash sequences to represent standard non-printable Use C-style backslash sequences to represent standard non-printable
characters. characters.
The following sequences are used to represent the indicated characters: The following sequences are used to represent the indicated characters:
.Bd -unfilled -offset indent .Bd -unfilled -offset indent
.Li \ea Tn - BEL No (007) .Li \ea Tn \(em BEL No (007)
.Li \eb Tn - BS No (010) .Li \eb Tn \(em BS No (010)
.Li \ef Tn - NP No (014) .Li \ef Tn \(em NP No (014)
.Li \en Tn - NL No (012) .Li \en Tn \(em NL No (012)
.Li \er Tn - CR No (015) .Li \er Tn \(em CR No (015)
.Li \es Tn - SP No (040) .Li \es Tn \(em SP No (040)
.Li \et Tn - HT No (011) .Li \et Tn \(em HT No (011)
.Li \ev Tn - VT No (013) .Li \ev Tn \(em VT No (013)
.Li \e0 Tn - NUL No (000) .Li \e0 Tn \(em NUL No (000)
.Ed .Ed
.Pp .Pp
When using this format, the When using this format, the
.Fa nextc .Fa nextc
parameter is looked at to determine parameter is looked at to determine if a
if a NUL character can be encoded as .Dv NUL
character can be encoded as
.Ql \e0 .Ql \e0
instead of instead of
.Ql \e000 . .Ql \e000 .
@ -284,13 +406,36 @@ If
.Fa nextc .Fa nextc
is an octal digit, the latter representation is used to is an octal digit, the latter representation is used to
avoid ambiguity. avoid ambiguity.
.Pp
Non-printable characters without C-style
backslash sequences use the default representation.
.It Dv VIS_OCTAL .It Dv VIS_OCTAL
Use a three digit octal sequence. Use a three digit octal sequence.
The form is The form is
.Ql \eddd .Ql \eddd
where where
.Ar d .Em d
represents an octal digit. represents an octal digit.
.It Dv VIS_CSTYLE \&| Dv VIS_OCTAL
Same as
.Dv VIS_CSTYLE
except that non-printable characters without C-style
backslash sequences use a three digit octal sequence.
.It Dv VIS_HTTPSTYLE
Use URI encoding as described in RFC 1738.
The form is
.Ql %xx
where
.Em x
represents a lower case hexadecimal digit.
.It Dv VIS_MIMESTYLE
Use MIME Quoted-Printable encoding as described in RFC 2045, only don't
break lines and don't handle CRLF.
The form is
.Ql =XX
where
.Em X
represents an upper case hexadecimal digit.
.El .El
.Pp .Pp
There is one additional flag, There is one additional flag,
@ -304,21 +449,112 @@ meta characters as
.Ql M-C ) . .Ql M-C ) .
With this flag set, the encoding is With this flag set, the encoding is
ambiguous and non-invertible. ambiguous and non-invertible.
.Sh MULTIBYTE CHARACTER SUPPORT
These functions support multibyte character input.
The encoding conversion is influenced by the setting of the
.Ev LC_CTYPE
environment variable which defines the set of characters
that can be copied without encoding.
.Pp
If
.Dv VIS_NOLOCALE
is set, processing is done assuming the C locale and overriding
any other environment settings.
.Pp
When 8-bit data is present in the input,
.Ev LC_CTYPE
must be set to the correct locale or to the C locale.
If the locales of the data and the conversion are mismatched,
multibyte character recognition may fail and encoding will be performed
byte-by-byte instead.
.Pp
As noted above,
.Fa dst
must be four times the number of bytes processed from
.Fa src .
But note that each multibyte character can be up to
.Dv MB_LEN_MAX
bytes
.\" (see
.\" .Xr multibyte 3 )
so in terms of multibyte characters,
.Fa dst
must be four times
.Dv MB_LEN_MAX
times the number of characters processed from
.Fa src .
.Sh ENVIRONMENT
.Bl -tag -width ".Ev LC_CTYPE"
.It Ev LC_CTYPE
Specify the locale of the input data.
Set to C if the input data locale is unknown.
.El
.Sh ERRORS
The functions
.Fn nvis
and
.Fn snvis
will return
.Dv NULL
and the functions
.Fn strnvis ,
.Fn strnvisx ,
.Fn strsnvis ,
and
.Fn strsnvisx ,
will return \-1 when the
.Fa dlen
destination buffer size is not enough to perform the conversion while
setting
.Va errno
to:
.Bl -tag -width ".Bq Er ENOSPC"
.It Bq Er ENOSPC
The destination buffer size is not large enough to perform the conversion.
.El
.Sh SEE ALSO .Sh SEE ALSO
.Xr unvis 1 , .Xr unvis 1 ,
.Xr vis 1 , .Xr vis 1 ,
.Xr snprintf 3 , .Xr glob 3 ,
.Xr strlcpy 3bsd , .\" .Xr multibyte 3 ,
.Xr unvis 3bsd .Xr unvis 3bsd
.Rs
.%A T. Berners-Lee
.%T Uniform Resource Locators (URL)
.%O "RFC 1738"
.Re
.Rs
.%T "Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies"
.%O "RFC 2045"
.Re
.Sh HISTORY .Sh HISTORY
The The
.Fn vis , .Fn vis ,
.Fn strvis .Fn strvis ,
and and
.Fn strvisx .Fn strvisx
functions first appeared in functions first appeared in
.Bx 4.4 . .Bx 4.4 .
The The
.Fn strnvis .Fn svis ,
function first appeared in .Fn strsvis ,
.Ox 2.9 . and
.Fn strsvisx
functions appeared in
.Nx 1.5 .
The buffer size limited versions of the functions
.Po Fn nvis ,
.Fn strnvis ,
.Fn strnvisx ,
.Fn snvis ,
.Fn strsnvis ,
and
.Fn strsnvisx Pc
appeared in
.Nx 6.0
and
.Fx 9.2 .
Multibyte character support was added in
.Nx 7.0
and
.Fx 9.2 .

View File

@ -144,4 +144,16 @@ LIBBSD_0.9 {
flopenat; flopenat;
pidfile_fileno; pidfile_fileno;
nvis;
snvis;
stravis;
strenvisx;
strnunvisx;
strsenvisx;
strsnvis;
strsnvisx;
strsvis;
strsvisx;
svis;
} LIBBSD_0.8; } LIBBSD_0.8;

View File

@ -1,3 +1,5 @@
/* $NetBSD: unvis.c,v 1.44 2014/09/26 15:43:36 roy Exp $ */
/*- /*-
* Copyright (c) 1989, 1993 * Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved. * The Regents of the University of California. All rights reserved.
@ -27,10 +29,22 @@
* SUCH DAMAGE. * SUCH DAMAGE.
*/ */
#include <sys/cdefs.h>
#include <sys/types.h> #include <sys/types.h>
#include <assert.h>
#include <ctype.h> #include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <errno.h>
#include <vis.h> #include <vis.h>
#ifdef __weak_alias
__weak_alias(strnunvisx,_strnunvisx)
#endif
#define _DIAGASSERT(x)
/* /*
* decode driven by state machine * decode driven by state machine
*/ */
@ -41,15 +55,128 @@
#define S_CTRL 4 /* control char started (^) */ #define S_CTRL 4 /* control char started (^) */
#define S_OCTAL2 5 /* octal digit 2 */ #define S_OCTAL2 5 /* octal digit 2 */
#define S_OCTAL3 6 /* octal digit 3 */ #define S_OCTAL3 6 /* octal digit 3 */
#define S_HEX2 7 /* hex digit 2 */ #define S_HEX 7 /* mandatory hex digit */
#define S_HEX1 8 /* http hex digit */
#define S_HEX2 9 /* http hex digit 2 */
#define S_MIME1 10 /* mime hex digit 1 */
#define S_MIME2 11 /* mime hex digit 2 */
#define S_EATCRNL 12 /* mime eating CRNL */
#define S_AMP 13 /* seen & */
#define S_NUMBER 14 /* collecting number */
#define S_STRING 15 /* collecting string */
#define S_HTTP 0x080 /* %HEXHEX escape */ #define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
#define xtod(c) (isdigit(c) ? (c - '0') : ((tolower(c) - 'a') + 10))
#define XTOD(c) (isdigit(c) ? (c - '0') : ((c - 'A') + 10))
#define isoctal(c) \ /*
(((unsigned char)(c)) >= '0' && ((unsigned char)(c)) <= '7') * RFC 1866
#define ishex(c) \ */
((((unsigned char)(c)) >= '0' && ((unsigned char)(c)) <= '9') || \ static const struct nv {
(((unsigned char)(c)) >= 'a' && ((unsigned char)(c)) <= 'f')) char name[7];
uint8_t value;
} nv[] = {
{ "AElig", 198 }, /* capital AE diphthong (ligature) */
{ "Aacute", 193 }, /* capital A, acute accent */
{ "Acirc", 194 }, /* capital A, circumflex accent */
{ "Agrave", 192 }, /* capital A, grave accent */
{ "Aring", 197 }, /* capital A, ring */
{ "Atilde", 195 }, /* capital A, tilde */
{ "Auml", 196 }, /* capital A, dieresis or umlaut mark */
{ "Ccedil", 199 }, /* capital C, cedilla */
{ "ETH", 208 }, /* capital Eth, Icelandic */
{ "Eacute", 201 }, /* capital E, acute accent */
{ "Ecirc", 202 }, /* capital E, circumflex accent */
{ "Egrave", 200 }, /* capital E, grave accent */
{ "Euml", 203 }, /* capital E, dieresis or umlaut mark */
{ "Iacute", 205 }, /* capital I, acute accent */
{ "Icirc", 206 }, /* capital I, circumflex accent */
{ "Igrave", 204 }, /* capital I, grave accent */
{ "Iuml", 207 }, /* capital I, dieresis or umlaut mark */
{ "Ntilde", 209 }, /* capital N, tilde */
{ "Oacute", 211 }, /* capital O, acute accent */
{ "Ocirc", 212 }, /* capital O, circumflex accent */
{ "Ograve", 210 }, /* capital O, grave accent */
{ "Oslash", 216 }, /* capital O, slash */
{ "Otilde", 213 }, /* capital O, tilde */
{ "Ouml", 214 }, /* capital O, dieresis or umlaut mark */
{ "THORN", 222 }, /* capital THORN, Icelandic */
{ "Uacute", 218 }, /* capital U, acute accent */
{ "Ucirc", 219 }, /* capital U, circumflex accent */
{ "Ugrave", 217 }, /* capital U, grave accent */
{ "Uuml", 220 }, /* capital U, dieresis or umlaut mark */
{ "Yacute", 221 }, /* capital Y, acute accent */
{ "aacute", 225 }, /* small a, acute accent */
{ "acirc", 226 }, /* small a, circumflex accent */
{ "acute", 180 }, /* acute accent */
{ "aelig", 230 }, /* small ae diphthong (ligature) */
{ "agrave", 224 }, /* small a, grave accent */
{ "amp", 38 }, /* ampersand */
{ "aring", 229 }, /* small a, ring */
{ "atilde", 227 }, /* small a, tilde */
{ "auml", 228 }, /* small a, dieresis or umlaut mark */
{ "brvbar", 166 }, /* broken (vertical) bar */
{ "ccedil", 231 }, /* small c, cedilla */
{ "cedil", 184 }, /* cedilla */
{ "cent", 162 }, /* cent sign */
{ "copy", 169 }, /* copyright sign */
{ "curren", 164 }, /* general currency sign */
{ "deg", 176 }, /* degree sign */
{ "divide", 247 }, /* divide sign */
{ "eacute", 233 }, /* small e, acute accent */
{ "ecirc", 234 }, /* small e, circumflex accent */
{ "egrave", 232 }, /* small e, grave accent */
{ "eth", 240 }, /* small eth, Icelandic */
{ "euml", 235 }, /* small e, dieresis or umlaut mark */
{ "frac12", 189 }, /* fraction one-half */
{ "frac14", 188 }, /* fraction one-quarter */
{ "frac34", 190 }, /* fraction three-quarters */
{ "gt", 62 }, /* greater than */
{ "iacute", 237 }, /* small i, acute accent */
{ "icirc", 238 }, /* small i, circumflex accent */
{ "iexcl", 161 }, /* inverted exclamation mark */
{ "igrave", 236 }, /* small i, grave accent */
{ "iquest", 191 }, /* inverted question mark */
{ "iuml", 239 }, /* small i, dieresis or umlaut mark */
{ "laquo", 171 }, /* angle quotation mark, left */
{ "lt", 60 }, /* less than */
{ "macr", 175 }, /* macron */
{ "micro", 181 }, /* micro sign */
{ "middot", 183 }, /* middle dot */
{ "nbsp", 160 }, /* no-break space */
{ "not", 172 }, /* not sign */
{ "ntilde", 241 }, /* small n, tilde */
{ "oacute", 243 }, /* small o, acute accent */
{ "ocirc", 244 }, /* small o, circumflex accent */
{ "ograve", 242 }, /* small o, grave accent */
{ "ordf", 170 }, /* ordinal indicator, feminine */
{ "ordm", 186 }, /* ordinal indicator, masculine */
{ "oslash", 248 }, /* small o, slash */
{ "otilde", 245 }, /* small o, tilde */
{ "ouml", 246 }, /* small o, dieresis or umlaut mark */
{ "para", 182 }, /* pilcrow (paragraph sign) */
{ "plusmn", 177 }, /* plus-or-minus sign */
{ "pound", 163 }, /* pound sterling sign */
{ "quot", 34 }, /* double quote */
{ "raquo", 187 }, /* angle quotation mark, right */
{ "reg", 174 }, /* registered sign */
{ "sect", 167 }, /* section sign */
{ "shy", 173 }, /* soft hyphen */
{ "sup1", 185 }, /* superscript one */
{ "sup2", 178 }, /* superscript two */
{ "sup3", 179 }, /* superscript three */
{ "szlig", 223 }, /* small sharp s, German (sz ligature) */
{ "thorn", 254 }, /* small thorn, Icelandic */
{ "times", 215 }, /* multiply sign */
{ "uacute", 250 }, /* small u, acute accent */
{ "ucirc", 251 }, /* small u, circumflex accent */
{ "ugrave", 249 }, /* small u, grave accent */
{ "uml", 168 }, /* umlaut (dieresis) */
{ "uuml", 252 }, /* small u, dieresis or umlaut mark */
{ "yacute", 253 }, /* small y, acute accent */
{ "yen", 165 }, /* yen sign */
{ "yuml", 255 }, /* small y, dieresis or umlaut mark */
};
/* /*
* unvis - decode characters previously encoded by vis * unvis - decode characters previously encoded by vis
@ -57,276 +184,367 @@
int int
unvis(char *cp, int c, int *astate, int flag) unvis(char *cp, int c, int *astate, int flag)
{ {
unsigned char uc = (unsigned char)c;
unsigned char st, ia, is, lc;
/*
* Bottom 8 bits of astate hold the state machine state.
* Top 8 bits hold the current character in the http 1866 nv string decoding
*/
#define GS(a) ((a) & 0xff)
#define SS(a, b) (((uint32_t)(a) << 24) | (b))
#define GI(a) ((uint32_t)(a) >> 24)
_DIAGASSERT(cp != NULL);
_DIAGASSERT(astate != NULL);
st = GS(*astate);
if (flag & UNVIS_END) { if (flag & UNVIS_END) {
if (*astate == S_OCTAL2 || *astate == S_OCTAL3) { switch (st) {
*astate = S_GROUND; case S_OCTAL2:
return (UNVIS_VALID); case S_OCTAL3:
case S_HEX2:
*astate = SS(0, S_GROUND);
return UNVIS_VALID;
case S_GROUND:
return UNVIS_NOCHAR;
default:
return UNVIS_SYNBAD;
} }
return (*astate == S_GROUND ? UNVIS_NOCHAR : UNVIS_SYNBAD);
} }
switch (*astate & ~S_HTTP) { switch (st) {
case S_GROUND: case S_GROUND:
*cp = 0; *cp = 0;
if (c == '\\') { if ((flag & VIS_NOESCAPE) == 0 && c == '\\') {
*astate = S_START; *astate = SS(0, S_START);
return (0); return UNVIS_NOCHAR;
} }
if (flag & VIS_HTTPSTYLE && c == '%') { if ((flag & VIS_HTTP1808) && c == '%') {
*astate = S_START | S_HTTP; *astate = SS(0, S_HEX1);
return (0); return UNVIS_NOCHAR;
}
if ((flag & VIS_HTTP1866) && c == '&') {
*astate = SS(0, S_AMP);
return UNVIS_NOCHAR;
}
if ((flag & VIS_MIMESTYLE) && c == '=') {
*astate = SS(0, S_MIME1);
return UNVIS_NOCHAR;
} }
*cp = c; *cp = c;
return (UNVIS_VALID); return UNVIS_VALID;
case S_START: case S_START:
if (*astate & S_HTTP) {
if (ishex(tolower(c))) {
*cp = isdigit(c) ? (c - '0') : (tolower(c) - 'a');
*astate = S_HEX2;
return (0);
}
}
switch(c) { switch(c) {
case '\\': case '\\':
*cp = c; *cp = c;
*astate = S_GROUND; *astate = SS(0, S_GROUND);
return (UNVIS_VALID); return UNVIS_VALID;
case '0': case '1': case '2': case '3': case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7': case '4': case '5': case '6': case '7':
*cp = (c - '0'); *cp = (c - '0');
*astate = S_OCTAL2; *astate = SS(0, S_OCTAL2);
return (0); return UNVIS_NOCHAR;
case 'M': case 'M':
*cp = 0200; *cp = (char)0200;
*astate = S_META; *astate = SS(0, S_META);
return (0); return UNVIS_NOCHAR;
case '^': case '^':
*astate = S_CTRL; *astate = SS(0, S_CTRL);
return (0); return UNVIS_NOCHAR;
case 'n': case 'n':
*cp = '\n'; *cp = '\n';
*astate = S_GROUND; *astate = SS(0, S_GROUND);
return (UNVIS_VALID); return UNVIS_VALID;
case 'r': case 'r':
*cp = '\r'; *cp = '\r';
*astate = S_GROUND; *astate = SS(0, S_GROUND);
return (UNVIS_VALID); return UNVIS_VALID;
case 'b': case 'b':
*cp = '\b'; *cp = '\b';
*astate = S_GROUND; *astate = SS(0, S_GROUND);
return (UNVIS_VALID); return UNVIS_VALID;
case 'a': case 'a':
*cp = '\007'; *cp = '\007';
*astate = S_GROUND; *astate = SS(0, S_GROUND);
return (UNVIS_VALID); return UNVIS_VALID;
case 'v': case 'v':
*cp = '\v'; *cp = '\v';
*astate = S_GROUND; *astate = SS(0, S_GROUND);
return (UNVIS_VALID); return UNVIS_VALID;
case 't': case 't':
*cp = '\t'; *cp = '\t';
*astate = S_GROUND; *astate = SS(0, S_GROUND);
return (UNVIS_VALID); return UNVIS_VALID;
case 'f': case 'f':
*cp = '\f'; *cp = '\f';
*astate = S_GROUND; *astate = SS(0, S_GROUND);
return (UNVIS_VALID); return UNVIS_VALID;
case 's': case 's':
*cp = ' '; *cp = ' ';
*astate = S_GROUND; *astate = SS(0, S_GROUND);
return (UNVIS_VALID); return UNVIS_VALID;
case 'E': case 'E':
*cp = '\033'; *cp = '\033';
*astate = S_GROUND; *astate = SS(0, S_GROUND);
return (UNVIS_VALID); return UNVIS_VALID;
case 'x':
*astate = SS(0, S_HEX);
return UNVIS_NOCHAR;
case '\n': case '\n':
/* /*
* hidden newline * hidden newline
*/ */
*astate = S_GROUND; *astate = SS(0, S_GROUND);
return (UNVIS_NOCHAR); return UNVIS_NOCHAR;
case '$': case '$':
/* /*
* hidden marker * hidden marker
*/ */
*astate = S_GROUND; *astate = SS(0, S_GROUND);
return (UNVIS_NOCHAR); return UNVIS_NOCHAR;
default:
if (isgraph(c)) {
*cp = c;
*astate = SS(0, S_GROUND);
return UNVIS_VALID;
} }
*astate = S_GROUND; }
return (UNVIS_SYNBAD); goto bad;
case S_META: case S_META:
if (c == '-') if (c == '-')
*astate = S_META1; *astate = SS(0, S_META1);
else if (c == '^') else if (c == '^')
*astate = S_CTRL; *astate = SS(0, S_CTRL);
else { else
*astate = S_GROUND; goto bad;
return (UNVIS_SYNBAD); return UNVIS_NOCHAR;
}
return (0);
case S_META1: case S_META1:
*astate = S_GROUND; *astate = SS(0, S_GROUND);
*cp |= c; *cp |= c;
return (UNVIS_VALID); return UNVIS_VALID;
case S_CTRL: case S_CTRL:
if (c == '?') if (c == '?')
*cp |= 0177; *cp |= 0177;
else else
*cp |= c & 037; *cp |= c & 037;
*astate = S_GROUND; *astate = SS(0, S_GROUND);
return (UNVIS_VALID); return UNVIS_VALID;
case S_OCTAL2: /* second possible octal digit */ case S_OCTAL2: /* second possible octal digit */
if (isoctal(c)) { if (isoctal(uc)) {
/* /*
* yes - and maybe a third * yes - and maybe a third
*/ */
*cp = (*cp << 3) + (c - '0'); *cp = (*cp << 3) + (c - '0');
*astate = S_OCTAL3; *astate = SS(0, S_OCTAL3);
return (0); return UNVIS_NOCHAR;
} }
/* /*
* no - done with current sequence, push back passed char * no - done with current sequence, push back passed char
*/ */
*astate = S_GROUND; *astate = SS(0, S_GROUND);
return (UNVIS_VALIDPUSH); return UNVIS_VALIDPUSH;
case S_OCTAL3: /* third possible octal digit */ case S_OCTAL3: /* third possible octal digit */
*astate = S_GROUND; *astate = SS(0, S_GROUND);
if (isoctal(c)) { if (isoctal(uc)) {
*cp = (*cp << 3) + (c - '0'); *cp = (*cp << 3) + (c - '0');
return (UNVIS_VALID); return UNVIS_VALID;
} }
/* /*
* we were done, push back passed char * we were done, push back passed char
*/ */
return (UNVIS_VALIDPUSH); return UNVIS_VALIDPUSH;
case S_HEX2: /* second mandatory hex digit */ case S_HEX:
if (ishex(tolower(c))) { if (!isxdigit(uc))
*cp = (isdigit(c) ? (*cp << 4) + (c - '0') : (*cp << 4) + (tolower(c) - 'a' + 10)); goto bad;
/*FALLTHROUGH*/
case S_HEX1:
if (isxdigit(uc)) {
*cp = xtod(uc);
*astate = SS(0, S_HEX2);
return UNVIS_NOCHAR;
} }
/*
* no - done with current sequence, push back passed char
*/
*astate = SS(0, S_GROUND);
return UNVIS_VALIDPUSH;
case S_HEX2:
*astate = S_GROUND; *astate = S_GROUND;
return (UNVIS_VALID); if (isxdigit(uc)) {
*cp = xtod(uc) | (*cp << 4);
return UNVIS_VALID;
}
return UNVIS_VALIDPUSH;
case S_MIME1:
if (uc == '\n' || uc == '\r') {
*astate = SS(0, S_EATCRNL);
return UNVIS_NOCHAR;
}
if (isxdigit(uc) && (isdigit(uc) || isupper(uc))) {
*cp = XTOD(uc);
*astate = SS(0, S_MIME2);
return UNVIS_NOCHAR;
}
goto bad;
case S_MIME2:
if (isxdigit(uc) && (isdigit(uc) || isupper(uc))) {
*astate = SS(0, S_GROUND);
*cp = XTOD(uc) | (*cp << 4);
return UNVIS_VALID;
}
goto bad;
case S_EATCRNL:
switch (uc) {
case '\r':
case '\n':
return UNVIS_NOCHAR;
case '=':
*astate = SS(0, S_MIME1);
return UNVIS_NOCHAR;
default:
*cp = uc;
*astate = SS(0, S_GROUND);
return UNVIS_VALID;
}
case S_AMP:
*cp = 0;
if (uc == '#') {
*astate = SS(0, S_NUMBER);
return UNVIS_NOCHAR;
}
*astate = SS(0, S_STRING);
/*FALLTHROUGH*/
case S_STRING:
ia = *cp; /* index in the array */
is = GI(*astate); /* index in the string */
lc = is == 0 ? 0 : nv[ia].name[is - 1]; /* last character */
if (uc == ';')
uc = '\0';
for (; ia < __arraycount(nv); ia++) {
if (is != 0 && nv[ia].name[is - 1] != lc)
goto bad;
if (nv[ia].name[is] == uc)
break;
}
if (ia == __arraycount(nv))
goto bad;
if (uc != 0) {
*cp = ia;
*astate = SS(is + 1, S_STRING);
return UNVIS_NOCHAR;
}
*cp = nv[ia].value;
*astate = SS(0, S_GROUND);
return UNVIS_VALID;
case S_NUMBER:
if (uc == ';')
return UNVIS_VALID;
if (!isdigit(uc))
goto bad;
*cp += (*cp * 10) + uc - '0';
return UNVIS_NOCHAR;
default: default:
bad:
/* /*
* decoder in unknown state - (probably uninitialized) * decoder in unknown state - (probably uninitialized)
*/ */
*astate = S_GROUND; *astate = SS(0, S_GROUND);
return (UNVIS_SYNBAD); return UNVIS_SYNBAD;
} }
} }
/* /*
* strunvis - decode src into dst * strnunvisx - decode src into dst
* *
* Number of chars decoded into dst is returned, -1 on error. * Number of chars decoded into dst is returned, -1 on error.
* Dst is null terminated. * Dst is null terminated.
*/ */
int int
strunvis(char *dst, const char *src) strnunvisx(char *dst, size_t dlen, const char *src, int flag)
{ {
char c; char c;
char *start = dst; char t = '\0', *start = dst;
int state = 0; int state = 0;
while ((c = *src++)) { _DIAGASSERT(src != NULL);
_DIAGASSERT(dst != NULL);
#define CHECKSPACE() \
do { \
if (dlen-- == 0) { \
errno = ENOSPC; \
return -1; \
} \
} while (/*CONSTCOND*/0)
while ((c = *src++) != '\0') {
again: again:
switch (unvis(dst, c, &state, 0)) { switch (unvis(&t, c, &state, flag)) {
case UNVIS_VALID: case UNVIS_VALID:
dst++; CHECKSPACE();
*dst++ = t;
break; break;
case UNVIS_VALIDPUSH: case UNVIS_VALIDPUSH:
dst++; CHECKSPACE();
*dst++ = t;
goto again; goto again;
case 0: case 0:
case UNVIS_NOCHAR: case UNVIS_NOCHAR:
break; break;
case UNVIS_SYNBAD:
errno = EINVAL;
return -1;
default: default:
_DIAGASSERT(/*CONSTCOND*/0);
errno = EINVAL;
return -1;
}
}
if (unvis(&t, c, &state, UNVIS_END) == UNVIS_VALID) {
CHECKSPACE();
*dst++ = t;
}
CHECKSPACE();
*dst = '\0'; *dst = '\0';
return (-1); return (int)(dst - start);
}
}
if (unvis(dst, c, &state, UNVIS_END) == UNVIS_VALID)
dst++;
*dst = '\0';
return (dst - start);
}
ssize_t
strnunvis(char *dst, const char *src, size_t sz)
{
char c, p;
char *start = dst, *end = dst + sz - 1;
int state = 0;
if (sz > 0)
*end = '\0';
while ((c = *src++)) {
again:
switch (unvis(&p, c, &state, 0)) {
case UNVIS_VALID:
if (dst < end)
*dst = p;
dst++;
break;
case UNVIS_VALIDPUSH:
if (dst < end)
*dst = p;
dst++;
goto again;
case 0:
case UNVIS_NOCHAR:
break;
default:
if (dst <= end)
*dst = '\0';
return (-1);
}
}
if (unvis(&p, c, &state, UNVIS_END) == UNVIS_VALID) {
if (dst < end)
*dst = p;
dst++;
}
if (dst <= end)
*dst = '\0';
return (dst - start);
} }
int int
strunvisx(char *dst, const char *src, int flag) strunvisx(char *dst, const char *src, int flag)
{ {
char c; return strnunvisx(dst, (size_t)~0, src, flag);
char *start = dst; }
int state = 0;
while ((c = *src++)) { int
again: strunvis(char *dst, const char *src)
switch (unvis(dst, c, &state, flag)) { {
case UNVIS_VALID: return strnunvisx(dst, (size_t)~0, src, 0);
dst++;
break;
case UNVIS_VALIDPUSH:
dst++;
goto again;
case 0:
case UNVIS_NOCHAR:
break;
default:
return (-1);
} }
}
if (unvis(dst, c, &state, UNVIS_END) == UNVIS_VALID) int
dst++; strnunvis(char *dst, size_t dlen, const char *src)
*dst = '\0'; {
return (dst - start); return strnunvisx(dst, dlen, src, 0);
} }

874
src/vis.c
View File

@ -1,4 +1,5 @@
/* $OpenBSD: vis.c,v 1.18 2005/08/29 18:38:41 otto Exp $ */ /* $NetBSD: vis.c,v 1.74 2017/11/27 16:37:21 christos Exp $ */
/*- /*-
* Copyright (c) 1989, 1993 * Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved. * The Regents of the University of California. All rights reserved.
@ -28,220 +29,717 @@
* SUCH DAMAGE. * SUCH DAMAGE.
*/ */
#include <sys/types.h> /*-
#include <limits.h> * Copyright (c) 1999, 2005 The NetBSD Foundation, Inc.
#include <ctype.h> * All rights reserved.
#include <string.h> *
#include <stdio.h> * Redistribution and use in source and binary forms, with or without
#include <vis.h> * modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#define isoctal(c) \ #include <sys/cdefs.h>
(((unsigned char)(c)) >= '0' && ((unsigned char)(c)) <= '7') #include <sys/types.h>
#define isvisible(c) \ #include <sys/param.h>
(((unsigned int)(c) <= UCHAR_MAX && \
isascii((unsigned char)(c)) && \ #include <assert.h>
(((c) != '*' && (c) != '?' && (c) != '[' && (c) != '#') || \ #include <vis.h>
(flag & VIS_GLOB) == 0) && \ #include <errno.h>
isgraph((unsigned char)(c))) || \ #include <stdlib.h>
((flag & VIS_SP) == 0 && (c) == ' ') || \ #include <wchar.h>
((flag & VIS_TAB) == 0 && (c) == '\t') || \ #include <wctype.h>
((flag & VIS_NL) == 0 && (c) == '\n') || \
((flag & VIS_SAFE) && ((c) == '\b' || \ #ifdef __weak_alias
(c) == '\007' || (c) == '\r' || \ __weak_alias(strvisx,_strvisx)
isgraph((unsigned char)(c))))) #endif
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#define _DIAGASSERT(x)
/*
* The reason for going through the trouble to deal with character encodings
* in vis(3), is that we use this to safe encode output of commands. This
* safe encoding varies depending on the character set. For example if we
* display ps output in French, we don't want to display French characters
* as M-foo.
*/
static wchar_t *do_svis(wchar_t *, wint_t, int, wint_t, const wchar_t *);
#undef BELL
#define BELL L'\a'
#if defined(LC_C_LOCALE)
#define iscgraph(c) isgraph_l(c, LC_C_LOCALE)
#else
/* Keep it simple for now, no locale stuff */
#define iscgraph(c) isgraph(c)
#ifdef notyet
#include <locale.h>
static int
iscgraph(int c) {
int rv;
char *ol;
ol = setlocale(LC_CTYPE, "C");
rv = isgraph(c);
if (ol)
setlocale(LC_CTYPE, ol);
return rv;
}
#endif
#endif
#define ISGRAPH(flags, c) \
(((flags) & VIS_NOLOCALE) ? iscgraph(c) : iswgraph(c))
#define iswoctal(c) (((u_char)(c)) >= L'0' && ((u_char)(c)) <= L'7')
#define iswwhite(c) (c == L' ' || c == L'\t' || c == L'\n')
#define iswsafe(c) (c == L'\b' || c == BELL || c == L'\r')
#define xtoa(c) L"0123456789abcdef"[c]
#define XTOA(c) L"0123456789ABCDEF"[c]
#define MAXEXTRAS 30
static const wchar_t char_shell[] = L"'`\";&<>()|{}]\\$!^~";
static const wchar_t char_glob[] = L"*?[#";
/*
* On NetBSD and glibc MB_LEN_MAX is currently > 8 which does not fit on any
* integer integral type and it is probably wrong, since currently the maximum
* number of bytes and character needs is 6. Until this is fixed, the
* loops below are using sizeof(uint64_t) - 1 instead of MB_LEN_MAX, and
* the assertion is commented out.
*/
#if 0
#ifndef CTASSERT
#define CTASSERT(x) _CTASSERT(x, __LINE__)
#define _CTASSERT(x, y) __CTASSERT(x, y)
#define __CTASSERT(x, y) typedef char __assert ## y[(x) ? 1 : -1]
#endif
CTASSERT(MB_LEN_MAX <= sizeof(uint64_t));
#endif
/*
* This is do_hvis, for HTTP style (RFC 1808)
*/
static wchar_t *
do_hvis(wchar_t *dst, wint_t c, int flags, wint_t nextc, const wchar_t *extra)
{
if (iswalnum(c)
/* safe */
|| c == L'$' || c == L'-' || c == L'_' || c == L'.' || c == L'+'
/* extra */
|| c == L'!' || c == L'*' || c == L'\'' || c == L'(' || c == L')'
|| c == L',')
dst = do_svis(dst, c, flags, nextc, extra);
else {
*dst++ = L'%';
*dst++ = xtoa(((unsigned int)c >> 4) & 0xf);
*dst++ = xtoa((unsigned int)c & 0xf);
}
return dst;
}
/*
* This is do_mvis, for Quoted-Printable MIME (RFC 2045)
* NB: No handling of long lines or CRLF.
*/
static wchar_t *
do_mvis(wchar_t *dst, wint_t c, int flags, wint_t nextc, const wchar_t *extra)
{
if ((c != L'\n') &&
/* Space at the end of the line */
((iswspace(c) && (nextc == L'\r' || nextc == L'\n')) ||
/* Out of range */
(!iswspace(c) && (c < 33 || (c > 60 && c < 62) || c > 126)) ||
/* Specific char to be escaped */
wcschr(L"#$@[\\]^`{|}~", c) != NULL)) {
*dst++ = L'=';
*dst++ = XTOA(((unsigned int)c >> 4) & 0xf);
*dst++ = XTOA((unsigned int)c & 0xf);
} else
dst = do_svis(dst, c, flags, nextc, extra);
return dst;
}
/*
* Output single byte of multibyte character.
*/
static wchar_t *
do_mbyte(wchar_t *dst, wint_t c, int flags, wint_t nextc, int iswextra)
{
if (flags & VIS_CSTYLE) {
switch (c) {
case L'\n':
*dst++ = L'\\'; *dst++ = L'n';
return dst;
case L'\r':
*dst++ = L'\\'; *dst++ = L'r';
return dst;
case L'\b':
*dst++ = L'\\'; *dst++ = L'b';
return dst;
case BELL:
*dst++ = L'\\'; *dst++ = L'a';
return dst;
case L'\v':
*dst++ = L'\\'; *dst++ = L'v';
return dst;
case L'\t':
*dst++ = L'\\'; *dst++ = L't';
return dst;
case L'\f':
*dst++ = L'\\'; *dst++ = L'f';
return dst;
case L' ':
*dst++ = L'\\'; *dst++ = L's';
return dst;
case L'\0':
*dst++ = L'\\'; *dst++ = L'0';
if (iswoctal(nextc)) {
*dst++ = L'0';
*dst++ = L'0';
}
return dst;
/* We cannot encode these characters in VIS_CSTYLE
* because they special meaning */
case L'n':
case L'r':
case L'b':
case L'a':
case L'v':
case L't':
case L'f':
case L's':
case L'0':
case L'M':
case L'^':
case L'$': /* vis(1) -l */
break;
default:
if (ISGRAPH(flags, c) && !iswoctal(c)) {
*dst++ = L'\\';
*dst++ = c;
return dst;
}
}
}
if (iswextra || ((c & 0177) == L' ') || (flags & VIS_OCTAL)) {
*dst++ = L'\\';
*dst++ = (u_char)(((u_int32_t)(u_char)c >> 6) & 03) + L'0';
*dst++ = (u_char)(((u_int32_t)(u_char)c >> 3) & 07) + L'0';
*dst++ = (c & 07) + L'0';
} else {
if ((flags & VIS_NOSLASH) == 0)
*dst++ = L'\\';
if (c & 0200) {
c &= 0177;
*dst++ = L'M';
}
if (iswcntrl(c)) {
*dst++ = L'^';
if (c == 0177)
*dst++ = L'?';
else
*dst++ = c + L'@';
} else {
*dst++ = L'-';
*dst++ = c;
}
}
return dst;
}
/*
* This is do_vis, the central code of vis.
* dst: Pointer to the destination buffer
* c: Character to encode
* flags: Flags word
* nextc: The character following 'c'
* extra: Pointer to the list of extra characters to be
* backslash-protected.
*/
static wchar_t *
do_svis(wchar_t *dst, wint_t c, int flags, wint_t nextc, const wchar_t *extra)
{
int iswextra, i, shft;
uint64_t bmsk, wmsk;
iswextra = wcschr(extra, c) != NULL;
if (!iswextra && (ISGRAPH(flags, c) || iswwhite(c) ||
((flags & VIS_SAFE) && iswsafe(c)))) {
*dst++ = c;
return dst;
}
/* See comment in istrsenvisx() output loop, below. */
wmsk = 0;
for (i = sizeof(wmsk) - 1; i >= 0; i--) {
shft = i * NBBY;
bmsk = (uint64_t)0xffLL << shft;
wmsk |= bmsk;
if ((c & wmsk) || i == 0)
dst = do_mbyte(dst, (wint_t)(
(uint64_t)(c & bmsk) >> shft),
flags, nextc, iswextra);
}
return dst;
}
typedef wchar_t *(*visfun_t)(wchar_t *, wint_t, int, wint_t, const wchar_t *);
/*
* Return the appropriate encoding function depending on the flags given.
*/
static visfun_t
getvisfun(int flags)
{
if (flags & VIS_HTTPSTYLE)
return do_hvis;
if (flags & VIS_MIMESTYLE)
return do_mvis;
return do_svis;
}
/*
* Expand list of extra characters to not visually encode.
*/
static wchar_t *
makeextralist(int flags, const char *src)
{
wchar_t *dst, *d;
size_t len;
const wchar_t *s;
len = strlen(src);
if ((dst = calloc(len + MAXEXTRAS, sizeof(*dst))) == NULL)
return NULL;
if ((flags & VIS_NOLOCALE) || mbstowcs(dst, src, len) == (size_t)-1) {
size_t i;
for (i = 0; i < len; i++)
dst[i] = (wchar_t)(u_char)src[i];
d = dst + len;
} else
d = dst + wcslen(dst);
if (flags & VIS_GLOB)
for (s = char_glob; *s; *d++ = *s++)
continue;
if (flags & VIS_SHELL)
for (s = char_shell; *s; *d++ = *s++)
continue;
if (flags & VIS_SP) *d++ = L' ';
if (flags & VIS_TAB) *d++ = L'\t';
if (flags & VIS_NL) *d++ = L'\n';
if (flags & VIS_DQ) *d++ = L'"';
if ((flags & VIS_NOSLASH) == 0) *d++ = L'\\';
*d = L'\0';
return dst;
}
/*
* istrsenvisx()
* The main internal function.
* All user-visible functions call this one.
*/
static int
istrsenvisx(char **mbdstp, size_t *dlen, const char *mbsrc, size_t mblength,
int flags, const char *mbextra, int *cerr_ptr)
{
wchar_t *dst, *src, *pdst, *psrc, *start, *extra;
size_t len, olen;
uint64_t bmsk, wmsk;
wint_t c;
visfun_t f;
int clen = 0, cerr, error = -1, i, shft;
char *mbdst, *mdst;
ssize_t mbslength, maxolen;
_DIAGASSERT(mbdstp != NULL);
_DIAGASSERT(mbsrc != NULL || mblength == 0);
_DIAGASSERT(mbextra != NULL);
mbslength = (ssize_t)mblength;
/*
* When inputing a single character, must also read in the
* next character for nextc, the look-ahead character.
*/
if (mbslength == 1)
mbslength++;
/*
* Input (mbsrc) is a char string considered to be multibyte
* characters. The input loop will read this string pulling
* one character, possibly multiple bytes, from mbsrc and
* converting each to wchar_t in src.
*
* The vis conversion will be done using the wide char
* wchar_t string.
*
* This will then be converted back to a multibyte string to
* return to the caller.
*/
/* Allocate space for the wide char strings */
psrc = pdst = extra = NULL;
mdst = NULL;
if ((psrc = calloc(mbslength + 1, sizeof(*psrc))) == NULL)
return -1;
if ((pdst = calloc((16 * mbslength) + 1, sizeof(*pdst))) == NULL)
goto out;
if (*mbdstp == NULL) {
if ((mdst = calloc((16 * mbslength) + 1, sizeof(*mdst))) == NULL)
goto out;
*mbdstp = mdst;
}
mbdst = *mbdstp;
dst = pdst;
src = psrc;
if (flags & VIS_NOLOCALE) {
/* Do one byte at a time conversion */
cerr = 1;
} else {
/* Use caller's multibyte conversion error flag. */
cerr = cerr_ptr ? *cerr_ptr : 0;
}
/*
* Input loop.
* Handle up to mblength characters (not bytes). We do not
* stop at NULs because we may be processing a block of data
* that includes NULs.
*/
while (mbslength > 0) {
/* Convert one multibyte character to wchar_t. */
if (!cerr)
clen = mbtowc(src, mbsrc, MB_LEN_MAX);
if (cerr || clen < 0) {
/* Conversion error, process as a byte instead. */
*src = (wint_t)(u_char)*mbsrc;
clen = 1;
cerr = 1;
}
if (clen == 0) {
/*
* NUL in input gives 0 return value. process
* as single NUL byte and keep going.
*/
clen = 1;
}
/* Advance buffer character pointer. */
src++;
/* Advance input pointer by number of bytes read. */
mbsrc += clen;
/* Decrement input byte count. */
mbslength -= clen;
}
len = src - psrc;
src = psrc;
/*
* In the single character input case, we will have actually
* processed two characters, c and nextc. Reset len back to
* just a single character.
*/
if (mblength < len)
len = mblength;
/* Convert extra argument to list of characters for this mode. */
extra = makeextralist(flags, mbextra);
if (!extra) {
if (dlen && *dlen == 0) {
errno = ENOSPC;
goto out;
}
*mbdst = '\0'; /* can't create extra, return "" */
error = 0;
goto out;
}
/* Look up which processing function to call. */
f = getvisfun(flags);
/*
* Main processing loop.
* Call do_Xvis processing function one character at a time
* with next character available for look-ahead.
*/
for (start = dst; len > 0; len--) {
c = *src++;
dst = (*f)(dst, c, flags, len >= 1 ? *src : L'\0', extra);
if (dst == NULL) {
errno = ENOSPC;
goto out;
}
}
/* Terminate the string in the buffer. */
*dst = L'\0';
/*
* Output loop.
* Convert wchar_t string back to multibyte output string.
* If we have hit a multi-byte conversion error on input,
* output byte-by-byte here. Else use wctomb().
*/
len = wcslen(start);
maxolen = dlen ? *dlen : (wcslen(start) * MB_LEN_MAX + 1);
olen = 0;
for (dst = start; len > 0; len--) {
if (!cerr)
clen = wctomb(mbdst, *dst);
if (cerr || clen < 0) {
/*
* Conversion error, process as a byte(s) instead.
* Examine each byte and higher-order bytes for
* data. E.g.,
* 0x000000000000a264 -> a2 64
* 0x000000001f00a264 -> 1f 00 a2 64
*/
clen = 0;
wmsk = 0;
for (i = sizeof(wmsk) - 1; i >= 0; i--) {
shft = i * NBBY;
bmsk = (uint64_t)0xffLL << shft;
wmsk |= bmsk;
if ((*dst & wmsk) || i == 0)
mbdst[clen++] = (char)(
(uint64_t)(*dst & bmsk) >>
shft);
}
cerr = 1;
}
/* If this character would exceed our output limit, stop. */
if (olen + clen > (size_t)maxolen)
break;
/* Advance output pointer by number of bytes written. */
mbdst += clen;
/* Advance buffer character pointer. */
dst++;
/* Incrment output character count. */
olen += clen;
}
/* Terminate the output string. */
*mbdst = '\0';
if (flags & VIS_NOLOCALE) {
/* Pass conversion error flag out. */
if (cerr_ptr)
*cerr_ptr = cerr;
}
free(extra);
free(pdst);
free(psrc);
return (int)olen;
out:
free(extra);
free(pdst);
free(psrc);
free(mdst);
return error;
}
static int
istrsenvisxl(char **mbdstp, size_t *dlen, const char *mbsrc,
int flags, const char *mbextra, int *cerr_ptr)
{
return istrsenvisx(mbdstp, dlen, mbsrc,
mbsrc != NULL ? strlen(mbsrc) : 0, flags, mbextra, cerr_ptr);
}
/*
* The "svis" variants all take an "extra" arg that is a pointer
* to a NUL-terminated list of characters to be encoded, too.
* These functions are useful e. g. to encode strings in such a
* way so that they are not interpreted by a shell.
*/
char *
svis(char *mbdst, int c, int flags, int nextc, const char *mbextra)
{
char cc[2];
int ret;
cc[0] = c;
cc[1] = nextc;
ret = istrsenvisx(&mbdst, NULL, cc, 1, flags, mbextra, NULL);
if (ret < 0)
return NULL;
return mbdst + ret;
}
char *
snvis(char *mbdst, size_t dlen, int c, int flags, int nextc, const char *mbextra)
{
char cc[2];
int ret;
cc[0] = c;
cc[1] = nextc;
ret = istrsenvisx(&mbdst, &dlen, cc, 1, flags, mbextra, NULL);
if (ret < 0)
return NULL;
return mbdst + ret;
}
int
strsvis(char *mbdst, const char *mbsrc, int flags, const char *mbextra)
{
return istrsenvisxl(&mbdst, NULL, mbsrc, flags, mbextra, NULL);
}
int
strsnvis(char *mbdst, size_t dlen, const char *mbsrc, int flags, const char *mbextra)
{
return istrsenvisxl(&mbdst, &dlen, mbsrc, flags, mbextra, NULL);
}
int
strsvisx(char *mbdst, const char *mbsrc, size_t len, int flags, const char *mbextra)
{
return istrsenvisx(&mbdst, NULL, mbsrc, len, flags, mbextra, NULL);
}
int
strsnvisx(char *mbdst, size_t dlen, const char *mbsrc, size_t len, int flags,
const char *mbextra)
{
return istrsenvisx(&mbdst, &dlen, mbsrc, len, flags, mbextra, NULL);
}
int
strsenvisx(char *mbdst, size_t dlen, const char *mbsrc, size_t len, int flags,
const char *mbextra, int *cerr_ptr)
{
return istrsenvisx(&mbdst, &dlen, mbsrc, len, flags, mbextra, cerr_ptr);
}
/* /*
* vis - visually encode characters * vis - visually encode characters
*/ */
char * char *
vis(char *dst, int c, int flag, int nextc) vis(char *mbdst, int c, int flags, int nextc)
{ {
c = (unsigned char)c; char cc[2];
int ret;
if (flag & VIS_HTTPSTYLE) { cc[0] = c;
/* Described in RFC 1808 */ cc[1] = nextc;
if (!(isalnum(c) /* alpha-numeric */
/* safe */ ret = istrsenvisx(&mbdst, NULL, cc, 1, flags, "", NULL);
|| c == '$' || c == '-' || c == '_' || c == '.' || c == '+' if (ret < 0)
/* extra */ return NULL;
|| c == '!' || c == '*' || c == '\'' || c == '(' return mbdst + ret;
|| c == ')' || c == ',')) {
*dst++ = '%';
snprintf(dst, 4, (c < 16 ? "0%X" : "%X"), c);
dst += 2;
goto done;
}
} }
if ((flag & VIS_GLOB) && char *
(c == '*' || c == '?' || c == '[' || c == '#')) nvis(char *mbdst, size_t dlen, int c, int flags, int nextc)
; {
else if (isgraph(c) || char cc[2];
((flag & VIS_SP) == 0 && c == ' ') || int ret;
((flag & VIS_TAB) == 0 && c == '\t') ||
((flag & VIS_NL) == 0 && c == '\n') ||
((flag & VIS_SAFE) && (c == '\b' || c == '\007' || c == '\r'))) {
*dst++ = c;
if (c == '\\' && (flag & VIS_NOSLASH) == 0)
*dst++ = '\\';
*dst = '\0';
return (dst);
}
if (flag & VIS_CSTYLE) { cc[0] = c;
switch(c) { cc[1] = nextc;
case '\n':
*dst++ = '\\'; ret = istrsenvisx(&mbdst, &dlen, cc, 1, flags, "", NULL);
*dst++ = 'n'; if (ret < 0)
goto done; return NULL;
case '\r': return mbdst + ret;
*dst++ = '\\';
*dst++ = 'r';
goto done;
case '\b':
*dst++ = '\\';
*dst++ = 'b';
goto done;
case '\a':
*dst++ = '\\';
*dst++ = 'a';
goto done;
case '\v':
*dst++ = '\\';
*dst++ = 'v';
goto done;
case '\t':
*dst++ = '\\';
*dst++ = 't';
goto done;
case '\f':
*dst++ = '\\';
*dst++ = 'f';
goto done;
case ' ':
*dst++ = '\\';
*dst++ = 's';
goto done;
case '\0':
*dst++ = '\\';
*dst++ = '0';
if (isoctal(nextc)) {
*dst++ = '0';
*dst++ = '0';
}
goto done;
}
}
if (((c & 0177) == ' ') || isgraph(c) || (flag & VIS_OCTAL)) {
*dst++ = '\\';
*dst++ = ((unsigned char)c >> 6 & 07) + '0';
*dst++ = ((unsigned char)c >> 3 & 07) + '0';
*dst++ = ((unsigned char)c & 07) + '0';
goto done;
}
if ((flag & VIS_NOSLASH) == 0)
*dst++ = '\\';
if (c & 0200) {
c &= 0177;
*dst++ = 'M';
}
if (iscntrl(c)) {
*dst++ = '^';
if (c == 0177)
*dst++ = '?';
else
*dst++ = c + '@';
} else {
*dst++ = '-';
*dst++ = c;
}
done:
*dst = '\0';
return (dst);
} }
/* /*
* strvis, strnvis, strvisx - visually encode characters from src into dst * strvis - visually encode characters from src into dst
* *
* Dst must be 4 times the size of src to account for possible * Dst must be 4 times the size of src to account for possible
* expansion. The length of dst, not including the trailing NUL, * expansion. The length of dst, not including the trailing NULL,
* is returned.
*/
int
strvis(char *mbdst, const char *mbsrc, int flags)
{
return istrsenvisxl(&mbdst, NULL, mbsrc, flags, "", NULL);
}
int
strnvis(char *mbdst, size_t dlen, const char *mbsrc, int flags)
{
return istrsenvisxl(&mbdst, &dlen, mbsrc, flags, "", NULL);
}
int
stravis(char **mbdstp, const char *mbsrc, int flags)
{
*mbdstp = NULL;
return istrsenvisxl(mbdstp, NULL, mbsrc, flags, "", NULL);
}
/*
* strvisx - visually encode characters from src into dst
*
* Dst must be 4 times the size of src to account for possible
* expansion. The length of dst, not including the trailing NULL,
* is returned. * is returned.
* *
* Strnvis will write no more than siz-1 bytes (and will NULL terminate). * Strvisx encodes exactly len characters from src into dst.
* The number of bytes needed to fully encode the string is returned.
*
* Strvisx encodes exactly len bytes from src into dst.
* This is useful for encoding a block of data. * This is useful for encoding a block of data.
*/ */
int
strvis(char *dst, const char *src, int flag)
{
char c;
char *start;
for (start = dst; (c = *src); ) int
dst = vis(dst, c, flag, *++src); strvisx(char *mbdst, const char *mbsrc, size_t len, int flags)
*dst = '\0'; {
return (dst - start); return istrsenvisx(&mbdst, NULL, mbsrc, len, flags, "", NULL);
} }
int int
strnvis(char *dst, const char *src, size_t siz, int flag) strnvisx(char *mbdst, size_t dlen, const char *mbsrc, size_t len, int flags)
{ {
char *start, *end; return istrsenvisx(&mbdst, &dlen, mbsrc, len, flags, "", NULL);
char tbuf[5];
int c, i;
i = 0;
for (start = dst, end = start + siz - 1; (c = *src) && dst < end; ) {
if (isvisible(c)) {
i = 1;
*dst++ = c;
if (c == '\\' && (flag & VIS_NOSLASH) == 0) {
/* need space for the extra '\\' */
if (dst < end)
*dst++ = '\\';
else {
dst--;
i = 2;
break;
}
}
src++;
} else {
i = vis(tbuf, c, flag, *++src) - tbuf;
if (dst + i <= end) {
memcpy(dst, tbuf, i);
dst += i;
} else {
src--;
break;
}
}
}
if (siz > 0)
*dst = '\0';
if (dst + i > end) {
/* adjust return value for truncation */
while ((c = *src))
dst += vis(tbuf, c, flag, *++src) - tbuf;
}
return (dst - start);
} }
int int
strvisx(char *dst, const char *src, size_t len, int flag) strenvisx(char *mbdst, size_t dlen, const char *mbsrc, size_t len, int flags,
int *cerr_ptr)
{ {
int c; return istrsenvisx(&mbdst, &dlen, mbsrc, len, flags, "", cerr_ptr);
char *start;
for (start = dst; len > 1; len--) {
c = *src;
dst = vis(dst, c, flag, *++src);
}
if (len)
dst = vis(dst, *src, flag, '\0');
*dst = '\0';
return (dst - start);
} }