diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc index 49f912ba6f35..37067569d38a 100644 --- a/lib/libc/gen/Makefile.inc +++ b/lib/libc/gen/Makefile.inc @@ -1,4 +1,4 @@ -# $NetBSD: Makefile.inc,v 1.106 2001/02/19 22:14:20 cgd Exp $ +# $NetBSD: Makefile.inc,v 1.107 2001/03/10 01:51:49 cgd Exp $ # from: @(#)Makefile.inc 8.6 (Berkeley) 5/4/95 # gen sources @@ -18,7 +18,7 @@ SRCS+= _errno.c alarm.c assert.c basename.c clock.c closedir.c \ pause.c popen.c psignal.c pwcache.c pw_scan.c raise.c readdir.c \ rewinddir.c scandir.c seekdir.c setdomainname.c \ sethostname.c setjmperr.c setmode.c setproctitle.c setprogname.c \ - siginterrupt.c __siglist14.c siglist.c signal.c \ + shquote.c shquotev.c siginterrupt.c __siglist14.c siglist.c signal.c \ __signame14.c signame.c __sigsetops14.c sigsetops.c sleep.c \ stringlist.c sysconf.c sysctl.c syslog.c telldir.c time.c times.c \ timezone.c toascii.c tolower_.c ttyname.c ttyslot.c toupper_.c \ @@ -48,7 +48,7 @@ MAN+= alarm.3 basename.3 bswap.3 clock.3 confstr.3 ctermid.3 ctype.3 \ ispunct.3 isspace.3 isupper.3 isxdigit.3 ldexp.3 lockf.3 \ modf.3 nice.3 nlist.3 pause.3 popen.3 psignal.3 pwcache.3 \ raise.3 realpath.3 scandir.3 setjmp.3 setmode.3 \ - setproctitle.3 siginterrupt.3 signal.3 sigsetops.3 sleep.3 \ + setproctitle.3 shquote.3 siginterrupt.3 signal.3 sigsetops.3 sleep.3 \ stringlist.3 sysconf.3 sysctl.3 syslog.3 time.3 times.3 \ timezone.3 toascii.3 tolower.3 toupper.3 ttyname.3 \ ualarm.3 ulimit.3 uname.3 unvis.3 usleep.3 utime.3 valloc.3 vis.3 @@ -98,6 +98,7 @@ MLINKS+=scandir.3 alphasort.3 MLINKS+=setjmp.3 _longjmp.3 setjmp.3 _setjmp.3 setjmp.3 longjmp.3 \ setjmp.3 longjmperror.3 setjmp.3 sigsetjmp.3 setjmp.3 siglongjmp.3 MLINKS+=setmode.3 getmode.3 +MLINKS+=shquote.3 shquotev.3 MLINKS+=sigsetops.3 sigemptyset.3 sigsetops.3 sigfillset.3 \ sigsetops.3 sigaddset.3 sigsetops.3 sigdelset.3 \ sigsetops.3 sigismember.3 diff --git a/lib/libc/gen/shquote.3 b/lib/libc/gen/shquote.3 new file mode 100644 index 000000000000..9807624018a4 --- /dev/null +++ b/lib/libc/gen/shquote.3 @@ -0,0 +1,239 @@ +.\" $NetBSD: shquote.3,v 1.1 2001/03/10 01:51:49 cgd Exp $ +.\" +.\" Copyright (c) 2001 Christopher G. Demetriou +.\" 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed for the +.\" NetBSD Project. See http://www.netbsd.org/ for +.\" information about NetBSD. +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +.\" +.\" <> +.\" +.Dd March 1, 2001 +.Dt SHQUOTE 3 +.Os +.Sh NAME +.Nm shquote , +.Nm shquotev +.Nd quote argument strings for use with the shell +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.Fd #include +.Ft size_t +.Fn shquote "const char *arg" "char *buf" "size_t bufsize" +.Ft size_t +.Fn shquotev "int argc" "char * const *argv" "char *buf" "size_t bufsize" +.Sh DESCRIPTION +The +.Fn shquote +and +.Fn shquotev +functions copy strings and transform the copies by adding shell +escape and quoting characters. They are used to encapsulate +arguments to be included in command strings passed to the +.Fn system +and +.Fn popen +functions, so that the arguments will have the correct values +after being evaluated by the shell. +.Pp +The exact method of quoting and escaping may vary, and is intended +to match the conventions of the shell used by +.Fn system +and +.Fn popen . +It may not match the conventions used by other shells. +In this implementation, the following +transformation is applied to each input string: +.Bl -bullet -width indent +.It +it is surrounded by single quotes +.Pq ' , +.It +any single quotes in the input are escaped by replacing them with +the four-character sequence: +.Li '\e'' , +and +.It +extraneous pairs of single quotes (caused by multiple adjacent single +quotes in the input string, or by single quotes at the beginning or +end of the input string) are elided. +.El +.Pp +The +.Fn shquote +function transforms the string specified by its +.Fa arg +argument, and places the result into the memory pointed to by +.Fa buf . +.Pp +The +.Fn shquotev +function transforms each of the +.Fa argc +strings specified by the array +.Fa argv +independently. The transformed strings are +placed in the memory pointed to by +.Fa buf , +separated by spaces. It does not modify the pointer array +specified by +.Fa argv +or the strings pointed to by the pointers in the arrary. +.Pp +Both functions write up to +.Fa bufsize +- 1 characters of output into the buffer pointed to by +.Fa buf , +then add a +.Li NUL +character to terminate the output string. If +.Fa bufsize +is given as zero, the +.Fa buf +parameter is ignored and no output is written. +.Sh RETURN VALUES +The +.Fn shquote +and +.Fn shquotev +functions return the number of characters necessary to hold the +result from operating on their input strings, +not including the terminating +.Li NUL . +That is, they return the length of the string that would have +been written to the output buffer, if it were large enough. +If an error occurs during processing, the value ((size_t)\-1) +is returned and +.Va errno +is set appropriately. +.Sh EXAMPLES +The following code fragment demonstrates how you might use +.Fn shquotev +to construct a command string to be used with +.Fn system . +The command uses an environment variable (which will be expanded by +the shell) to determine the actual program to run. Note that +the environment variable may be expanded by the shell into multiple +words. The first word of the expansion will be used by the shell +as the name of the program to run, +and the rest will be passed as arguments to the program. +.Bd -literal -offset indent +char **argv, c, *cmd; +size_t cmdlen, len, qlen; +int argc; + +\&... + +/* + * Size buffer to hold the command string, and allocate it. + * Buffer of length one given to snprintf() for portability. + */ +cmdlen = snprintf(&c, 1, "${PROG-%s} ", PROG_DEFAULT); +qlen = shquotev(argc, argv, NULL, 0) + 1; +if (qlen == (size_t)-1) { + \&... +} +cmdlen += qlen; +cmd = malloc(cmdlen); +if (cmd == NULL) { + \&... +} + +/* Create the command string. */ +len = snprintf(cmd, cmdlen, "${PROG-%s} ", PROG_DEFAULT); +qlen = shquotev(argc, argv, cmd + len, cmdlen - len); +if (qlen == (size_t)-1) { + /* Should not ever happen. */ + \&... +} +len += qlen; + +/* "cmd" can now be passed to system(). */ +.Ed +.Pp +The following example shows how you would implement the same +functionality using the +.Fn shquote +function directly. +.Bd -literal -offset indent +char **argv, c, *cmd; +size_t cmdlen, len, qlen; +int argc, i; + +\&... + +/* + * Size buffer to hold the command string, and allocate it. + * Buffer of length one given to snprintf() for portability. + */ +cmdlen = snprintf(&c, 1, "${PROG-%s} ", PROG_DEFAULT); +for (i = 0; i < argc; i++) { + qlen = shquote(argv[i], NULL, 0) + 1; + if (qlen == (size_t)-1) { + \&... + } + cmdlen += qlen; +} +cmd = malloc(cmdlen); +if (cmd == NULL) { + \&... +} + +/* Start the command string with the env var reference. */ +len = snprintf(cmd, cmdlen, "${PROG-%s} ", PROG_DEFAULT); + +/* Quote all of the arguments when copying them. */ +for (i = 0; i < argc; i++) { + qlen = shquote(argv[i], cmd + len, cmdlen - len); + if (qlen == (size_t)-1) { + /* Should not ever happen. */ + \&... + } + len += qlen; + cmd[len++] = ' '; +} +cmd[--len] = '\0'; + +/* "cmd" can now be passed to system(). */ +.Ed +.Sh SEE ALSO +.Xr sh 1 , +.Xr popen 3 , +.Xr system 3 +.Sh BUGS +This implementation does not currently handle strings containing multibyte +characters properly. To address this issue, +.Pa /bin/sh +.Po +the shell used by +.Fn system +and +.Fn popen +.Pc +must first be fixed to handle multibyte characters. When that has been +done, these functions can have multibyte character support enabled. diff --git a/lib/libc/gen/shquote.c b/lib/libc/gen/shquote.c new file mode 100644 index 000000000000..a5450ff7f0f6 --- /dev/null +++ b/lib/libc/gen/shquote.c @@ -0,0 +1,181 @@ +/* $NetBSD: shquote.c,v 1.1 2001/03/10 01:51:49 cgd Exp $ */ + +/* + * Copyright (c) 2001 Christopher G. Demetriou + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the + * NetBSD Project. See http://www.netbsd.org/ for + * information about NetBSD. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 SHQUOTE_USE_MULTIBYTE if you want shquote() to handle multibyte + * characters using mbrtowc(). + * + * Please DO NOT rip this #ifdef out of the code. It's also here to help + * portability. + */ +#define SHQUOTE_USE_MULTIBYTE + +#include +#ifdef SHQUOTE_USE_MULTIBYTE +#include +#include +#endif + +/* + * shquote(): + * + * Requotes arguments so that they'll be interpreted properly by the + * shell (/bin/sh). + * + * Wraps single quotes around the string, and replaces single quotes + * in the string with the sequence: + * '\'' + * + * Returns the number of characters required to hold the resulting quoted + * argument. + * + * The buffer supplied is filled in and NUL-terminated. If 'bufsize' + * indicates that the buffer is too short to hold the output string, the + * first (bufsize - 1) bytes of quoted argument are filled in and the + * buffer is NUL-terminated. + * + * Changes could be made to optimize the length of strings output by this + * function: + * + * * if there are no metacharacters or whitespace in the input, + * the output could be the input string. + */ + +#ifdef SHQUOTE_USE_MULTIBYTE + +#define XLATE_OUTCH(x) wcrtomb(outch, (x), &mbso) +#define XLATE_INCH() \ + do { \ + n = mbrtowc(&c, arg, MB_CUR_MAX, &mbsi); \ + } while (0) + +#else + +#define XLATE_OUTCH(x) (outch[0] = (x), 1) +#define XLATE_INCH() \ + do { \ + n = ((c = *arg) != '\0') ? 1 : 0; \ + } while (0) + +#endif + +#define PUT(x) \ + do { \ + outchlen = XLATE_OUTCH(x); \ + if (outchlen == (size_t)-1) \ + goto bad; \ + rv += outchlen; \ + if (bufsize != 0) { \ + if (bufsize < outchlen || \ + (bufsize == outchlen && \ + outch[outchlen - 1] != '\0')) { \ + *buf = '\0'; \ + bufsize = 0; \ + } else { \ + memcpy(buf, outch, outchlen); \ + buf += outchlen; \ + bufsize -= outchlen; \ + } \ + } \ + } while (0) + +size_t +shquote(const char *arg, char *buf, size_t bufsize) +{ +#ifdef SHQUOTE_USE_MULTIBYTE + char outch[MB_CUR_MAX]; + mbstate_t mbsi, mbso; + wchar_t c, lastc; + size_t outchlen; +#else + char outch[1]; + char c, lastc; + size_t outchlen; +#endif + char *end; + size_t rv; + int n; + + rv = 0; + lastc = 0; + end = (bufsize != 0) ? &buf[bufsize - 1] : NULL; +#ifdef SHQUOTE_USE_MULTIBYTE + memset(&mbsi, 0, sizeof mbsi); + memset(&mbso, 0, sizeof mbso); +#endif + + if (*arg != '\'') + PUT('\''); + for (;;) { + XLATE_INCH(); + if (n == (size_t)-1) + goto bad; + if (n <= 0) + break; + arg += n; + lastc = c; + + if (c == '\'') { + if (rv != 0) + PUT('\''); + PUT('\\'); + PUT('\''); + for (;;) { + XLATE_INCH(); + if (n <= 0 || c != '\'') + break; + PUT('\\'); + PUT('\''); + arg += n; + } + if (n > 0) + PUT('\''); + } else + PUT(c); + } + if (lastc != '\'') + PUT('\''); + + /* Put multibyte or NUL terminator, but don't count the NUL. */ + PUT('\0'); + rv--; + + return rv; + +bad: + /* A multibyte character encoding or decoding error occurred. */ + return -1; +} diff --git a/lib/libc/gen/shquotev.c b/lib/libc/gen/shquotev.c new file mode 100644 index 000000000000..5610a060aa53 --- /dev/null +++ b/lib/libc/gen/shquotev.c @@ -0,0 +1,80 @@ +/* $NetBSD: shquotev.c,v 1.1 2001/03/10 01:51:49 cgd Exp $ */ + +/* + * Copyright (c) 2001 Christopher G. Demetriou + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the + * NetBSD Project. See http://www.netbsd.org/ for + * information about NetBSD. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * <> + */ + +#include + +/* + * shquotev(): + * + * Apply shquote() to a set of strings, separating the results by spaces. + */ + +size_t +shquotev(int argc, char * const * argv, char *buf, size_t bufsize) +{ + size_t rv, callrv; + int i; + + rv = 0; + + if (argc == 0) { + if (bufsize != 0) + *buf = '\0'; + return rv; + } + + for (i = 0; i < argc; i++) { + callrv = shquote(argv[i], buf, bufsize); + if (callrv == (size_t)-1) + goto bad; + rv += callrv; + buf += callrv; + bufsize = (bufsize > callrv) ? (bufsize - callrv) : 0; + + if (i < (argc - 1)) { + rv++; + if (bufsize > 1) { + *buf++ = ' '; + bufsize--; + } + } + } + + return rv; + +bad: + return -1; +}