From: Akim Demaille Date: Wed, 29 Nov 2000 15:52:02 +0000 (+0000) Subject: QNX 4.2.5's expr always exits 1 when `:' is used with parens. X-Git-Tag: autoconf-2.50~381 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9f5b35eb49acd5da3f7ec7ae3f0a6686816bc8d5;p=thirdparty%2Fautoconf.git QNX 4.2.5's expr always exits 1 when `:' is used with parens. * doc/autoconf.texi (Limitations of Usual Tools) : More information, thanks to Paul Berrevoets, Paul Eggert and David Morgan. * sh.m4 (_AS_EXPR_PREPARE): New. (AS_DIRNAME): Use it. --- diff --git a/ChangeLog b/ChangeLog index c29f0a691..6be75c1cb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2000-11-29 Akim Demaille + + QNX 4.2.5's expr always exits 1 when `:' is used with parens. + + * doc/autoconf.texi (Limitations of Usual Tools) : More + information, thanks to Paul Berrevoets, Paul Eggert and David + Morgan. + * sh.m4 (_AS_EXPR_PREPARE): New. + (AS_DIRNAME): Use it. + 2000-11-29 Akim Demaille sizeof (struct {char a,b; }) is not required to be 2. diff --git a/THANKS b/THANKS index 77bc23258..6696d3c25 100644 --- a/THANKS +++ b/THANKS @@ -28,6 +28,7 @@ Chris Torek torek@bsdi.com Cort Dougan cort@cs.nmt.edu Dave Adams adams@hpesdwa.fc.hp.com Dave Love fx@gnu.org +David Morgan dmorgan@symark.com Didier Desseaux didess@infonie.fr Didier Verna didier@xemacs.org Dietmar P. Schindler schd@mra.man.de @@ -98,6 +99,7 @@ Noah Elliott elliott@hera.llnl.gov Noah Friedman friedman@gnu.ai.mit.edu Ossama Othman ossama@debian.org Patrick Tullmann tullmann@cs.utah.edu +Paul Berrevoets paul@swi.com Paul Eggert eggert@twinsun.com Paul Gampe paulg@apnic.net Paul Martinolich martinol@datasync.com diff --git a/doc/autoconf.texi b/doc/autoconf.texi index 6fea9c131..3b0e96b8c 100644 --- a/doc/autoconf.texi +++ b/doc/autoconf.texi @@ -5825,36 +5825,57 @@ foo @item @command{expr} @cindex @command{expr} No @command{expr} keyword starts with @samp{x}, so use @samp{expr -x"@var{word}" : '@var{regex}'} to keep @command{expr} from +x"@var{word}" : 'x@var{regex}'} to keep @command{expr} from misinterpreting @var{word}. -You can use @samp{|}. There is one portability problem occuring when -you @samp{|} together the empty string (or zero) with the empty string. -For example: +Don't use @code{length}, @code{substr}, @code{match} and @code{index}. + +@item @command{expr} (@samp{|}) +@cindex @command{expr} (@samp{|}) +You can use @samp{|}. Although @sc{posix} does require that @samp{expr +''} return the empty string, it does not specify the result when you +@samp{|} together the empty string (or zero) with the empty string. For +example: @example expr '' \| '' @end example -@sc{gnu}/Linux and @sc{posix.2} return the empty string for this case, -but traditional Unix returns @samp{0}. In the latest @sc{posix} draft, -the specification has been changed to match traditional Unix's behavior -(which is bizarre, but it's too late to fix this). +@sc{gnu}/Linux and @sc{posix.2-1992} return the empty string for this +case, but traditional Unix returns @samp{0} (Solaris is one such +example). In the latest @sc{posix} draft, the specification has been +changed to match traditional Unix's behavior (which is bizarre, but it's +too late to fix this). Please note that the same problem does arise +when the empty string results from a computation, as in: -Avoid this portability problem by avoiding the empty string. +@example +expr bar : foo \| foo : bar +@end example -Don't use @code{length}, @code{substr}, @code{match} and @code{index}. +@noindent +Avoid this portability problem by avoiding the empty string. @item @command{expr} (@samp{:}) @cindex @command{expr} Don't use @samp{\?}, @samp{\+} and @samp{\|} in patterns, they are not supported on Solaris. -The @sc{posix}.2-1996 standard is ambiguous as to whether @samp{expr 'a' -: '\(b\)'} outputs @samp{0} or the empty string. In practice, it -outputs the empty string on most platforms, but portable scripts should -not assume this. For instance, the @sc{qnx} 4.2.5 native @command{expr} -returns @samp{0}. +The @sc{posix.2-1992} standard is ambiguous as to whether @samp{expr a : +b} (and @samp{expr 'a' : '\(b\)'}) output @samp{0} or the empty string. +In practice, it outputs the empty string on most platforms, but portable +scripts should not assume this. For instance, the @sc{qnx} 4.2.5 native +@command{expr} returns @samp{0}. + +You may believe that one means to get a uniform behavior would be to use +the empty string as a default value: + +@example +expr a : b \| '' +@end example + +@noindent +unfortunately this behaves exactly as the original expression, see the +@samp{@command{expr} (@samp{:})} entry for more information. Older @command{expr} implementations (e.g. SunOS 4 @command{expr} and Solaris 8 @command{/usr/ucb/expr}) have a silly length limit that causes @@ -5862,6 +5883,38 @@ Solaris 8 @command{/usr/ucb/expr}) have a silly length limit that causes bytes. In this case, you might want to fall back on @samp{echo|sed} if @command{expr} fails. +Don't leave, there is some more! + +The @sc{qnx} 4.2.5 @command{expr}, in addition of preferring @samp{0} to +the empty string, has a funny behavior wrt exit status: it's always 1 +when the parenthesis are used! + +@example +$ val=`expr 'a' : 'a'`; echo "$?: $val" +0: 1 +$ val=`expr 'a' : 'b'`; echo "$?: $val" +1: 0 + +$ val=`expr 'a' : '\(a\)'`; echo "?: $val" +1: a +$ val=`expr 'a' : '\(b\)'`; echo "?: $val" +1: 0 +@end example + +@noindent +In practice this can be a big problem if you are ready to catch failures +of @command{expr} programs with some other method (such as using +@command{sed}), since you may get twice the result. For instance + +@example +$ expr 'a' : '\(a\)' || echo 'a' | sed 's/^\(a\)$/\1/' +@end example + +@noindent +will output @samp{a} on most hosts, but @samp{aa} on @sc{qnx} 4.2.5. A +simple work around consists in testing @command{expr} and use a variable +set to @command{expr} or to @command{false} according to the result. + @item @command{grep} @cindex @command{grep} diff --git a/lib/m4sugar/m4sh.m4 b/lib/m4sugar/m4sh.m4 index 7fb820021..be72f6c58 100644 --- a/lib/m4sugar/m4sh.m4 +++ b/lib/m4sugar/m4sh.m4 @@ -66,6 +66,7 @@ elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then set -o posix fi +_AS_EXPR_PREPARE _AS_UNSET_PREPARE # NLS nuisances. @@ -95,6 +96,23 @@ AS_UNSET([CDPATH], [:]) # This section is lexicographically sorted. +# AS_EXIT([EXIT-CODE = 1]) +# ------------------------ +# Exit and set exit code to EXIT-CODE in the way that it's seen +# within "trap 0". +# +# We cannot simply use "exit N" because some shells (zsh and Solaris sh) +# will not set $? to N while running the code set by "trap 0" +# So we set $? by executing "exit N" in the subshell and then exit. +# "false" is used for exit code 1 (default), ":" is used for 0 +m4_define([AS_EXIT], +[{ m4_case([$1], + [0], [:; exit], + [], [false; exit], + [1], [false; exit], + [(exit $1); exit]); }]) + + # AS_IFELSE(TEST, [IF-TRUE], [IF-FALSE]) # -------------------------------------- # Expand into @@ -138,21 +156,6 @@ m4_defun([AS_UNSET], $as_unset $1 || test "${$1+set}" != set || { $1=$2; export $1; }]) -# AS_EXIT([EXIT-CODE = 1]) -# ------------------------ -# Exit and set exit code to EXIT-CODE in the way that it's seen -# within "trap 0". -# -# We cannot simply use "exit N" because some shells (zsh and Solaris sh) -# will not set $? to N while running the code set by "trap 0" -# So we set $? by executing "exit N" in the subshell and then exit. -# "false" is used for exit code 1 (default), ":" is used for 0 -m4_define([AS_EXIT], -[{ m4_case([$1], - [0], [:; exit], - [], [false; exit], - [1], [false; exit], - [(exit $1); exit]); }]) @@ -242,29 +245,13 @@ m4_define([AS_ERROR], -## ------------------------------------------- ## -## 4. Portable versions of common file utils. ## -## ------------------------------------------- ## +## -------------------------------------- ## +## 4. Portable versions of common tools. ## +## -------------------------------------- ## # This section is lexicographically sorted. -# AS_MKDIR_P(PATH) -# ---------------- -# Emulate `mkdir -p' with plain `mkdir'. -m4_define([AS_MKDIR_P], -[{ case $1 in - [[\\/]]* | ?:[[\\/]]* ) ac_incr_dir=;; - *) ac_incr_dir=.;; -esac -ac_dummy=$1 -for ac_mkdir_dir in `IFS=/; set X $ac_dummy; shift; echo "$[@]"`; do - ac_incr_dir=$ac_incr_dir/$ac_mkdir_dir - test -d $ac_incr_dir || mkdir $ac_incr_dir -done; } -])# AS_MKDIR_P - - # AS_DIRNAME(PATHNAME) # -------------------- # Simulate running `dirname(1)' on PATHNAME, not all systems have it. @@ -276,14 +263,20 @@ done; } # a silly length limit that causes expr to fail if the matched # substring is longer than 120 bytes. So fall back on echo|sed if # expr fails. -m4_define([AS_DIRNAME_EXPR], -[expr X[]$1 : 'X\(.*[[^/]]\)//*[[^/][^/]]*/*$' \| \ - X[]$1 : 'X\(//\)[[^/]]' \| \ - X[]$1 : 'X\(//\)$' \| \ - X[]$1 : 'X\(/\)' \| \ - . : '\(.\)']) - -m4_define([AS_DIRNAME_SED], +# +# FIXME: Please note the following m4_require is quite wrong: if the first +# occurrence of AS_DIRNAME_EXPR is in a backquoted expression, the +# shell will be lost. We might have to introduce diversions for +# setting up an M4sh script: required macros will them be expanded there. +m4_defun([AS_DIRNAME_EXPR], +[m4_require([_AS_EXPR_PREPARE])dnl +$as_expr X[]$1 : 'X\(.*[[^/]]\)//*[[^/][^/]]*/*$' \| \ + X[]$1 : 'X\(//\)[[^/]]' \| \ + X[]$1 : 'X\(//\)$' \| \ + X[]$1 : 'X\(/\)' \| \ + . : '\(.\)']) + +m4_defun([AS_DIRNAME_SED], [echo X[]$1 | sed ['/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } @@ -291,11 +284,41 @@ m4_define([AS_DIRNAME_SED], /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q']]) -m4_define([AS_DIRNAME], +m4_defun([AS_DIRNAME], [AS_DIRNAME_EXPR([$1]) 2>/dev/null || AS_DIRNAME_SED([$1])]) +# _AS_EXPR_PREPARE +# ---------------- +# Some expr work properly (i.e. compute and issue the right result), +# but exit with failure. When a fall back to expr (as in AS_DIRNAME) +# is provided, you get twice the result. Prevent this. +m4_defun([_AS_EXPR_PREPARE], +[as_expr=`expr a : '\(a\)'` +case $as_expr,$? in + a,0) as_expr=expr;; + *) as_expr=false;; +esac[]dnl +])# _AS_EXPR_PREPARE + + +# AS_MKDIR_P(PATH) +# ---------------- +# Emulate `mkdir -p' with plain `mkdir'. +m4_define([AS_MKDIR_P], +[{ case $1 in + [[\\/]]* | ?:[[\\/]]* ) ac_incr_dir=;; + *) ac_incr_dir=.;; +esac +ac_dummy=$1 +for ac_mkdir_dir in `IFS=/; set X $ac_dummy; shift; echo "$[@]"`; do + ac_incr_dir=$ac_incr_dir/$ac_mkdir_dir + test -d $ac_incr_dir || mkdir $ac_incr_dir +done; } +])# AS_MKDIR_P + + ## ------------------ ## ## 5. Common idioms. ## diff --git a/m4sh.m4 b/m4sh.m4 index 7fb820021..be72f6c58 100644 --- a/m4sh.m4 +++ b/m4sh.m4 @@ -66,6 +66,7 @@ elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then set -o posix fi +_AS_EXPR_PREPARE _AS_UNSET_PREPARE # NLS nuisances. @@ -95,6 +96,23 @@ AS_UNSET([CDPATH], [:]) # This section is lexicographically sorted. +# AS_EXIT([EXIT-CODE = 1]) +# ------------------------ +# Exit and set exit code to EXIT-CODE in the way that it's seen +# within "trap 0". +# +# We cannot simply use "exit N" because some shells (zsh and Solaris sh) +# will not set $? to N while running the code set by "trap 0" +# So we set $? by executing "exit N" in the subshell and then exit. +# "false" is used for exit code 1 (default), ":" is used for 0 +m4_define([AS_EXIT], +[{ m4_case([$1], + [0], [:; exit], + [], [false; exit], + [1], [false; exit], + [(exit $1); exit]); }]) + + # AS_IFELSE(TEST, [IF-TRUE], [IF-FALSE]) # -------------------------------------- # Expand into @@ -138,21 +156,6 @@ m4_defun([AS_UNSET], $as_unset $1 || test "${$1+set}" != set || { $1=$2; export $1; }]) -# AS_EXIT([EXIT-CODE = 1]) -# ------------------------ -# Exit and set exit code to EXIT-CODE in the way that it's seen -# within "trap 0". -# -# We cannot simply use "exit N" because some shells (zsh and Solaris sh) -# will not set $? to N while running the code set by "trap 0" -# So we set $? by executing "exit N" in the subshell and then exit. -# "false" is used for exit code 1 (default), ":" is used for 0 -m4_define([AS_EXIT], -[{ m4_case([$1], - [0], [:; exit], - [], [false; exit], - [1], [false; exit], - [(exit $1); exit]); }]) @@ -242,29 +245,13 @@ m4_define([AS_ERROR], -## ------------------------------------------- ## -## 4. Portable versions of common file utils. ## -## ------------------------------------------- ## +## -------------------------------------- ## +## 4. Portable versions of common tools. ## +## -------------------------------------- ## # This section is lexicographically sorted. -# AS_MKDIR_P(PATH) -# ---------------- -# Emulate `mkdir -p' with plain `mkdir'. -m4_define([AS_MKDIR_P], -[{ case $1 in - [[\\/]]* | ?:[[\\/]]* ) ac_incr_dir=;; - *) ac_incr_dir=.;; -esac -ac_dummy=$1 -for ac_mkdir_dir in `IFS=/; set X $ac_dummy; shift; echo "$[@]"`; do - ac_incr_dir=$ac_incr_dir/$ac_mkdir_dir - test -d $ac_incr_dir || mkdir $ac_incr_dir -done; } -])# AS_MKDIR_P - - # AS_DIRNAME(PATHNAME) # -------------------- # Simulate running `dirname(1)' on PATHNAME, not all systems have it. @@ -276,14 +263,20 @@ done; } # a silly length limit that causes expr to fail if the matched # substring is longer than 120 bytes. So fall back on echo|sed if # expr fails. -m4_define([AS_DIRNAME_EXPR], -[expr X[]$1 : 'X\(.*[[^/]]\)//*[[^/][^/]]*/*$' \| \ - X[]$1 : 'X\(//\)[[^/]]' \| \ - X[]$1 : 'X\(//\)$' \| \ - X[]$1 : 'X\(/\)' \| \ - . : '\(.\)']) - -m4_define([AS_DIRNAME_SED], +# +# FIXME: Please note the following m4_require is quite wrong: if the first +# occurrence of AS_DIRNAME_EXPR is in a backquoted expression, the +# shell will be lost. We might have to introduce diversions for +# setting up an M4sh script: required macros will them be expanded there. +m4_defun([AS_DIRNAME_EXPR], +[m4_require([_AS_EXPR_PREPARE])dnl +$as_expr X[]$1 : 'X\(.*[[^/]]\)//*[[^/][^/]]*/*$' \| \ + X[]$1 : 'X\(//\)[[^/]]' \| \ + X[]$1 : 'X\(//\)$' \| \ + X[]$1 : 'X\(/\)' \| \ + . : '\(.\)']) + +m4_defun([AS_DIRNAME_SED], [echo X[]$1 | sed ['/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } @@ -291,11 +284,41 @@ m4_define([AS_DIRNAME_SED], /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q']]) -m4_define([AS_DIRNAME], +m4_defun([AS_DIRNAME], [AS_DIRNAME_EXPR([$1]) 2>/dev/null || AS_DIRNAME_SED([$1])]) +# _AS_EXPR_PREPARE +# ---------------- +# Some expr work properly (i.e. compute and issue the right result), +# but exit with failure. When a fall back to expr (as in AS_DIRNAME) +# is provided, you get twice the result. Prevent this. +m4_defun([_AS_EXPR_PREPARE], +[as_expr=`expr a : '\(a\)'` +case $as_expr,$? in + a,0) as_expr=expr;; + *) as_expr=false;; +esac[]dnl +])# _AS_EXPR_PREPARE + + +# AS_MKDIR_P(PATH) +# ---------------- +# Emulate `mkdir -p' with plain `mkdir'. +m4_define([AS_MKDIR_P], +[{ case $1 in + [[\\/]]* | ?:[[\\/]]* ) ac_incr_dir=;; + *) ac_incr_dir=.;; +esac +ac_dummy=$1 +for ac_mkdir_dir in `IFS=/; set X $ac_dummy; shift; echo "$[@]"`; do + ac_incr_dir=$ac_incr_dir/$ac_mkdir_dir + test -d $ac_incr_dir || mkdir $ac_incr_dir +done; } +])# AS_MKDIR_P + + ## ------------------ ## ## 5. Common idioms. ## diff --git a/tests/m4sh.at b/tests/m4sh.at index 5fdd49a0b..8eccd2597 100644 --- a/tests/m4sh.at +++ b/tests/m4sh.at @@ -12,7 +12,9 @@ AT_BANNER([M4sh.]) AT_SETUP([[AS_DIRNAME & AS_DIRNAME_SED]]) AT_DATA(configure.in, -[[define([AS_DIRNAME_TEST], +[[_AS_EXPR_PREPARE + +define([AS_DIRNAME_TEST], [dir=`AS_DIRNAME([$1])` test "$dir" = "$2" || echo "dirname($1) = $dir instead of $2" >&2