524 lines
13 KiB
Plaintext
524 lines
13 KiB
Plaintext
#! @SHELL@
|
|
# 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
|
|
emulate sh
|
|
NULLCMD=:
|
|
# Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
|
|
# is contrary to our usage. Disable this feature.
|
|
alias -g '${1+"$@"}'='"$@"'
|
|
setopt NO_GLOB_SUBST
|
|
else
|
|
case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
|
|
fi
|
|
BIN_SH=xpg4; export BIN_SH # for Tru64
|
|
DUALCASE=1; export DUALCASE # for MKS sh
|
|
|
|
if test "${LANG+set}" = set; then LANG=C; export LANG; fi
|
|
if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
|
|
if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
|
|
if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi
|
|
|
|
# Also make sure CDPATH is empty, and IFS is space, tab, \n in that order.
|
|
# Be careful to avoid that editors munge IFS
|
|
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
|
|
IFS=" "" ""
|
|
"
|
|
|
|
: ${TMPDIR=/tmp}
|
|
: ${ZIP="@ZIP@"}
|
|
: ${UNZIP="@UNZIP@"}
|
|
progname="$0"
|
|
|
|
# Emit a usage message and exit with error status 1
|
|
usage () {
|
|
cat >&2 <<EOF
|
|
Usage: $0 {ctxu}[vfm0Mi@] [jar-file] [manifest-file] {[-C dir] files} ...
|
|
Options:
|
|
-c create new archive
|
|
-t list table of contents for archive
|
|
-x extract named (or all) files from archive
|
|
-u update existing archive
|
|
-v generate verbose output on standard output
|
|
-f specify archive file name
|
|
-m include manifest information from specified manifest file
|
|
-0 store only; use no ZIP compression
|
|
-M do not create a manifest file for the entries
|
|
-i generate index information for the specified jar files
|
|
-@ instead of {[-C dir] files} ... accept one or more response files,
|
|
each containing one command-line argument
|
|
-C change to the specified directory and include the following file
|
|
If any file is a directory then it is processed recursively.
|
|
The manifest file name and the archive file name needs to be specified
|
|
in the same order the 'm' and 'f' flags are specified.
|
|
|
|
Example 1: to archive two class files into an archive called classes.jar:
|
|
jar cvf classes.jar Foo.class Bar.class
|
|
Example 2: use an existing manifest file 'mymanifest' and archive all the
|
|
files in the foo/ directory into 'classes.jar':
|
|
jar cvfm classes.jar mymanifest -C foo/ .
|
|
|
|
EOF
|
|
(exit 1); exit 1
|
|
}
|
|
|
|
# Emit an error message and exit with error status 1
|
|
error () {
|
|
echo "$progname: $*" >&2
|
|
(exit 1); exit 1
|
|
}
|
|
|
|
# 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 -f "$2"/"$1"; then
|
|
error "$1": Duplicate entry.
|
|
fi
|
|
dir=`dirname "$1"`
|
|
$mkdir_p "$2"/"$dir"
|
|
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 () {
|
|
test -n "$JARTMP" && return
|
|
|
|
{
|
|
JARTMP=`(umask 077 && mktemp -d "$TMPDIR/jarXXXXXX") 2>/dev/null` &&
|
|
test -n "$JARTMP" && test -d "$JARTMP"
|
|
} || {
|
|
JARTMP=$TMPDIR/jar$$-$RANDOM
|
|
(umask 077 && mkdir "$JARTMP")
|
|
} || exit $?
|
|
|
|
trap 'exit_status=$?
|
|
if test -n "$JARTMP"; then rm -rf "$JARTMP"; fi
|
|
exit $exit_status' 0
|
|
}
|
|
|
|
# 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 () {
|
|
dir=`dirname "$1"`
|
|
$mkdir_p "$dir"
|
|
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
|
|
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
|
|
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
|
|
if cd "$1"; then
|
|
return
|
|
else
|
|
(exit 1); exit 1
|
|
fi
|
|
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[1] " " d[2] " " t[1] " " t[2] " 00"
|
|
gsub (/^ *[0-9]* ..-..-.. ..:.. /, "")
|
|
printf "%6d %s %s\n", size, strftime ("%a %b %d %H:%M:%S %Z %Y", mktime (timestamp)), $0
|
|
}'
|
|
}
|
|
|
|
# mkdir -p emulation based on the mkinstalldirs script.
|
|
func_mkdir_p () {
|
|
for file
|
|
do
|
|
case $file in
|
|
/*) pathcomp=/ ;;
|
|
*) pathcomp= ;;
|
|
esac
|
|
oIFS=$IFS
|
|
IFS=/
|
|
set fnord $file
|
|
shift
|
|
IFS=$oIFS
|
|
|
|
errstatus=0
|
|
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='func_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
|
|
|
|
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
|
|
if $skip; then
|
|
skip=false
|
|
continue
|
|
fi
|
|
case "$arg" in
|
|
-C) skip=: ;;
|
|
-C*) ;;
|
|
*)
|
|
escaped=`echo "X$arg" | sed 's/^X//; s/[].[^$\\*]/\\\\&/g' `
|
|
grep "^$escaped/" "$JARTMP"/list >> "$JARTMP"/chosen || :
|
|
grep "^$escaped\$" "$JARTMP"/list >> "$JARTMP"/chosen || :
|
|
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" | \
|
|
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 ${1+"$@"}
|
|
if test $manifest_kind != none; then
|
|
make_manifest "$JARTMP"/files/META-INF/MANIFEST.MF $manifest_kind "$manifest_file"
|
|
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 ${1+"$@"}
|
|
|
|
# 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
|