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/strnstr.c
src/unvis.c
src/vis.c
Copyright:
Copyright © 1980, 1982, 1986, 1989-1994
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
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:
include/bsd/sys/endian.h
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
* The Regents of the University of California. All rights reserved.
@ -27,7 +29,6 @@
* SUCH DAMAGE.
*
* @(#)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
@ -38,25 +39,34 @@
/*
* to select alternate encoding format
*/
#define VIS_OCTAL 0x01 /* use octal \ddd format */
#define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropriate */
#define VIS_OCTAL 0x0001 /* use octal \ddd format */
#define VIS_CSTYLE 0x0002 /* use \[nrft0..] where appropriate */
/*
* to alter set of characters encoded (default is to encode all
* non-graphic except space, tab, and newline).
*/
#define VIS_SP 0x04 /* also encode space */
#define VIS_TAB 0x08 /* also encode tab */
#define VIS_NL 0x10 /* also encode newline */
#define VIS_SP 0x0004 /* also encode space */
#define VIS_TAB 0x0008 /* also encode tab */
#define VIS_NL 0x0010 /* also encode newline */
#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
*/
#define VIS_NOSLASH 0x40 /* inhibit printing '\' */
#define VIS_HTTPSTYLE 0x80 /* http-style escape % HEX HEX */
#define VIS_GLOB 0x100 /* encode glob(3) magics */
#define VIS_NOSLASH 0x0040 /* inhibit printing '\' */
#define VIS_HTTP1808 0x0080 /* http-style escape % hex hex */
#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
@ -70,7 +80,7 @@
/*
* unvis flags
*/
#define UNVIS_END 1 /* no more characters */
#define UNVIS_END _VIS_END /* no more characters */
#ifdef LIBBSD_OVERLAY
#include <sys/cdefs.h>
@ -80,12 +90,33 @@
__BEGIN_DECLS
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 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 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 strnunvis(char *, size_t, const char *);
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);
__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
.\" 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
.\" SUCH DAMAGE.
.\"
.Dd $Mdocdate: May 31 2007 $
.\" @(#)unvis.3 8.2 (Berkeley) 12/11/93
.\"
.Dd March 12, 2011
.Dt UNVIS 3bsd
.Os
.Sh NAME
.Nm unvis ,
.Nm strunvis ,
.Nm strnunvis
.Nm strnunvis ,
.Nm strunvisx ,
.Nm strnunvisx
.Nd decode a visual representation of characters
.Sh LIBRARY
.ds str-Lb-libbsd Utility functions from BSD systems (libbsd, \-lbsd)
@ -44,88 +48,91 @@
.Xr libbsd 7
for include usage.)
.Ft int
.Fn unvis "char *cp" "char c" "int *astate" "int flag"
.Fn unvis "char *cp" "int c" "int *astate" "int flag"
.Ft int
.Fn strunvis "char *dst" "char *src"
.Ft ssize_t
.Fn strnunvis "char *dst" "char *src" "size_t size"
.Fn strunvis "char *dst" "const char *src"
.Ft int
.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
The
.Fn unvis ,
.Fn strunvis
and
.Fn strnunvis
functions are used to decode a visual representation of characters,
as produced by the
.Fn strunvisx
functions
are used to decode a visual representation of characters, as produced
by the
.Xr vis 3bsd
function, back into the original form.
function, back into
the original form.
.Pp
The
.Fn unvis
is called with successive characters in
.Fa c
until a valid
sequence is recognized, at which time the decoded character is
available at the character pointed to by
.Fa cp .
function is called with successive characters in
.Ar c
until a valid sequence is recognized, at which time the decoded
character is available at the character pointed to by
.Ar cp .
.Pp
The
.Fn strunvis
decodes the characters pointed to by
.Fa src
function decodes the characters pointed to by
.Ar src
into the buffer pointed to by
.Fa 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.
.Ar dst .
The
.Fn strunvis
function simply copies
.Fa src
.Ar src
to
.Fa dst ,
.Ar dst ,
decoding any escape sequences along the way,
and returns the number of characters placed into
.Fa dst ,
.Ar dst ,
or \-1 if an
invalid escape sequence was detected.
The size of
.Fa dst
should be
equal to the size of
.Fa src
.Ar dst
should be equal to the size of
.Ar src
(that is, no expansion takes place during decoding).
.Pp
The
.Fn strunvisx
function does the same as the
.Fn strunvis
terminates the destination string with a trailing NUL byte;
.Fn strnunvis
does so if
.Fa size
is larger than 0.
function,
but it allows you to add a flag that specifies the style the string
.Ar src
is encoded with.
Currently, the supported flags are:
.Dv VIS_HTTPSTYLE
and
.Dv VIS_MIMESTYLE .
.Pp
The
.Fn unvis
function implements a state machine that can be used to decode an arbitrary
stream of bytes.
function implements a state machine that can be used to decode an
arbitrary stream of bytes.
All state associated with the bytes being decoded is stored outside the
.Fn unvis
function (that is, a pointer to the state is passed in), so
calls decoding different streams can be freely intermixed.
To start decoding a stream of bytes, first initialize an integer
to zero.
To start decoding a stream of bytes, first initialize an integer to zero.
Call
.Fn unvis
with each successive byte, along with a pointer
to this integer, and a pointer to a destination character.
.Sh RETURN VALUES
The
.Fn unvis
function has several return codes that must be handled properly.
They are:
.Bl -tag -width UNVIS_VALIDPUSH
.It Li \&0 (zero)
.It Li \&0 No (zero)
Another character is necessary; nothing has been recognized yet.
.It Dv UNVIS_VALID
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.
This return code is necessary to indicate a logical break between characters.
.It Dv UNVIS_SYNBAD
An invalid escape sequence was detected, or the decoder is in an
unknown state.
An invalid escape sequence was detected, or the decoder is in an unknown state.
The decoder is placed into the starting state.
.El
.Pp
When all bytes in the stream have been processed, call
.Fn unvis
one more time with
.Fa flag
set to
one more time with flag set to
.Dv UNVIS_END
to extract any remaining character (the character passed in is ignored).
.Pp
The
.Fn strunvis
function returns the number of bytes written (not counting
the trailing NUL byte) or \-1 if an error occurred.
.Fa flag
argument is also used to specify the encoding style of the source.
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
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
.Fn unvis .
.Bd -literal -offset indent
@ -177,25 +195,72 @@ again:
case UNVIS_NOCHAR:
break;
case UNVIS_VALID:
(void) putchar(out);
(void)putchar(out);
break;
case UNVIS_VALIDPUSH:
(void) putchar(out);
(void)putchar(out);
goto again;
case UNVIS_SYNBAD:
(void)fprintf(stderr, "bad sequence!\en");
exit(1);
errx(EXIT_FAILURE, "Bad character sequence!");
}
}
if (unvis(&out, (char)0, &state, UNVIS_END) == UNVIS_VALID)
(void) putchar(out);
if (unvis(&out, '\e0', &state, UNVIS_END) == UNVIS_VALID)
(void)putchar(out);
.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
.Xr unvis 1 ,
.Xr vis 1 ,
.Xr vis 3bsd
.Rs
.%A R. Fielding
.%T Relative Uniform Resource Locators
.%O RFC1808
.Re
.Sh HISTORY
The
.Fn unvis
function first appeared in
function
first appeared in
.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
.\" 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
.\" SUCH DAMAGE.
.\"
.Dd $Mdocdate: May 31 2007 $
.\" @(#)vis.3 8.1 (Berkeley) 6/9/93
.\"
.Dd April 22, 2017
.Dt VIS 3bsd
.Os
.Sh NAME
.Nm vis ,
.Nm nvis ,
.Nm strvis ,
.Nm stravis ,
.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
.Sh LIBRARY
.ds str-Lb-libbsd Utility functions from BSD systems (libbsd, \-lbsd)
.Lb libbsd
.Sh SYNOPSIS
.In stdlib.h
.In vis.h
(See
.Xr libbsd 7
for include usage.)
.Ft char *
.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
.Fn strvis "char *dst" "const char *src" "int flag"
.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
.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
The
.Fn vis
function copies into
function
copies into
.Fa dst
a string which represents the character
.Fa c .
If
.Fa c
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.
The maximum length of any encoding is four
characters (not including the trailing NUL);
bytes (not including the trailing
.Dv NUL ) ;
thus, when
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
NUL.
The
.Fa flag
parameter is used for altering the default range of
be four times the number of bytes encoded, plus one for the trailing
.Dv NUL .
The flag parameter is used for altering the default range of
characters considered for encoding and for altering the visual
representation.
The additional character,
@ -84,9 +118,11 @@ encoding format (explained below).
.Pp
The
.Fn strvis ,
.Fn strnvis
.Fn stravis ,
.Fn strnvis ,
.Fn strvisx ,
and
.Fn strvisx
.Fn strnvisx
functions copy into
.Fa dst
a visual representation of
@ -94,89 +130,153 @@ the string
.Fa src .
The
.Fn strvis
function encodes characters from
.Fa src
up to the first NUL.
The
and
.Fn strnvis
function encodes characters from
functions encode characters from
.Fa src
up to the first NUL or the end of
.Fa dst ,
as indicated by
.Fa size .
up to the
first
.Dv NUL .
The
.Fn strvisx
function encodes exactly
and
.Fn strnvisx
functions encode exactly
.Fa len
characters from
.Fa src
(this
is useful for encoding a block of data that may contain NULs).
All three forms NUL terminate
.Fa dst ,
except for
.Fn strnvis
when
.Fa size
is zero, in which case
.Fa dst
is not touched.
For
.Fn strvis
and
.Fn strvisx ,
the size of
is useful for encoding a block of data that may contain
.Dv NUL Ns 's ) .
Both forms
.Dv NUL
terminate
.Fa dst .
The size of
.Fa dst
must be four times the number
of characters encoded from
of bytes encoded from
.Fa src
(plus one for the NUL).
.Fn strvis
(plus one for the
.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
.Fn strvisx
return the number of characters in
.Fa dst
(not including the trailing NUL).
.Fn strnvis
returns the length that
.Fa dst
would become if it were of unlimited size (similar to
.Xr snprintf 3
or
.Xr strlcpy 3bsd ) .
This can be used to detect truncation but it also means that
the return value of
.Fn strnvis
must not be used without checking it against
.Fa size .
.Fn strnvisx
functions return \-1 and set
.Va errno
to
.Dv ENOSPC .
The
.Fn strenvisx
function takes an additional argument,
.Fa cerr_ptr ,
that is used to pass in and out a multibyte conversion error flag.
This is useful when processing single characters at a time when
it is possible that the locale may be set to something other
than the locale of the characters in the input data.
.Pp
The functions
.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
The encoding is a unique, invertible representation composed entirely of
graphic characters; it can be decoded back into the original form using
the
.Xr unvis 3bsd
or
.Xr unvis 3bsd ,
.Xr strunvis 3bsd
or
.Xr strnunvis 3bsd
functions.
.Pp
There are two parameters that can be controlled: the range of
characters that are encoded, and the type
of representation used.
By default, all non-graphic characters
except space, tab, and newline are encoded
(see
characters that are encoded (applies only to
.Fn vis ,
.Fn nvis ,
.Fn strvis ,
.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 ) .
The following flags
alter this:
.Bl -tag -width VIS_WHITEX
.It Dv VIS_DQ
Also encode double quotes
.It Dv VIS_GLOB
Also encode magic characters recognized by
.Xr glob 3
.Pf ( Ql * ,
Also encode the magic characters
.Ql ( * ,
.Ql \&? ,
.Ql \&[ )
.Ql \&[ ,
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
Also encode space.
.It Dv VIS_TAB
@ -185,34 +285,56 @@ Also encode tab.
Also encode newline.
.It Dv VIS_WHITE
Synonym for
.Dv VIS_SP
\&|
.Dv VIS_TAB
\&|
.Dv VIS_NL .
.Dv VIS_SP | VIS_TAB | VIS_NL .
.It Dv VIS_META
Synonym for
.Dv VIS_WHITE | VIS_GLOB | VIS_SHELL .
.It Dv VIS_SAFE
Only encode
.Dq unsafe
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.
Currently this form allows space,
tab, newline, backspace, bell, and return -- in addition
to all graphic characters -- unencoded.
Currently this form allows space, tab, newline, backspace, bell, and
return \(em in addition to all graphic characters \(em unencoded.
.El
.Pp
There are three forms of encoding.
All forms use the backslash
(The above flags have no effect for
.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
character to introduce a special
sequence; two backslashes are used to represent a real backslash.
to introduce a special
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:
.Bl -tag -width VIS_CSTYLE
.It (default)
Use an
.Ql M
to represent meta characters (characters with the 8th
bit set), and use a caret
bit set), and use caret
.Ql ^
to represent control characters (see
.Xr iscntrl 3 ) .
@ -256,27 +378,27 @@ space.
.It Dv \e240
Represents Meta-space.
.El
.Pp
.It Dv VIS_CSTYLE
Use C-style backslash sequences to represent standard non-printable
characters.
The following sequences are used to represent the indicated characters:
.Bd -unfilled -offset indent
.Li \ea Tn - BEL No (007)
.Li \eb Tn - BS No (010)
.Li \ef Tn - NP No (014)
.Li \en Tn - NL No (012)
.Li \er Tn - CR No (015)
.Li \es Tn - SP No (040)
.Li \et Tn - HT No (011)
.Li \ev Tn - VT No (013)
.Li \e0 Tn - NUL No (000)
.Li \ea Tn \(em BEL No (007)
.Li \eb Tn \(em BS No (010)
.Li \ef Tn \(em NP No (014)
.Li \en Tn \(em NL No (012)
.Li \er Tn \(em CR No (015)
.Li \es Tn \(em SP No (040)
.Li \et Tn \(em HT No (011)
.Li \ev Tn \(em VT No (013)
.Li \e0 Tn \(em NUL No (000)
.Ed
.Pp
When using this format, the
.Fa nextc
parameter is looked at to determine
if a NUL character can be encoded as
parameter is looked at to determine if a
.Dv NUL
character can be encoded as
.Ql \e0
instead of
.Ql \e000 .
@ -284,13 +406,36 @@ If
.Fa nextc
is an octal digit, the latter representation is used to
avoid ambiguity.
.Pp
Non-printable characters without C-style
backslash sequences use the default representation.
.It Dv VIS_OCTAL
Use a three digit octal sequence.
The form is
.Ql \eddd
where
.Ar d
.Em d
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
.Pp
There is one additional flag,
@ -304,21 +449,112 @@ meta characters as
.Ql M-C ) .
With this flag set, the encoding is
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
.Xr unvis 1 ,
.Xr vis 1 ,
.Xr snprintf 3 ,
.Xr strlcpy 3bsd ,
.Xr glob 3 ,
.\" .Xr multibyte 3 ,
.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
The
.Fn vis ,
.Fn strvis
.Fn strvis ,
and
.Fn strvisx
functions first appeared in
.Bx 4.4 .
The
.Fn strnvis
function first appeared in
.Ox 2.9 .
.Fn svis ,
.Fn strsvis ,
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;
pidfile_fileno;
nvis;
snvis;
stravis;
strenvisx;
strnunvisx;
strsenvisx;
strsnvis;
strsnvisx;
strsvis;
strsvisx;
svis;
} 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
* The Regents of the University of California. All rights reserved.
@ -27,10 +29,22 @@
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <errno.h>
#include <vis.h>
#ifdef __weak_alias
__weak_alias(strnunvisx,_strnunvisx)
#endif
#define _DIAGASSERT(x)
/*
* decode driven by state machine
*/
@ -41,15 +55,128 @@
#define S_CTRL 4 /* control char started (^) */
#define S_OCTAL2 5 /* octal digit 2 */
#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')
#define ishex(c) \
((((unsigned char)(c)) >= '0' && ((unsigned char)(c)) <= '9') || \
(((unsigned char)(c)) >= 'a' && ((unsigned char)(c)) <= 'f'))
/*
* RFC 1866
*/
static const struct nv {
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
@ -57,276 +184,367 @@
int
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 (*astate == S_OCTAL2 || *astate == S_OCTAL3) {
*astate = S_GROUND;
return (UNVIS_VALID);
switch (st) {
case S_OCTAL2:
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:
*cp = 0;
if (c == '\\') {
*astate = S_START;
return (0);
if ((flag & VIS_NOESCAPE) == 0 && c == '\\') {
*astate = SS(0, S_START);
return UNVIS_NOCHAR;
}
if (flag & VIS_HTTPSTYLE && c == '%') {
*astate = S_START | S_HTTP;
return (0);
if ((flag & VIS_HTTP1808) && c == '%') {
*astate = SS(0, S_HEX1);
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;
return (UNVIS_VALID);
return UNVIS_VALID;
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) {
case '\\':
*cp = c;
*astate = S_GROUND;
return (UNVIS_VALID);
*astate = SS(0, S_GROUND);
return UNVIS_VALID;
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
*cp = (c - '0');
*astate = S_OCTAL2;
return (0);
*astate = SS(0, S_OCTAL2);
return UNVIS_NOCHAR;
case 'M':
*cp = 0200;
*astate = S_META;
return (0);
*cp = (char)0200;
*astate = SS(0, S_META);
return UNVIS_NOCHAR;
case '^':
*astate = S_CTRL;
return (0);
*astate = SS(0, S_CTRL);
return UNVIS_NOCHAR;
case 'n':
*cp = '\n';
*astate = S_GROUND;
return (UNVIS_VALID);
*astate = SS(0, S_GROUND);
return UNVIS_VALID;
case 'r':
*cp = '\r';
*astate = S_GROUND;
return (UNVIS_VALID);
*astate = SS(0, S_GROUND);
return UNVIS_VALID;
case 'b':
*cp = '\b';
*astate = S_GROUND;
return (UNVIS_VALID);
*astate = SS(0, S_GROUND);
return UNVIS_VALID;
case 'a':
*cp = '\007';
*astate = S_GROUND;
return (UNVIS_VALID);
*astate = SS(0, S_GROUND);
return UNVIS_VALID;
case 'v':
*cp = '\v';
*astate = S_GROUND;
return (UNVIS_VALID);
*astate = SS(0, S_GROUND);
return UNVIS_VALID;
case 't':
*cp = '\t';
*astate = S_GROUND;
return (UNVIS_VALID);
*astate = SS(0, S_GROUND);
return UNVIS_VALID;
case 'f':
*cp = '\f';
*astate = S_GROUND;
return (UNVIS_VALID);
*astate = SS(0, S_GROUND);
return UNVIS_VALID;
case 's':
*cp = ' ';
*astate = S_GROUND;
return (UNVIS_VALID);
*astate = SS(0, S_GROUND);
return UNVIS_VALID;
case 'E':
*cp = '\033';
*astate = S_GROUND;
return (UNVIS_VALID);
*astate = SS(0, S_GROUND);
return UNVIS_VALID;
case 'x':
*astate = SS(0, S_HEX);
return UNVIS_NOCHAR;
case '\n':
/*
* hidden newline
*/
*astate = S_GROUND;
return (UNVIS_NOCHAR);
*astate = SS(0, S_GROUND);
return UNVIS_NOCHAR;
case '$':
/*
* hidden marker
*/
*astate = S_GROUND;
return (UNVIS_NOCHAR);
*astate = SS(0, S_GROUND);
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:
if (c == '-')
*astate = S_META1;
*astate = SS(0, S_META1);
else if (c == '^')
*astate = S_CTRL;
else {
*astate = S_GROUND;
return (UNVIS_SYNBAD);
}
return (0);
*astate = SS(0, S_CTRL);
else
goto bad;
return UNVIS_NOCHAR;
case S_META1:
*astate = S_GROUND;
*astate = SS(0, S_GROUND);
*cp |= c;
return (UNVIS_VALID);
return UNVIS_VALID;
case S_CTRL:
if (c == '?')
*cp |= 0177;
else
*cp |= c & 037;
*astate = S_GROUND;
return (UNVIS_VALID);
*astate = SS(0, S_GROUND);
return UNVIS_VALID;
case S_OCTAL2: /* second possible octal digit */
if (isoctal(c)) {
if (isoctal(uc)) {
/*
* yes - and maybe a third
*/
*cp = (*cp << 3) + (c - '0');
*astate = S_OCTAL3;
return (0);
*astate = SS(0, S_OCTAL3);
return UNVIS_NOCHAR;
}
/*
* no - done with current sequence, push back passed char
*/
*astate = S_GROUND;
return (UNVIS_VALIDPUSH);
*astate = SS(0, S_GROUND);
return UNVIS_VALIDPUSH;
case S_OCTAL3: /* third possible octal digit */
*astate = S_GROUND;
if (isoctal(c)) {
*astate = SS(0, S_GROUND);
if (isoctal(uc)) {
*cp = (*cp << 3) + (c - '0');
return (UNVIS_VALID);
return UNVIS_VALID;
}
/*
* we were done, push back passed char
*/
return (UNVIS_VALIDPUSH);
return UNVIS_VALIDPUSH;
case S_HEX2: /* second mandatory hex digit */
if (ishex(tolower(c))) {
*cp = (isdigit(c) ? (*cp << 4) + (c - '0') : (*cp << 4) + (tolower(c) - 'a' + 10));
case S_HEX:
if (!isxdigit(uc))
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;
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:
bad:
/*
* decoder in unknown state - (probably uninitialized)
*/
*astate = S_GROUND;
return (UNVIS_SYNBAD);
*astate = SS(0, S_GROUND);
return UNVIS_SYNBAD;
}
}
/*
* strunvis - decode src into dst
* strnunvisx - decode src into dst
*
* Number of chars decoded into dst is returned, -1 on error.
* Dst is null terminated.
*/
int
strunvis(char *dst, const char *src)
strnunvisx(char *dst, size_t dlen, const char *src, int flag)
{
char c;
char *start = dst;
char t = '\0', *start = dst;
int state = 0;
while ((c = *src++)) {
again:
switch (unvis(dst, c, &state, 0)) {
_DIAGASSERT(src != NULL);
_DIAGASSERT(dst != NULL);
#define CHECKSPACE() \
do { \
if (dlen-- == 0) { \
errno = ENOSPC; \
return -1; \
} \
} while (/*CONSTCOND*/0)
while ((c = *src++) != '\0') {
again:
switch (unvis(&t, c, &state, flag)) {
case UNVIS_VALID:
dst++;
CHECKSPACE();
*dst++ = t;
break;
case UNVIS_VALIDPUSH:
dst++;
CHECKSPACE();
*dst++ = t;
goto again;
case 0:
case UNVIS_NOCHAR:
break;
case UNVIS_SYNBAD:
errno = EINVAL;
return -1;
default:
*dst = '\0';
return (-1);
_DIAGASSERT(/*CONSTCOND*/0);
errno = EINVAL;
return -1;
}
}
if (unvis(dst, c, &state, UNVIS_END) == UNVIS_VALID)
dst++;
if (unvis(&t, c, &state, UNVIS_END) == UNVIS_VALID) {
CHECKSPACE();
*dst++ = t;
}
CHECKSPACE();
*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);
return (int)(dst - start);
}
int
strunvisx(char *dst, const char *src, int flag)
{
char c;
char *start = dst;
int state = 0;
while ((c = *src++)) {
again:
switch (unvis(dst, c, &state, flag)) {
case UNVIS_VALID:
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)
dst++;
*dst = '\0';
return (dst - start);
return strnunvisx(dst, (size_t)~0, src, flag);
}
int
strunvis(char *dst, const char *src)
{
return strnunvisx(dst, (size_t)~0, src, 0);
}
int
strnunvis(char *dst, size_t dlen, const char *src)
{
return strnunvisx(dst, dlen, src, 0);
}

876
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
* The Regents of the University of California. All rights reserved.
@ -28,220 +29,717 @@
* SUCH DAMAGE.
*/
#include <sys/types.h>
#include <limits.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <vis.h>
/*-
* Copyright (c) 1999, 2005 The NetBSD Foundation, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* 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) \
(((unsigned char)(c)) >= '0' && ((unsigned char)(c)) <= '7')
#define isvisible(c) \
(((unsigned int)(c) <= UCHAR_MAX && \
isascii((unsigned char)(c)) && \
(((c) != '*' && (c) != '?' && (c) != '[' && (c) != '#') || \
(flag & VIS_GLOB) == 0) && \
isgraph((unsigned char)(c))) || \
((flag & VIS_SP) == 0 && (c) == ' ') || \
((flag & VIS_TAB) == 0 && (c) == '\t') || \
((flag & VIS_NL) == 0 && (c) == '\n') || \
((flag & VIS_SAFE) && ((c) == '\b' || \
(c) == '\007' || (c) == '\r' || \
isgraph((unsigned char)(c)))))
#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/param.h>
#include <assert.h>
#include <vis.h>
#include <errno.h>
#include <stdlib.h>
#include <wchar.h>
#include <wctype.h>
#ifdef __weak_alias
__weak_alias(strvisx,_strvisx)
#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
*/
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) {
/* Described in RFC 1808 */
if (!(isalnum(c) /* alpha-numeric */
/* safe */
|| c == '$' || c == '-' || c == '_' || c == '.' || c == '+'
/* extra */
|| c == '!' || c == '*' || c == '\'' || c == '('
|| c == ')' || c == ',')) {
*dst++ = '%';
snprintf(dst, 4, (c < 16 ? "0%X" : "%X"), c);
dst += 2;
goto done;
}
}
cc[0] = c;
cc[1] = nextc;
if ((flag & VIS_GLOB) &&
(c == '*' || c == '?' || c == '[' || c == '#'))
;
else if (isgraph(c) ||
((flag & VIS_SP) == 0 && c == ' ') ||
((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);
}
ret = istrsenvisx(&mbdst, NULL, cc, 1, flags, "", NULL);
if (ret < 0)
return NULL;
return mbdst + ret;
}
if (flag & VIS_CSTYLE) {
switch(c) {
case '\n':
*dst++ = '\\';
*dst++ = 'n';
goto done;
case '\r':
*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);
char *
nvis(char *mbdst, size_t dlen, int c, int flags, int nextc)
{
char cc[2];
int ret;
cc[0] = c;
cc[1] = nextc;
ret = istrsenvisx(&mbdst, &dlen, cc, 1, flags, "", NULL);
if (ret < 0)
return NULL;
return mbdst + ret;
}
/*
* 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
* 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.
*
* Strnvis will write no more than siz-1 bytes (and will NULL terminate).
* The number of bytes needed to fully encode the string is returned.
*
* Strvisx encodes exactly len bytes from src into dst.
* Strvisx encodes exactly len characters from src into dst.
* 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); )
dst = vis(dst, c, flag, *++src);
*dst = '\0';
return (dst - start);
int
strvisx(char *mbdst, const char *mbsrc, size_t len, int flags)
{
return istrsenvisx(&mbdst, NULL, mbsrc, len, flags, "", NULL);
}
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;
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);
return istrsenvisx(&mbdst, &dlen, mbsrc, len, flags, "", NULL);
}
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;
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);
return istrsenvisx(&mbdst, &dlen, mbsrc, len, flags, "", cerr_ptr);
}