]> git.ipfire.org Git - thirdparty/autoconf.git/commitdiff
QNX 4.2.5's expr always exits 1 when `:' is used with parens.
authorAkim Demaille <akim@epita.fr>
Wed, 29 Nov 2000 15:52:02 +0000 (15:52 +0000)
committerAkim Demaille <akim@epita.fr>
Wed, 29 Nov 2000 15:52:02 +0000 (15:52 +0000)
* doc/autoconf.texi (Limitations of Usual Tools) <expr>: More
information, thanks to Paul Berrevoets, Paul Eggert and David
Morgan.
* sh.m4 (_AS_EXPR_PREPARE): New.
(AS_DIRNAME): Use it.

ChangeLog
THANKS
doc/autoconf.texi
lib/m4sugar/m4sh.m4
m4sh.m4
tests/m4sh.at

index c29f0a691469903f61f889ac6251433bedfb9e5b..6be75c1cb72b49e159d4a5fa9e62df327c5d91f4 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2000-11-29  Akim Demaille  <akim@epita.fr>
+
+       QNX 4.2.5's expr always exits 1 when `:' is used with parens.
+
+       * doc/autoconf.texi (Limitations of Usual Tools) <expr>: 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  <akim@epita.fr>
 
        sizeof (struct {char a,b; }) is not required to be 2.
diff --git a/THANKS b/THANKS
index 77bc23258ef4d0a81ce9349b142d81f4cd2a1331..6696d3c25abb7de4f49e16c3c3f5bc27de7f9144 100644 (file)
--- 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
index 6fea9c13194acb846f9f26211454945b766726b0..3b0e96b8c2ffd0e7df97eb52b0aa0ad3204de4e5 100644 (file)
@@ -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}
index 7fb8200210d36d96d808a457d6734d9b1a991c92..be72f6c58688ce620d941ebcc7b7cffe71de5096 100644 (file)
@@ -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 7fb8200210d36d96d808a457d6734d9b1a991c92..be72f6c58688ce620d941ebcc7b7cffe71de5096 100644 (file)
--- 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.  ##
index 5fdd49a0b5d66b8fb42853d02f109ee822874a42..8eccd2597044e838764de97dc001cc7eebc54498 100644 (file)
@@ -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