#! @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 -e "$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. 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='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