[RFT/RFA] bashjar patch
Ralf Wildenhues
Ralf.Wildenhues@gmx.de
Tue May 2 19:20:00 GMT 2006
[ please Cc: me on replies ]
Hi Paolo,
* Paolo Bonzini wrote on Fri, Apr 28, 2006 at 11:31:01PM CEST:
>
> Testing is appreciated with shells other than bash.
Some comments (untested):
> Index: scripts/jar.in
> ===================================================================
> --- scripts/jar.in (revision 0)
> +++ scripts/jar.in (revision 0)
> @@ -0,0 +1,503 @@
> +#! /bin/sh
> +# Copyright (C) 2006 Free Software Foundation
> +# Written by Paolo Bonzini.
> +#
> +# 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 2 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, write to the Free Software Foundation, Inc.,
> +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
> +
> +
> +# POSIX and NLS nuisances, taken from autoconf.
> +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
*snip*
Be encouraged to pick the AS_BOURNE_COMPATIBLE contents from
Autoconf-2.59c (which contain a fix for the OpenBSD and OSF shells).
Also, add
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
to avoid some possible clutter.
> +progname="$0"
Will the script ever be invoked as $shell $path/to/jar? Then this can
be wrong.
> +# Usage: copy SRC DEST
> +# Copy file SRC to directory DEST, which is the staging area of the jar file.
> +# Fail if it is already present or if it is not a regular file.
> +copy () {
> + if test -f "$1"; then
> + # A simple optimization. Optimistically assuming that ln will work
> + # cuts 60% of the run-time!
> + if ln "$1" "$2"/"$1" > /dev/null 2>&1; then
> + return 0
> + fi
> +
> + if test -e "$2"/"$1"; then
> + error "$1": Duplicate entry.
> + fi
> + $mkdir_p "$2"/`dirname "$1"`
> + ln "$1" "$2"/"$1" > /dev/null 2>&1 || cp "$1" "$2"/"$1"
> + elif test -e "$1"; then
> + error "$1": Invalid file type.
> + else
> + error "$1": File not found.
> + fi
> +}
> +
> +# Make a temporary directory and store its name in the JARTMP variable.
> +make_tmp () {
Be encouraged to use the code documented in
info Autoconf "Limitations of Usual Tools"
under "mktemp" (again, 2.59c) instead (AS_TMPDIR).
> + trap 'rm -rf $JARTMP' EXIT
I think EXIT is not portable, but 0 is. Since with mktemp you'll still
need a trap or similar cleanup mechanism, and the exit status of the
script matters, you'll need to change all instances "exit N" to
(exit N); exit N
(like AS_EXIT) to portably set the exit status. Otherwise, e.g. on some
HP-UX you may falsely get a zero exit status.
Also, although this may be wrongly-guided or just plain FUD, I'd always
rather write
test -n "$JARTMP" && rm -rf "$JARTMP"
for safety.
> +# Usage: make_manifest destfile kind [source-manifest]
> +# Create a manifest file and store it in destfile. KIND can be "default",
> +# or "user", in which case SOURCE-MANIFEST must be specified as well.
> +make_manifest () {
> + $mkdir_p `dirname "$1"`
> + case $2 in
> + default)
> + cat > "$1" <<\EOF
> +Manifest-Version: 1.0
> +Created-By: @VERSION@
> +
> +EOF
> + ;;
> + user)
> + cp "$3" "$1"
> + ;;
> + esac
> +}
> +
> +# Usage: set_var var [value]
> +# Exit with an error if set_var was already called for the same VAR. Else
> +# set the variable VAR to the value VALUE (or the empty value if no parameter
> +# is given).
> +set_var () {
> + if eval test x\$set_$1 = xset; then
> + error Incompatible or repeated options.
> + else
> + eval $1='"$2"'
This can be simplified to
eval $1=\$2
> + eval set_$1=set
> + fi
> +}
> +
> +# Process the arguments, including -C options, and copy the whole tree
> +# to $JARTMP/files so that zip can be invoked later from there.
> +make_files () {
> + change=false
> + if $process_response_files; then
> + if test $# = 0; then
> + while read arg; do
> + make_files_1 "$arg"
> + done
> + else
> + for infile
> + do
> + exec 5<&0
> + exec 0< $infile
> + while read arg; do
> + make_files_1 "$arg"
> + done
> + exec 0<&5
I think it's ok to close 5 here:
exec 5<&-
> + done
> + fi
> + else
> + for arg
> + do
> + make_files_1 "$arg"
> + done
> + fi
> + cd "$old_dir"
> +}
> +
> +# Usage: make_files_1 ARG
> +# Process one argument, ARG.
> +make_files_1 () {
> + if $change; then
> + change=false
> + cd "$1" || exit 1
> + return
> + fi
> + case "$1" in
> + -C)
> + change=:
> + ;;
> + -C*)
> + cd `expr "$1" : '-C\(.*\)' `
> + return
> + ;;
> + *)
> + if test -d "$1"; then
> + $mkdir_p $JARTMP/files/"$1"
> + find "$1" | while read file; do
> + if test -d "$file"; then
> + $mkdir_p $JARTMP/files/"$file"
> + else
> + copy "$file" $JARTMP/files
> + fi
> + done
> + else
> + copy "$1" $JARTMP/files
> + fi
> + ;;
> + esac
> + cd "$old_dir"
> +}
> +
> +# Same as "jar tf $1".
> +jar_list () {
> + $UNZIP -l "$1" | \
> + sed '1,/^ ----/d;/^ ----/,$d;s/^ *[0-9]* ..-..-.. ..:.. //'
> +}
> +
> +# Same as "jar tvf $1".
> +jar_list_verbose () {
> + $UNZIP -l "$1" | \
> + @AWK@ 'BEGIN { yes = 0 }
> + /^ ----/ { yes = !yes; next }
> + yes {
> + size=$1
> + split ($2, d, "-")
> + split ($3, t, ":")
> + d[3] += (d[3] < 80) ? 2000 : 1900
> + timestamp=d[3] " " d[2] " " d[1] " " t[1] " " t[2] " 00"
> + gsub (/^ *[0-9]* ..-..-.. ..:.. /, "")
> + printf "%6d %s %s\n", size, strftime ("%+", mktime (timestamp)), $0
> + }'
> +}
I think this requires a non-traditional awk, and assumes that
AC_PROG_AWK will find one. (I don't know whether the install
documentation states this.)
> +
> +# mkdir -p emulation based on the mkinstalldirs script.
> +mkdir_p ()
> +{
> + for file
> + do
> + case $file in
> + /*) pathcomp=/ ;;
> + *) pathcomp= ;;
> + esac
> + oIFS=$IFS
> + IFS=/
> + set fnord $file
> + shift
> + IFS=$oIFS
Ah, this is a bug (which is in mkinstalldirs as well; I'll report it
upstream):
IFS is allowed to be unset at shell startup; when it is, everything is
fine, as Posix requires things to work as if IFS contained space, tab,
newline. However, after this sequence, IFS is set but empty; in this
case, field splitting is turned off. (Some ash versions expose this.)
To fix, initialize IFS with space, tab, newline (in that order!) at
script startup.
> +
> + for d
> + do
> + test "x$d" = x && continue
> + pathcomp=$pathcomp$d
> + case $pathcomp in
> + -*) pathcomp=./$pathcomp ;;
> + esac
> +
> + if test ! -d "$pathcomp"; then
> + mkdir "$pathcomp" || lasterr=$?
> + test -d "$pathcomp" || errstatus=$lasterr
> + fi
> + pathcomp=$pathcomp/
> + done
> + done
> + return "$errstatus"
> +}
> +
> +# Detect mkdir -p
> +# On NextStep and OpenStep, the `mkdir' command does not
> +# recognize any option. It will interpret all options as
> +# directories to create, and then abort because `.' already
> +# exists.
> +if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then
> + mkdir_p='mkdir -p'
> +else
> + mkdir_p='mkdir_p'
> + test -d ./-p && rmdir ./-p
> + test -d ./--version && rmdir ./--version
> +fi
> +
> +# Process the first command line option.
> +case "$1" in
> + -*) commands=`echo X"$1" | sed 's/^X-//' ` ;;
> + *) commands="$1"
> +esac
> +shift
> +
> +# Operation to perform on the JAR file
> +mode=unknown
> +
> +# First -C option on the command line
> +cur_dir=.
> +
> +# Base directory for -C options
> +old_dir=`pwd`
> +# JAR file to operate on
> +jarfile=
> +
> +# default for no {m,M} option, user for "m" option, none for "M" option
> +manifest_kind=default
> +
> +# "-0" if the "0" option was given
> +store=
> +
> +# true if the "v" option was given
> +verbose=false
> +
> +# true if the non-standard "@" option was given
> +process_response_files=false
> +
> +# An exec command if we need to redirect the zip/unzip commands' output
> +out_redirect=:
> +
> +while test -n "$commands"; do
> + # Process a letter at a time
> + command=`expr "$commands" : '\(.\)'`
> + commands=`expr "$commands" : '.\(.*\)'`
> + case "$command" in
> + c)
> + set_var mode create
> + ;;
> + t)
> + set_var mode list
> + ;;
> + x)
> + set_var mode extract
> + ;;
> + u)
> + set_var mode update
> + ;;
> +
> + f)
> + test $# = 0 && usage
> + # Multiple "f" options are accepted by Sun's JAR tool.
> + jarfile="$1"
> + test -z "$jarfile" && usage
> + shift
> + ;;
> + m)
> + test $# = 0 && usage
> + # Multiple "m" options are accepted by Sun's JAR tool, but
> + # M always overrides m.
> + test "$manifest_kind" = default && manifest_kind=user
> + manifest_file="$1"
> + test -z "$manifest_file" && usage
> + shift
> + ;;
> + 0)
> + store=-0
> + ;;
> + v)
> + verbose=:
> + ;;
> + i)
> + # Not yet implemented, and probably never will.
> + ;;
> + M)
> + manifest_kind=none
> + ;;
> + C)
> + test $# = 0 && usage
> + cur_dir="$1"
> + shift
> + ;;
> + @)
> + process_response_files=: ;;
> + *)
> + usage ;;
> + esac
> +done
> +
> +set -e
If you're going to use "set -e", be sure to go over code that has AND
lists (&&) at least, see this thread:
http://lists.gnu.org/archive/html/autoconf-patches/2006-05/msg00005.html
> +
> +case "X$jarfile" in
> + X)
> + # Work on stdin/stdout. Messages go to stderr, and if we need an input
> + # JAR file we save it temporarily in the temporary directory.
> + make_tmp
> + $mkdir_p $JARTMP/out
> + jarfile=$JARTMP/out/tmp-stdin.jar
> + out_redirect='exec >&2'
> + case $mode in
> + update|extract|list)
> + if $process_response_files && test $# = 0; then
> + error Cannot use stdin for response file.
> + fi
> + cat > $JARTMP/out/tmp-stdin.jar
> + ;;
> + esac
> + ;;
> +
> + X*/*)
> + # Make an absolute path.
> + dir=`dirname "$jarfile"`
> + jarfile=`cd $dir && pwd`/`basename "$jarfile"`
> + ;;
> +
> + X*)
> + # Make an absolute path from a filename in the current directory.
> + jarfile=`pwd`/`basename "$jarfile"`
> + ;;
> +esac
> +
> +# Perform a -C option if given right away.
> +cd "$cur_dir"
> +
> +case $mode in
> + unknown)
> + usage
> + ;;
> +
> + extract)
> + make_tmp
> +
> + # Extract the list of files in the JAR file
> + jar_list "$jarfile" > $JARTMP/list
> +
> + # If there are files on the command line, expand directories and skip -C
> + # command line arguments
> + for arg
> + do
> + $skip && skip=false && continue
> + case "$arg" in
> + -C) skip=: ;;
> + -C*) ;;
> + *)
> + escaped=`echo "$arg" | sed -n 's/[][.^$\*]/\\&/' `
> + grep -e "^list/" >> $JARTMP/chosen || :
> + grep -e "^list$" >> $JARTMP/chosen || :
"grep -e" isn't portable. Luckily you can just drop the "-e" here.
> + esac
> + done
> + test -f $JARTMP/chosen || cp $JARTMP/list $JARTMP/chosen
> +
> + # Really execute unzip
> + if $verbose; then
> + sort < $JARTMP/chosen | uniq | xargs $UNZIP -o "$jarfile" | \
Just curious: is there a reason not to use sort -u (two instances)?
> + sed -ne 's/^ creating/ created/p' -e 's/^ inflating/extracted/p'
> + else
> + sort < $JARTMP/chosen | uniq | xargs $UNZIP -o "$jarfile" > /dev/null
> + fi
> + ;;
> +
> + create)
> + make_tmp
> + $mkdir_p $JARTMP/out
> + $mkdir_p $JARTMP/files
> +
> + # Do not overwrite the JAR file if something goes wrong
> + tmp_jarfile=$JARTMP/out/`basename "$jarfile"`
> +
> + # Prepare the files in the temporary directory. This is necessary to
> + # support -C and still save relative paths in the JAR file.
> + make_files "$@"
Since you used the zsh workaround above, you can use ${1+"$@"} here, to
avoid an error if no arguments are given. (Another instance further
down.)
Hmm, some real old shells don't reset the positional parameters after
function calls, see here:
http://www.in-ulm.de/~mascheck/bourne/function_parameters.html
I think in practice there is no need to worry about this, I don't think
those systems will ever run jar; I don't have access to a system with
such a shell.
> + if test $manifest_kind != none; then
> + make_manifest $JARTMP/files/META-INF/MANIFEST.MF $manifest_kind $manifest_file
manifest_file should be double-quoted (two instances).
> + fi
> +
> + # Really execute zip
> + if $verbose; then
> + (eval $out_redirect; cd $JARTMP/files && $ZIP -rv "$tmp_jarfile" $store .)
> + else
> + (cd "$JARTMP/files" && $ZIP -r "$tmp_jarfile" $store . > /dev/null)
> + fi
> + test "$jarfile" = "$tmp_jarfile" || mv "$tmp_jarfile" "$jarfile"
> + ;;
> +
> + update)
> + make_tmp
> + $mkdir_p $JARTMP/files
> + make_files "$@"
> +
> + # Same as above, but zip takes care of not overwriting the file
> + case $manifest_kind in
> + none)
> + $verbose && (eval $out_redirect; echo removing manifest)
> + $ZIP -d "$jarfile" META-INF/MANIFEST.MF > /dev/null 2>&1 || :
> + ;;
> + *)
> + make_manifest $JARTMP/files/META-INF/MANIFEST.MF $manifest_kind $manifest_file
> + ;;
> + esac
> + if $verbose; then
> + (eval $out_redirect; cd $JARTMP/files && $ZIP -ruv "$jarfile" $store .)
> + else
> + (cd $JARTMP/files && $ZIP -ru "$jarfile" $store . > /dev/null)
> + fi
> + ;;
> +
> + list)
> + # Everything's done in the functions
> + if $verbose; then
> + jar_list_verbose "$jarfile"
> + else
> + jar_list "$jarfile"
> + fi ;;
> +esac
> +
> +if test "$out_redirect" != :; then
> + # Cat back to stdout if necessary
> + case $mode in
> + create|update) cat $JARTMP/out/tmp-stdin.jar ;;
> + esac
> +fi
> +exit 0
More information about the Gcc-patches
mailing list