From 0907050e35c9887dcf961bda2111bd34c3eeb0e8 Mon Sep 17 00:00:00 2001 From: Kaz Kylheku Date: Mon, 26 Jun 2023 22:27:58 -0700 Subject: Add function for shell quoting. * README.md: Mention and cppawk-quote man page. * share/cppawk/include/quote.h, * share/cppawk/include/quote-priv.h: New files. * testcases-quote: New file. * cppawk-quote.1: New file. * runtests: Add testcases-quote. --- README.md | 3 + cppawk-quote.1 | 119 ++++++++++++++++++++++++++++++++++++++ runtests | 5 ++ share/cppawk/include/quote-priv.h | 69 ++++++++++++++++++++++ share/cppawk/include/quote.h | 37 ++++++++++++ testcases-quote | 36 ++++++++++++ 6 files changed, 269 insertions(+) create mode 100644 cppawk-quote.1 create mode 100644 share/cppawk/include/quote-priv.h create mode 100644 share/cppawk/include/quote.h create mode 100644 testcases-quote diff --git a/README.md b/README.md index 921b01d..8fa661c 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,9 @@ There are currently * [``](../tree/cppawk-field.1): utilities for manipulating the Awk positional parameters ("fields"). +* [``](../tree/cppawk-quote.1): provides the `q` function for + quoting text for safe insertion into shells cripts. + Several unreleased headers are in the development queue: * ``: some associative array utilities. diff --git a/cppawk-quote.1 b/cppawk-quote.1 new file mode 100644 index 0000000..29af8e6 --- /dev/null +++ b/cppawk-quote.1 @@ -0,0 +1,119 @@ +.\" cppawk: C preprocessor wrapper around awk +.\" Copyright 2023 Kaz Kylheku +.\" +.\" BSD-2 License +.\" +.\" 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. +.de bk +.IP " " +.PP +.. +.TH CPPAWK-QUOTE 1 "26 June 2023" "cppawk Libraries" "Shell Quoting" + +.SH NAME +q \- Function for shell quoting. + +.SH SYNOPSIS + +.ft B + #include + + q(str) \fI// return str, shell-escaped\fP +.ft R + +.SH DESCRIPTION +.bk +The +.B q +function shell-escapes its argument +.IR str . +This means that if the argument contains +special characters, it is transformed such that the resulting string +can safely be inserted into a shell command as an argument. That argument, +when interpreted by the shell, will be interpreted as the original text +which was input to +.BR q . + +If +.I str +contains any of the following special characters, it will be single-quoted +(turned into a string which begins and ends with the single quote, +also known as the ASCII apostrophe character). + +.ft B + \e * ? $ ` ~ +.ft R + +Otherwise, if it contains single quotes +it will be double-quoted (surrounded in double quotes). + +Otherwise, if it contains double quotes, it will be +single-quoted. + +Otherwise, if it contains spaces, tabs or newlines, it will +be double-quoted. + +If none of the above cases apply, +.I str +is returned unmodified. + +If single quoting is applied to a string which contains single quote +characters, those characters are replaced by the sequence +.BR '\e'' . + +If double quoting is applied to a string which contains double quotes, +those characters are replaced by +.BR \e" . + +.B Examples: + +.ft B + // safely execute mv + + function mv(from, to) + { + system("mv -- " q(from) " " q(to)) + } + + // code // output + print q("abc") abc + print q("ab cd") "ab cd" + print q("ab\enc") "ab + c" + print q("ab'cd") "ab'cd" + print q("ab\e"cd") 'ab"cd' + print q("abc*") 'abc*' +.ft R + + +.SH "SEE ALSO" + +cppawk(1) + +.SH BUGS + +.SH AUTHOR +Kaz Kylheku + +.SH COPYRIGHT +Copyright 2023, BSD2 License. diff --git a/runtests b/runtests index 961bc19..91ff3c6 100755 --- a/runtests +++ b/runtests @@ -41,3 +41,8 @@ if [ -z "$suite" -o "$suite" = "array" ] ; then cppawk=./bin/cppawk ./testsuite.awk testcases-array cppawk="./bin/cppawk --awk=mawk" ./testsuite.awk -v skip=5,6 testcases-array fi + +if [ -z "$suite" -o "$suite" = "quote" ] ; then + cppawk=./bin/cppawk ./testsuite.awk testcases-quote + cppawk="./bin/cppawk --awk=mawk" ./testsuite.awk testcases-quote +fi diff --git a/share/cppawk/include/quote-priv.h b/share/cppawk/include/quote-priv.h new file mode 100644 index 0000000..1dd635d --- /dev/null +++ b/share/cppawk/include/quote-priv.h @@ -0,0 +1,69 @@ +// cppawk: C preprocessor wrapper around awk +// Copyright 2023 Kaz Kylheku +// +// BSD-2 License +// +// 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. + +#ifndef __CPPAWK_QUOTE_PRIV_H +#define __CPPAWK_QUOTE_PRIV_H + +function __squote(__str) +{ + gsub("'", "'\\''", __str) + return "'" __str "'" +} + +function __dquote(__str) +{ + gsub("\"", "\\\"", __str) + return "\"" __str "\"" +} + +function __quote(__str, + __dquot_present, + __squot_present, + __meta_present, + __space_present) +{ + __dquot_present = __str ~ "\"" + __squot_present = __str ~ "'" + __meta_present = __str ~ "[\\\\*?$`~]" + __space_present = __str ~ "[\\t\\n ]" + + if (__meta_present) + return __squote(__str) + + if (__squot_present) + return __dquote(__str) + + if (__dquot_present) + return "'" __str "'" + + if (__space_present) + return __dquote(__str) + + return __str +} + +#endif diff --git a/share/cppawk/include/quote.h b/share/cppawk/include/quote.h new file mode 100644 index 0000000..9b91568 --- /dev/null +++ b/share/cppawk/include/quote.h @@ -0,0 +1,37 @@ +// cppawk: C preprocessor wrapper around awk +// Copyright 2023 Kaz Kylheku +// +// BSD-2 License +// +// 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. + +#ifndef __CPPAWK_QUOTE_H +#define __CPPAWK_QUOTE_H + +#ifndef __CPPAWK_QUOTE_PRIV_H +#include "quote-priv.h" +#endif + +#define q(str) __quote(str) + +#endif diff --git a/testcases-quote b/testcases-quote new file mode 100644 index 0000000..272261b --- /dev/null +++ b/testcases-quote @@ -0,0 +1,36 @@ +1: +$cppawk ' +#include + +BEGIN { + print q("") + print q("abc") + print q("abc\\def") + print q("'\''foo'\''") + print q("\"foo\"") + print q("\"fo'\''o\"") + print q("abc\ndef") + print q("abc*def") + print q("abc?def") + print q("abc`def") + print q("abc`de'\''f") + print q("a b c") + print q("a\tb\tc") + print q("~bob/foo") +}' +: + +abc +'abc\def' +"'foo'" +'"foo"' +"\"fo'o\"" +"abc +def" +'abc*def' +'abc?def' +'abc`def' +'abc`de'\''f' +"a b c" +"a b c" +'~bob/foo' -- cgit v1.2.3