halt="use 'if test' instead of 'if ['" \
$(_sc_search_regexp)
+# : ${foo=`bar`} is not perfectly portable (see Shellology in autoconf's manual)
+exclude_file_name_regexp--sc_prohibit_command_in_subst = ^cfg.mk$$
+sc_prohibit_command_in_subst:
+ @prohibit='\$$\{[^`}]*`[^`]*`[^}]*}' \
+ halt='do not use `command` in $${ } substitution`' \
+ $(_sc_search_regexp)
+
# Check for quotes within backquotes within quotes "`"bar"`"
exclude_file_name_regexp--sc_prohibit_nested_quotes = ^cfg.mk$$
sc_prohibit_nested_quotes:
fi || :;
endef
-exclude_file_name_regexp--sc_useless_braces_in_variable_derefs = /cvsu$$
+exclude_file_name_regexp--sc_useless_braces_in_variable_derefs = \
+ test-funclib-quote.sh$$
sc_useless_braces_in_variable_derefs:
@prohibit='\$${[0-9A-Za-z_]+}[^0-9A-Za-z_]' \
halt='found spurious braces around variable dereference' \
$(_sc_search_regexp)
# List syntax-check exempted files.
-exclude_file_name_regexp--sc_error_message_uppercase = \
- ^$(_build-aux)/cvsu$$
exclude_file_name_regexp--sc_prohibit_strcmp = \
^doc/libtool.texi$$
exclude_file_name_regexp--sc_prohibit_test_minus_ao = \
^m4/libtool.m4$$
-exclude_file_name_regexp--sc_space_tab = \.diff$$
+exclude_file_name_regexp--sc_space_tab = (\.diff|test-funclib-quote.sh)$$
exclude_file_name_regexp--sc_trailing_blank-non-rfc3676 = \.diff$$
--- /dev/null
+#! /bin/sh
+
+# Unit test helper.
+#
+# Copyright (C) 2015 Free Software Foundation, Inc.
+# This file is part of the GNUlib Library.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+
+# This shell snippet (when sourced) tries to find as many /bin/sh compatible
+# shells as possible on tested box -- and then re-executes the calling script
+# in all of them. At the end, the default shell (/bin/sh usually) is also
+# tested.
+#
+# To write compatible test-case, you usually need only those lines:
+#
+# #! /bin/sh
+#
+# all_shells_script=$0
+# . "$abs_srcdir/test-all-shells.sh"
+#
+# your_check && all_shells_error "failed your_check"
+#
+# $all_shells_exit_cmd
+
+
+# List of shells we try to check in.
+: ${GL_ALL_SHELLS='ash bash dash ksh zsh busybox'}
+
+# List of directories to search for the shell interpreter.
+: ${GL_ALL_SHELLS_DIRS='/bin /sbin'}
+
+# List of shell emulations to test with BusyBox.
+: ${GL_ALL_SHELLS_BBE='sh ash'}
+
+. "$abs_aux_dir"/funclib.sh || exit 1
+
+all_shells_exit_cmd=:
+
+: ${all_shells_script=false}
+
+
+all_shells_error ()
+{
+ $ECHO "error: $*" >&2
+ all_shells_exit_cmd=false
+}
+
+
+__notify_shell ()
+{
+ $ECHO "== running in '$*' ==" >&2
+}
+
+
+__reexec_in_all_shells ()
+{
+ for _G_dir in $GL_ALL_SHELLS_DIRS
+ do
+ for _G_shell in $GL_ALL_SHELLS
+ do
+ _G_abs_shell=$_G_dir/$_G_shell
+ test -f "$_G_abs_shell" || continue
+
+ case $_G_abs_shell in
+ *busybox)
+ for _G_bbe in $GL_ALL_SHELLS_BBE
+ do
+ _G_full_bb="$_G_abs_shell $_G_bbe"
+ __notify_shell "$_G_full_bb"
+ __GL_ALL_SHELLS_SHELL="$_G_full_bb" \
+ "$_G_abs_shell" "$_G_bbe" "$all_shells_script" ${1+"$@"} \
+ || all_shells_error "can't run in $_G_bbe"
+ done
+ ;;
+ *)
+ __notify_shell "$_G_abs_shell"
+ __GL_ALL_SHELLS_SHELL="$_G_abs_shell" \
+ "$_G_abs_shell" "$all_shells_script" ${1+"$@"} \
+ || all_shells_error "can't run in $_G_abs_shell"
+ ;;
+ esac
+ done
+ done
+}
+
+
+test x = "x$__GL_ALL_SHELLS_SHELL" \
+ && __reexec_in_all_shells ${1+"$@"} \
+ && __notify_shell "default shell"
+
+:
--- /dev/null
+#! /bin/sh
+
+# Unit tests for funclib.sh
+#
+# Copyright (C) 2015 Free Software Foundation, Inc.
+# This file is part of the GNUlib Library.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+
+all_shells_script=$0
+. "$abs_srcdir/test-all-shells.sh" || exit 1
+
+
+_compare_or_error ()
+{
+ _G_msg="strings differ:
+ a: $2
+ b: $3"
+ test "$2" = "$3" || all_shells_error "$_G_msg"
+}
+
+
+dump_args ()
+{
+ dump_args_result=
+ _separator=
+ for arg
+ do
+ func_append dump_args_result "$_separator$arg"
+ _separator=' '
+ done
+}
+
+
+# test_q_eval DESC ARGs...
+# ------------------------
+# Apply 'func_quote eval' on ARGs and eval the result. The eval-ed result must
+# 100% match the original ARGs.
+test_q_eval ()
+{
+ description="$1" ; shift
+
+ dump_args ${1+"$@"}
+ original=$dump_args_result
+
+ $ECHO "~> func_quote eval: $description: $original"
+
+ # Pretty implementation
+ func_quote eval,pretty ${1+"$@"} \
+ || all_shells_error "can't pretty func_quote args '$*'"
+ eval "set dummy $func_quote_result" ; shift
+ dump_args ${1+"$@"}
+ pretty=$dump_args_result
+
+ # Fast implementation (if available)
+ func_quote eval ${1+"$@"} || all_shells_error "can't fast func_quote args '$*'"
+ eval "set dummy $func_quote_result" ; shift
+ dump_args ${1+"$@"}
+ fast=$dump_args_result
+
+ _compare_or_error "$description (pretty)" "$original" "$pretty"
+ _compare_or_error "$description (fast)" "$original" "$fast"
+}
+
+
+# test_q_arg_eval DESC ARG
+# ------------------------
+# Apply 'func_quote_arg eval' on ARG and eval the result. Echo-ed result within
+# eval must match original echo-ed ARG.
+test_q_arg_eval ()
+{
+ description=$1
+ original=$2
+ original_echo=`$ECHO "$original"`
+
+ $ECHO "~> func_quote_arg eval: $description: $original_echo"
+
+ func_quote_arg pretty,unquoted "$original" \
+ || all_shells_error "can't quote_arg: $original"
+ pretty=$func_quote_arg_result
+ pretty_unquoted=$func_quote_arg_unquoted_result
+ pretty_echo=`eval '$ECHO '"$pretty"` \
+ || all_shells_error "can't eval: $pretty"
+
+ pretty_unquoted_echo=`eval '$ECHO '"\"$pretty_unquoted\""` \
+ || all_shells_error "can't eval: $pretty"
+
+ # Fast implementation.
+ func_quote_arg eval "$original"
+ fast=$func_quote_arg_result
+ fast_echo=`eval '$ECHO '"$fast"` || all_shells_error "can't eval: $pretty"
+
+ _compare_or_error "$description (pretty)" \
+ "$original_echo" "$pretty_echo"
+ _compare_or_error "$description (pretty_unquoted)" \
+ "$original_echo" "$pretty_unquoted_echo"
+ _compare_or_error "$description (fast)" \
+ "$original_echo" "$fast_echo"
+}
+
+
+# test_q_expand DESC EXP_RESULT ARG
+# ---------------------------------
+# Test that 'func_quote expand' works fine --> all shell special characters are
+# quoted except '$' -- while all variables are expanded.
+test_q_expand ()
+{
+ description=$1 ; shift
+ exp_result=$1 ; shift
+
+ dump_args ${1+"$@"}
+ $ECHO "~> func_quote expand: $description: $dump_args_result"
+
+ func_quote expand ${1+"$@"}
+ eval "set dummy $func_quote_result" ; shift
+ dump_args ${1+"$@"}
+
+ _compare_or_error "$description (expand)" "$exp_result" "$dump_args_result"
+}
+
+
+## ============== ##
+## Start testing! ##
+## ============== ##
+
+aaa=aaa ; bbb=bbb ; ccc=ccc ; ddd=ddd
+
+# Needed for later testing of globbing.
+touch fltestA fltestB
+
+test_q_eval basic a b c
+# TODO: Intentionally not checking newline here yet, it never worked.
+test_q_eval spaces 'space space' 'tab tab'
+test_q_eval empty_arg '' '' ''
+test_q_eval globs '*' '.*' '[a-zA-Z0-9_]' '?' '~'
+test_q_eval variables '$aaa' '${bbb}' '"${ccc} $ddd"'
+test_q_eval exclamation-mark '$!' '!$' '!'
+test_q_eval tilde '"~"'
+test_q_eval single-quotes "'a'" "'"'$bbb'"'"
+test_q_eval shell-vars '$1' '$@' '$*' 'ending$'
+test_q_eval complicated-cmd grep b '>' /noperm '<' /noperm
+
+test_q_arg_eval basic a
+test_q_arg_eval single-quotes "'''"
+test_q_arg_eval double-quotes '"""'
+test_q_arg_eval tilde '~'
+test_q_arg_eval ampersand '&'
+test_q_arg_eval pipe '|'
+test_q_arg_eval questionmark 'fltest?'
+test_q_arg_eval glob-bracket 'fltest[A-Z]'
+test_q_arg_eval space 'space space'
+test_q_arg_eval tab 'tab tab'
+test_q_arg_eval '`command`' '`false command`'
+test_q_arg_eval '$(command)' '$(false command)'
+test_q_arg_eval semicolon '; false'
+test_q_arg_eval vars '$aaa ${bbb} "${ccc} $ddd"'
+test_q_arg_eval if-then-else 'if false; then false; else false; fi'
+test_q_arg_eval file-redirect 'echo a > /no-perm 2> /no-perm'
+test_q_arg_eval case-stmt 'case $empty in "") false;; a) broken ;; esac'
+test_q_arg_eval comment 'unexistent #'
+test_q_arg_eval func 'func () { } # syntax error'
+
+test_q_expand basic 'a b c aaa d' a b c '$aaa' d
+test_q_expand double-quotes '" " " "bbb"' '"' '" "' '"$bbb"'
+test_q_expand spaces ' ccc' ' ' ' ' '${ccc}'
+# Note the *no expected space* here!
+test_q_expand non-existent '' '$empty' '${empty}'
+test_q_expand non-existent-2 '"" ""' '"$empty"' '"${empty}"'
+# TODO: Intentionally not checking '$(cmd)' yet.
+test_q_expand '`command`' '`aaa bbb`' '`$aaa $empty${bbb}`'
+
+$all_shells_exit_cmd && rm -rf fltest*