Fix bash completion for *.pkg.tar
[pacman-ng.git] / scripts / repo-add.sh.in
blob4708086c18d1504331d93e400f504a3a4f482859
1 #!/bin/bash
3 # repo-add - add a package to a given repo database file
4 # repo-remove - remove a package entry from a given repo database file
5 # @configure_input@
7 # Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org>
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
22 shopt -s extglob
24 # gettext initialization
25 export TEXTDOMAIN='pacman-scripts'
26 export TEXTDOMAINDIR='@localedir@'
28 myver='@PACKAGE_VERSION@'
29 confdir='@sysconfdir@'
31 QUIET=0
32 DELTA=0
33 WITHFILES=0
34 SIGN=0
35 VERIFY=0
36 REPO_DB_FILE=
37 LOCKFILE=
38 CLEAN_LOCK=0
40 # ensure we have a sane umask set
41 umask 0022
43 m4_include(library/output_format.sh)
45 # print usage instructions
46 usage() {
47 cmd=${0##*/}
48 printf "%s (pacman) %s\n\n" "$cmd" "$myver"
49 if [[ $cmd == "repo-add" ]] ; then
50 printf "$(gettext "Usage: repo-add [options] <path-to-db> <package|delta> ...\n")"
51 printf "$(gettext "\
52 repo-add will update a package database by reading a package file.\n\
53 Multiple packages to add can be specified on the command line.\n\n")"
54 printf "$(gettext "Options:\n")"
55 printf "$(gettext " -d, --delta generate and add delta for package update\n")"
56 printf "$(gettext " -f, --files update database's file list\n")"
57 elif [[ $cmd == "repo-remove" ]] ; then
58 printf "$(gettext "Usage: repo-remove [options] <path-to-db> <packagename|delta> ...\n\n")"
59 printf "$(gettext "\
60 repo-remove will update a package database by removing the package name\n\
61 specified on the command line from the given repo database. Multiple\n\
62 packages to remove can be specified on the command line.\n\n")"
63 printf "$(gettext "Options:\n")"
65 printf "$(gettext " -q, --quiet minimize output\n")"
66 printf "$(gettext " -s, --sign sign database with GnuPG after update\n")"
67 printf "$(gettext " -k, --key <key> use the specified key to sign the database\n")"
68 printf "$(gettext " -v, --verify verify database's signature before update\n")"
69 printf "$(gettext "\n\
70 See %s(8) for more details and descriptions of the available options.\n\n")" $cmd
71 if [[ $cmd == "repo-add" ]] ; then
72 echo "$(gettext "Example: repo-add /path/to/repo.db.tar.gz pacman-3.0.0-1-i686.pkg.tar.gz")"
73 elif [[ $cmd == "repo-remove" ]] ; then
74 echo "$(gettext "Example: repo-remove /path/to/repo.db.tar.gz kernel26")"
78 version() {
79 cmd=${0##*/}
80 printf "%s (pacman) %s\n\n" "$cmd" "$myver"
81 printf "$(gettext "\
82 Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org>\n\n\
83 This is free software; see the source for copying conditions.\n\
84 There is NO WARRANTY, to the extent permitted by law.\n")"
87 # format a metadata entry
88 # arg1 - Entry name
89 # ... - value(s)
90 format_entry() {
91 local field=$1; shift
93 if [[ $1 ]]; then
94 printf '%%%s%%\n' "$field"
95 printf '%s\n' "$@"
96 printf '\n'
100 find_pkgentry() {
101 local pkgname=$1
102 local pkgentry
103 for pkgentry in $tmpdir/tree/$pkgname*; do
104 name=${pkgentry##*/}
105 if [[ ${name%-*-*} = $pkgname ]]; then
106 echo $pkgentry
107 return 0
109 done
110 return 1
113 # Get the package name from the delta filename
114 get_delta_pkgname() {
115 local tmp
117 tmp=${1##*/}
118 echo ${tmp%-*-*_to*}
121 # write a delta entry
122 # arg1 - path to delta file
123 db_write_delta() {
124 deltafile="$1"
125 pkgname="$(get_delta_pkgname $deltafile)"
127 pkgentry=$(find_pkgentry $pkgname)
128 if [[ -z $pkgentry ]]; then
129 error "$(gettext "No database entry for package '%s'.")" "$pkgname"
130 return 1
132 deltas="$pkgentry/deltas"
133 if [[ ! -f $deltas ]]; then
134 echo -e "%DELTAS%" >$deltas
136 # get md5sum and compressed size of package
137 md5sum="$(openssl dgst -md5 "$deltafile")"
138 md5sum="${md5sum##* }"
139 csize=$(@SIZECMD@ "$deltafile")
141 oldfile=$(xdelta3 printhdr $deltafile | grep "XDELTA filename (source)" | sed 's/.*: *//')
142 newfile=$(xdelta3 printhdr $deltafile | grep "XDELTA filename (output)" | sed 's/.*: *//')
144 if grep -q "$oldfile.*$newfile" $deltas; then
145 sed -i.backup "/$oldfile.*$newfile/d" $deltas && rm -f $deltas.backup
147 msg2 "$(gettext "Adding 'deltas' entry : %s -> %s")" "$oldfile" "$newfile"
148 echo ${deltafile##*/} $md5sum $csize $oldfile $newfile >> $deltas
150 return 0
151 } # end db_write_delta
153 # remove a delta entry
154 # arg1 - path to delta file
155 db_remove_delta() {
156 deltafile="$1"
157 filename=${deltafile##*/}
158 pkgname="$(get_delta_pkgname $deltafile)"
160 pkgentry=$(find_pkgentry $pkgname)
161 if [[ -z $pkgentry ]]; then
162 return 1
164 deltas="$pkgentry/deltas"
165 if [[ ! -f $deltas ]]; then
166 return 1
168 if grep -q "$filename" $deltas; then
169 sed -i.backup "/$filename/d" $deltas && rm -f $deltas.backup
170 msg2 "$(gettext "Removing existing entry '%s'...")" "$filename"
171 return 0
174 return 1
175 } # end db_remove_delta
177 check_gpg() {
178 if ! type -p gpg >/dev/null; then
179 error "$(gettext "Cannot find the gpg binary! Is GnuPG installed?")"
180 exit 1 # $E_MISSING_PROGRAM
184 # sign the package database once repackaged
185 create_signature() {
186 (( ! SIGN )) && return
187 local dbfile="$1"
188 local ret=0
189 msg "$(gettext "Signing database...")"
191 local SIGNWITHKEY=""
192 if [[ -n $GPGKEY ]]; then
193 SIGNWITHKEY="-u ${GPGKEY}"
195 gpg --detach-sign --use-agent ${SIGNWITHKEY} "$dbfile" &>/dev/null || ret=$?
197 if (( ! ret )); then
198 msg2 "$(gettext "Created signature file %s.")" "${dbfile##*/}.sig"
199 else
200 warning "$(gettext "Failed to sign package database.")"
204 # verify the existing package database signature
205 verify_signature() {
206 (( ! VERIFY )) && return
207 local dbfile="$1"
208 local ret=0
209 msg "$(gettext "Verifying database signature...")"
211 if [[ ! -f $dbfile.sig ]]; then
212 warning "$(gettext "No existing signature found, skipping verification.")"
213 return
215 gpg --verify "$dbfile.sig" || ret=$?
216 if (( ! ret )); then
217 msg2 "$(gettext "Database signature file verified.")"
218 else
219 error "$(gettext "Database signature was NOT valid!")"
220 exit 1
224 verify_repo_extension() {
225 local repofile=$1
227 case "$repofile" in
228 *.@(db|files).tar.gz) TAR_OPT="z" ;;
229 *.@(db|files).tar.bz2) TAR_OPT="j" ;;
230 *.@(db|files).tar.xz) TAR_OPT="J" ;;
231 *.@(db|files).tar.Z) TAR_OPT="Z" ;;
232 *.@(db|files).tar) TAR_OPT="" ;;
233 *) error "$(gettext "'%s' does not have a valid archive extension.")" \
234 "$repofile"
235 exit 1 ;;
236 esac
238 printf '%s' "$TAR_OPT"
241 # write an entry to the pacman database
242 # arg1 - path to package
243 db_write_entry() {
244 # blank out all variables
245 local pkgfile="$1"
246 local -a _groups _licenses _replaces _depends _conflicts _provides _optdepends
247 local pkgname pkgver pkgdesc csize size url arch builddate packager \
248 md5sum sha256sum pgpsig pgpsigsize
250 # read info from the zipped package
251 local line var val
252 while read -r line; do
253 [[ ${line:0:1} = '#' ]] && continue
254 IFS=' =' read -r var val < <(printf '%s\n' "$line")
256 # normalize whitespace with an extglob
257 declare "$var=${val//+([[:space:]])/ }"
258 case "$var" in
259 group) _groups+=("$group") ;;
260 license) _licenses+=("$license") ;;
261 replaces) _replaces+=("$replaces") ;;
262 depend) _depends+=("$depend") ;;
263 conflict) _conflicts+=("$conflict") ;;
264 provides) _provides+=("$provides") ;;
265 optdepend) _optdepends+=("$optdepend") ;;
266 esac
267 done< <(bsdtar -xOqf "$pkgfile" .PKGINFO)
269 # ensure $pkgname and $pkgver variables were found
270 if [[ -z $pkgname || -z $pkgver ]]; then
271 error "$(gettext "Invalid package file '%s'.")" "$pkgfile"
272 return 1
275 if [[ -d $tmpdir/tree/$pkgname-$pkgver ]]; then
276 warning "$(gettext "An entry for '%s' already existed")" "$pkgname-$pkgver"
277 else
278 if (( DELTA )); then
279 pkgentry=$(find_pkgentry $pkgname)
280 if [[ -n $pkgentry ]]; then
281 local oldfilename=$(grep -A1 FILENAME $pkgentry/desc | tail -n1)
282 local oldfile="$(dirname $1)/$oldfilename"
287 # compute base64'd PGP signature
288 if [[ -f "$pkgfile.sig" ]]; then
289 pgpsigsize=$(@SIZECMD@ "$pkgfile.sig")
290 if (( pgpsigsize > 16384 )); then
291 error "$(gettext "Invalid package signature file '%s'.")" "$pkgfile.sig"
292 return 1
294 msg2 "$(gettext "Adding package signature...")"
295 pgpsig=$(openssl base64 -in "$pkgfile.sig" | tr -d '\n')
298 csize=$(@SIZECMD@ "$pkgfile")
300 # compute checksums
301 msg2 "$(gettext "Computing checksums...")"
302 md5sum="$(openssl dgst -md5 "$pkgfile")"
303 md5sum="${md5sum##* }"
304 sha256sum="$(openssl dgst -sha256 "$pkgfile")"
305 sha256sum="${sha256sum##* }"
307 # remove an existing entry if it exists, ignore failures
308 db_remove_entry "$pkgname"
310 # create package directory
311 pushd "$tmpdir/tree" >/dev/null
312 mkdir "$pkgname-$pkgver"
313 pushd "$pkgname-$pkgver" >/dev/null
315 # restore an eventual deltas file
316 [[ -f ../$pkgname.deltas ]] && mv "../$pkgname.deltas" deltas
318 # create desc entry
319 msg2 "$(gettext "Creating '%s' db entry...")" 'desc'
321 format_entry "FILENAME" "${1##*/}"
322 format_entry "NAME" "$pkgname"
323 format_entry "BASE" "$pkgbase"
324 format_entry "VERSION" "$pkgver"
325 format_entry "DESC" "$pkgdesc"
326 format_entry "GROUPS" "${_groups[@]}"
327 format_entry "CSIZE" "$csize"
328 format_entry "ISIZE" "$size"
330 # add checksums
331 format_entry "MD5SUM" "$md5sum"
332 format_entry "SHA256SUM" "$sha256sum"
334 # add PGP sig
335 format_entry "PGPSIG" "$pgpsig"
337 format_entry "URL" "$url"
338 format_entry "LICENSE" "${_licenses[@]}"
339 format_entry "ARCH" "$arch"
340 format_entry "BUILDDATE" "$builddate"
341 format_entry "PACKAGER" "$packager"
342 format_entry "REPLACES" "${_replaces[@]}"
343 } >'desc'
345 # create depends entry
346 msg2 "$(gettext "Creating '%s' db entry...")" 'depends'
348 format_entry "DEPENDS" "${_depends[@]}"
349 format_entry "CONFLICTS" "${_conflicts[@]}"
350 format_entry "PROVIDES" "${_provides[@]}"
351 format_entry "OPTDEPENDS" "${_optdepends[@]}"
352 } >'depends'
354 popd >/dev/null
355 popd >/dev/null
357 # create files file if wanted
358 if (( WITHFILES )); then
359 msg2 "$(gettext "Creating '%s' db entry...")" 'files'
360 local files_path="$tmpdir/tree/$pkgname-$pkgver/files"
361 echo "%FILES%" >$files_path
362 bsdtar --exclude='^.*' -tf "$pkgfile" >>$files_path
365 # create a delta file
366 if (( DELTA )); then
367 if [[ -n $oldfilename ]]; then
368 if [[ -f $oldfile ]]; then
369 delta=$(pkgdelta -q $oldfile $1)
370 if [[ -f $delta ]]; then
371 db_write_delta $delta
373 else
374 warning "$(gettext "Old package file not found: %s")" "$oldfilename"
379 return 0
380 } # end db_write_entry
382 # remove existing entries from the DB
383 # arg1 - package name
384 db_remove_entry() {
385 local pkgname=$1
386 local notfound=1
387 local pkgentry=$(find_pkgentry $pkgname)
388 while [[ -n $pkgentry ]]; do
389 notfound=0
390 if [[ -f $pkgentry/deltas ]]; then
391 mv "$pkgentry/deltas" "$tmpdir/tree/$pkgname.deltas"
393 msg2 "$(gettext "Removing existing entry '%s'...")" \
394 "${pkgentry##*/}"
395 rm -rf $pkgentry
396 pkgentry=$(find_pkgentry $pkgname)
397 done
398 return $notfound
399 } # end db_remove_entry
401 elephant() {
402 case $(( RANDOM % 2 )) in
403 0) printf '%s\n' "H4sIAL3qBE4CAyWLwQ3AMAgD/0xh5UPzYiFUMgjq7LUJsk7yIQNAQTAikFUDnqkr" \
404 "OQFOUm0Wd9pHCi13ONjBpVdqcWx+EdXVX4vXvGv5cgztB9+fJxZ7AAAA"
407 1) printf '%s\n' "H4sIAJVWBU4CA21RMQ7DIBDbeYWrDgQJ7rZ+IA/IB05l69alcx5fc0ASVXUk4jOO" \
408 "7yAAUWtorygwJ4hlMii0YkJKKRKGvsMsiykl1SalvrMD1gUXyXRkGZPx5OPft81K" \
409 "tNAiAjyGjYO47h1JjizPkJrCWbK/4C+uLkT7bzpGc7CT9bmOzNSW5WLSO5vexjmH" \
410 "ZL9JFFZeAa0a2+lKjL2anpYfV+0Zx9LJ+/MC8nRayuDlSNy2rfAPibOzsiWHL0jL" \
411 "SsjFAQAA"
413 esac | openssl base64 -d | gzip -d
416 check_repo_db() {
417 local repodir
419 # ensure the path to the DB exists
420 if [[ "$LOCKFILE" == /* ]]; then
421 repodir=${LOCKFILE%/*}/
422 else
423 repodir=$PWD/$LOCKFILE
424 repodir=${repodir%/*}/
427 if [[ ! -d "$repodir" ]]; then
428 error "$(gettext "%s does not exist or is not a directory.")" "$repodir"
429 exit 1
432 # check lock file
433 if ( set -o noclobber; echo "$$" > "$LOCKFILE") 2> /dev/null; then
434 CLEAN_LOCK=1
435 else
436 error "$(gettext "Failed to acquire lockfile: %s.")" "$LOCKFILE"
437 [[ -f $LOCKFILE ]] && error "$(gettext "Held by process %s")" "$(cat $LOCKFILE)"
438 exit 1
441 if [[ -f $REPO_DB_FILE ]]; then
442 # there are two situations we can have here- a DB with some entries,
443 # or a DB with no contents at all.
444 if ! bsdtar -tqf "$REPO_DB_FILE" '*/desc' >/dev/null 2>&1; then
445 # check empty case
446 if [[ -n $(bsdtar -tqf "$REPO_DB_FILE" '*' 2>/dev/null) ]]; then
447 error "$(gettext "Repository file '%s' is not a proper pacman database.")" "$REPO_DB_FILE"
448 exit 1
451 verify_signature "$REPO_DB_FILE"
452 msg "$(gettext "Extracting database to a temporary location...")"
453 bsdtar -xf "$REPO_DB_FILE" -C "$tmpdir/tree"
454 else
455 case "$cmd" in
456 repo-remove)
457 error "$(gettext "Repository file '%s' was not found.")" "$REPO_DB_FILE"
458 exit 1
460 repo-add)
461 # check if the file can be created (write permission, directory existence, etc)
462 if ! touch "$REPO_DB_FILE"; then
463 error "$(gettext "Repository file '%s' could not be created.")" "$REPO_DB_FILE"
464 exit 1
466 rm -f "$REPO_DB_FILE"
468 esac
472 add() {
473 if [[ ! -f $1 ]]; then
474 error "$(gettext "File '%s' not found.")" "$1"
475 return 1
478 if [[ ${1##*.} == "delta" ]]; then
479 deltafile=$1
480 msg "$(gettext "Adding delta '%s'")" "$deltafile"
481 if ! type xdelta3 &>/dev/null; then
482 error "$(gettext "Cannot find the xdelta3 binary! Is xdelta3 installed?")"
483 exit 1
485 if db_write_delta "$deltafile"; then
486 return 0
487 else
488 return 1
492 pkgfile=$1
493 if ! bsdtar -tqf "$pkgfile" .PKGINFO >/dev/null 2>&1; then
494 error "$(gettext "'%s' is not a package file, skipping")" "$pkgfile"
495 return 1
498 msg "$(gettext "Adding package '%s'")" "$pkgfile"
500 db_write_entry "$pkgfile"
503 remove() {
504 if [[ ${1##*.} == "delta" ]]; then
505 deltafile=$1
506 msg "$(gettext "Searching for delta '%s'...")" "$deltafile"
507 if db_remove_delta "$deltafile"; then
508 return 0
509 else
510 error "$(gettext "Delta matching '%s' not found.")" "$deltafile"
511 return 1
515 pkgname=$1
516 msg "$(gettext "Searching for package '%s'...")" "$pkgname"
518 if db_remove_entry "$pkgname"; then
519 rm -f "$tmpdir/tree/$pkgname.deltas"
520 return 0
521 else
522 error "$(gettext "Package matching '%s' not found.")" "$pkgname"
523 return 1
527 trap_exit() {
528 # unhook all traps to avoid race conditions
529 trap '' EXIT TERM HUP QUIT INT ERR
531 echo
532 error "$@"
533 clean_up 1
536 clean_up() {
537 local exit_code=${1:-$?}
539 # unhook all traps to avoid race conditions
540 trap '' EXIT TERM HUP QUIT INT ERR
542 [[ -d $tmpdir ]] && rm -rf "$tmpdir"
543 (( CLEAN_LOCK )) && [[ -f $LOCKFILE ]] && rm -f "$LOCKFILE"
545 exit $exit_code
548 # PROGRAM START
550 # determine whether we have gettext; make it a no-op if we do not
551 if ! type gettext &>/dev/null; then
552 gettext() {
553 echo "$@"
557 case "$1" in
558 -h|--help) usage; exit 0;;
559 -V|--version) version; exit 0;;
560 esac
562 # figure out what program we are
563 cmd=${0##*/}
564 if [[ $cmd == "repo-elephant" ]]; then
565 elephant
566 exit 0
569 if [[ $cmd != "repo-add" && $cmd != "repo-remove" ]]; then
570 error "$(gettext "Invalid command name '%s' specified.")" "$cmd"
571 exit 1
574 tmpdir=$(mktemp -d /tmp/repo-tools.XXXXXXXXXX) || (\
575 error "$(gettext "Cannot create temp directory for database building.")"; \
576 exit 1)
577 mkdir $tmpdir/tree
579 trap 'clean_up' EXIT
580 for signal in TERM HUP QUIT; do
581 trap "trap_exit \"$(gettext "%s signal caught. Exiting...")\" \"$signal\"" "$signal"
582 done
583 trap 'trap_exit "$(gettext "Aborted by user! Exiting...")"' INT
584 trap 'trap_exit "$(gettext "An unknown error has occurred. Exiting...")"' ERR
586 declare -a args
587 success=0
588 # parse arguments
589 while (( $# )); do
590 case "$1" in
591 -q|--quiet) QUIET=1;;
592 -d|--delta) DELTA=1;;
593 -f|--files) WITHFILES=1;;
594 -s|--sign)
595 check_gpg
596 SIGN=1
597 if ! gpg --list-key ${GPGKEY} &>/dev/null; then
598 if [[ ! -z $GPGKEY ]]; then
599 error "$(gettext "The key ${GPGKEY} does not exist in your keyring.")"
600 else
601 error "$(gettext "There is no key in your keyring.")"
603 exit 1
606 -k|--key)
607 check_gpg
608 shift
609 GPGKEY="$1"
610 if ! gpg --list-key ${GPGKEY} &>/dev/null; then
611 error "$(gettext "The key ${GPGKEY} does not exist in your keyring.")"
612 exit 1
615 -v|--verify)
616 check_gpg
617 VERIFY=1
620 args+=("$1")
622 esac
623 shift
624 done
626 REPO_DB_FILE=${args[0]}
627 if [[ -z $REPO_DB_FILE ]]; then
628 usage
629 exit 1
632 LOCKFILE=$REPO_DB_FILE.lck
634 verify_repo_extension "$REPO_DB_FILE" >/dev/null
635 check_repo_db
637 for arg in "${args[@]:1}"; do
638 case "$cmd" in
639 repo-add) add "$arg" ;;
640 repo-remove) remove "$arg" ;;
641 esac && success=1
642 done
644 # if at least one operation was a success, re-zip database
645 if (( success )); then
646 msg "$(gettext "Creating updated database file '%s'")" "$REPO_DB_FILE"
648 TAR_OPT=$(verify_repo_extension "$REPO_DB_FILE")
649 filename=${REPO_DB_FILE##*/}
651 pushd "$tmpdir/tree" >/dev/null
652 if ( shopt -s nullglob; files=(*); (( ${#files[*]} )) ); then
653 bsdtar -c${TAR_OPT}f "$tmpdir/$filename" *
654 else
655 # we have no packages remaining? zip up some emptyness
656 warning "$(gettext "No packages remain, creating empty database.")"
657 bsdtar -c${TAR_OPT}f "$tmpdir/$filename" -T /dev/null
659 popd >/dev/null
661 create_signature "$tmpdir/$filename"
663 [[ -f $REPO_DB_FILE ]] && mv -f "$REPO_DB_FILE" "${REPO_DB_FILE}.old"
664 if [[ -f $REPO_DB_FILE.sig ]]; then
665 mv -f "$REPO_DB_FILE.sig" "$REPO_DB_FILE.old.sig"
666 else
667 rm -f "$REPO_DB_FILE.old.sig"
669 [[ -f $tmpdir/$filename ]] && mv "$tmpdir/$filename" "$REPO_DB_FILE"
670 [[ -f $tmpdir/$filename.sig ]] && mv "$tmpdir/$filename.sig" "$REPO_DB_FILE.sig"
671 dblink="${REPO_DB_FILE%.tar*}"
672 target=${REPO_DB_FILE##*/}
673 rm -f "$dblink" "$dblink.sig"
674 ln -s "$target" "$dblink" 2>/dev/null || \
675 ln "$target" "$dblink" 2>/dev/null || \
676 cp "$REPO_DB_FILE" "$dblink"
677 if [[ -f "$REPO_DB_FILE.sig" ]]; then
678 ln -s "$target.sig" "$dblink.sig" 2>/dev/null || \
679 ln "$target.sig" "$dblink.sig" 2>/dev/null || \
680 cp "$REPO_DB_FILE.sig" "$dblink.sig"
682 else
683 msg "$(gettext "No packages modified, nothing to do.")"
684 exit 1
687 exit 0
688 # vim: set ts=2 sw=2 noet: