1#
2# CDDL HEADER START
3#
4# The contents of this file are subject to the terms of the
5# Common Development and Distribution License (the "License").
6# You may not use this file except in compliance with the License.
7#
8# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9# or http://www.opensolaris.org/os/licensing.
10# See the License for the specific language governing permissions
11# and limitations under the License.
12#
13# When distributing Covered Code, include this CDDL HEADER in each
14# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15# If applicable, add the following below this CDDL HEADER, with the
16# fields enclosed by brackets "[]" replaced with your own identifying
17# information: Portions Copyright [yyyy] [name of copyright owner]
18#
19# CDDL HEADER END
20#
21
22#
23# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24#
25
26#
27# Test whether CR #6766246 ("bug in pattern matching") has been fixed.
28# 
29# Quote from CR #6766246: 
30# ---- snip ----
31# The bootstrap script of pkgsrc contains this code
32# checkarg_sane_absolute_path() {
33#   case "$1" in
34#     "") ;; # the default value will be used.
35#     *[!-A-Za-z0-9_./]*)
36#       die "ERROR: Invalid characters in path $1 (from $2)." ;;
37#     /*) ;;
38#     *) die "ERROR: The argument to $2 must be an absolute path." ;;
39#   esac
40# }
41# It turns out, the leading "!" in the pattern is not interpreted
42# as negation, and the first "-" not as a literal. Instead the
43# character range  "! to A" is constructed. Paths containing "%"
44# or "@" are accepted, but paths containing "-" are rejected.
45# Note that this interpretation makes the whole pattern
46# syntactically wrong, which isn't noticed either.
47# 
48# Test case:
49# -- snip --
50# !/bin/sh
51# case "$1" in
52# *[!-A-Za-z0-9_./]*)
53#         echo invalid characters used in $1
54#         ;;
55# *)
56#         echo only valid characters used in $1
57#         ;;
58# esac
59# -- snip --
60# Expected Result:
61#    strings containing a "-" should be accepted, strings containing
62#    a "@" should be rejected
63# Actual Result:
64#    strings containing a "-" are rejected, strings containing a
65#    "@" are accepted
66# Workaround
67#    The pattern "*[!A-Za-z0-9_./-]*" (i.e. shifting the dash to
68#    the end) works as expected.
69# ---- snip ----
70
71
72# test setup
73function err_exit
74{
75	print -u2 -n "\t"
76	print -u2 -r ${Command}[$1]: "${@:2}"
77	(( Errors < 127 && Errors++ ))
78}
79alias err_exit='err_exit $LINENO'
80
81set -o nounset
82Command=${0##*/}
83integer Errors=0
84
85
86## test 1 (based on the bug report):
87
88function do_match
89{
90	case "$1" in
91		*[!-A-Za-z0-9_./]*)
92			print "match"
93			;;
94		*)
95			print "nomatch"
96			;;
97	esac
98	return 0
99}
100
101typeset pat
102
103pat="foo-bar" ; [[ "$(do_match "${pat}")" == "nomatch" ]] || err_exit "${pat} matched."
104pat="foo+bar" ; [[ "$(do_match "${pat}")" == "match"   ]] || err_exit "${pat} not matched."
105pat="foo/bar" ; [[ "$(do_match "${pat}")" == "nomatch" ]] || err_exit "${pat} matched."
106pat="foo_bar" ; [[ "$(do_match "${pat}")" == "nomatch" ]] || err_exit "${pat} matched."
107pat="foo@bar" ; [[ "$(do_match "${pat}")" == "match"   ]] || err_exit "${pat} not matched."
108pat="foobar-" ; [[ "$(do_match "${pat}")" == "nomatch" ]] || err_exit "${pat} matched."
109pat="foobar+" ; [[ "$(do_match "${pat}")" == "match"   ]] || err_exit "${pat} not matched."
110pat="foobar/" ; [[ "$(do_match "${pat}")" == "nomatch" ]] || err_exit "${pat} matched."
111pat="foobar_" ; [[ "$(do_match "${pat}")" == "nomatch" ]] || err_exit "${pat} matched."
112pat="foobar@" ; [[ "$(do_match "${pat}")" == "match"   ]] || err_exit "${pat} not matched."
113pat="-foobar" ; [[ "$(do_match "${pat}")" == "nomatch" ]] || err_exit "${pat} matched."
114pat="+foobar" ; [[ "$(do_match "${pat}")" == "match"   ]] || err_exit "${pat} not matched."
115pat="/foobar" ; [[ "$(do_match "${pat}")" == "nomatch" ]] || err_exit "${pat} matched."
116pat="_foobar" ; [[ "$(do_match "${pat}")" == "nomatch" ]] || err_exit "${pat} matched."
117pat="@foobar" ; [[ "$(do_match "${pat}")" == "match"   ]] || err_exit "${pat} not matched."
118
119
120## test 2 (gsf's test chain):
121
122# Make sure LC_COLLATE has a value
123if [[ "${LC_COLLATE}" == "" ]] ; then
124	if [[ ${LANG} != "" && "${LC_ALL}" == "" ]] ; then
125		LC_COLLATE="${LANG}"
126	fi
127fi
128
129if [[ "${LC_ALL}" != "" ]] ; then
130	LC_COLLATE="${LC_ALL}"
131fi
132
133[[ "${LC_COLLATE}" != "" ]] || err_exit "LC_COLLATE empty."
134
135set -- \
136        'A'   0 1 1   0 1 1      1 0 0   1 0 0   \
137        'Z'   0 1 1   0 1 1      1 0 0   1 0 0   \
138        '/'   0 0 0   0 0 0      1 1 1   1 1 1   \
139        '.'   0 0 0   0 0 0      1 1 1   1 1 1   \
140        '_'   0 0 0   0 0 0      1 1 1   1 1 1   \
141        '-'   1 1 1   1 1 1      0 0 0   0 0 0   \
142        '%'   0 0 0   0 0 0      1 1 1   1 1 1   \
143        '@'   0 0 0   0 0 0      1 1 1   1 1 1   \
144        '!'   0 0 0   0 0 0      1 1 1   1 1 1   \
145        '^'   0 0 0   0 0 0      1 1 1   1 1 1   \
146        # retain this line #
147while (( $# >= 13 )) ; do
148	c=$1
149	shift
150	for p in \
151		'[![.-.]]' \
152		'[![.-.][:upper:]]' \
153		'[![.-.]A-Z]' \
154		'[!-]' \
155		'[!-[:upper:]]' \
156		'[!-A-Z]' \
157		'[[.-.]]' \
158		'[[.-.][:upper:]]' \
159		'[[.-.]A-Z]' \
160		'[-]' \
161		'[-[:upper:]]' \
162		'[-A-Z]' \
163		# retain this line #
164	do      e=$1
165		shift
166		[[ $c == $p ]]
167		g=$?
168		[[ $g == $e ]] || err_exit "[[ '$c' == $p ]] for LC_COLLATE=$l failed -- expected $e, got $g"
169	done
170done
171
172
173# tests done
174exit $((Errors))
175