+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.
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
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
@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
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}
set -o posix
fi
+_AS_EXPR_PREPARE
_AS_UNSET_PREPARE
# NLS nuisances.
# 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
$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]); }])
-## ------------------------------------------- ##
-## 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.
# 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; }
/^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. ##
set -o posix
fi
+_AS_EXPR_PREPARE
_AS_UNSET_PREPARE
# NLS nuisances.
# 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
$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]); }])
-## ------------------------------------------- ##
-## 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.
# 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; }
/^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. ##
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