repo-add: fix path designation regression
[pacman-ng.git] / scripts / repo-add.sh.in
blobde0b12c1c40c8212cfd218d75abdb9f725067ec1
1 #!@BASH_SHELL@
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 # gettext initialization
23 export TEXTDOMAIN='pacman'
24 export TEXTDOMAINDIR='@localedir@'
26 myver='@PACKAGE_VERSION@'
27 confdir='@sysconfdir@'
29 QUIET=0
30 DELTA=0
31 WITHFILES=0
32 SIGN=0
33 VERIFY=0
34 REPO_DB_FILE=
35 LOCKFILE=
36 CLEAN_LOCK=0
38 # ensure we have a sane umask set
39 umask 0022
41 m4_include(library/output_format.sh)
43 # print usage instructions
44 usage() {
45 cmd=${0##*/}
46 printf "%s (pacman) %s\n\n" "$cmd" "$myver"
47 if [[ $cmd == "repo-add" ]] ; then
48 printf "$(gettext "Usage: repo-add [options] <path-to-db> <package|delta> ...\n")"
49 printf "$(gettext "\
50 repo-add will update a package database by reading a package file.\n\
51 Multiple packages to add can be specified on the command line.\n\n")"
52 printf "$(gettext "Options:\n")"
53 printf "$(gettext " -d, --delta generate and add delta for package update\n")"
54 printf "$(gettext " -f, --files update database's file list\n")"
55 elif [[ $cmd == "repo-remove" ]] ; then
56 printf "$(gettext "Usage: repo-remove [options] <path-to-db> <packagename|delta> ...\n\n")"
57 printf "$(gettext "\
58 repo-remove will update a package database by removing the package name\n\
59 specified on the command line from the given repo database. Multiple\n\
60 packages to remove can be specified on the command line.\n\n")"
61 printf "$(gettext "Options:\n")"
63 printf "$(gettext " -q, --quiet minimize output\n")"
64 printf "$(gettext " -s, --sign sign database with GnuPG after update\n")"
65 printf "$(gettext " -k, --key <key> use the specified key to sign the database\n")"
66 printf "$(gettext " -v, --verify verify database's signature before update\n")"
67 printf "$(gettext "\n\
68 See %s(8) for more details and descriptions of the available options.\n\n")" $cmd
69 if [[ $cmd == "repo-add" ]] ; then
70 echo "$(gettext "Example: repo-add /path/to/repo.db.tar.gz pacman-3.0.0-1-i686.pkg.tar.gz")"
71 elif [[ $cmd == "repo-remove" ]] ; then
72 echo "$(gettext "Example: repo-remove /path/to/repo.db.tar.gz kernel26")"
76 version() {
77 cmd=${0##*/}
78 printf "%s (pacman) %s\n\n" "$cmd" "$myver"
79 printf "$(gettext "\
80 Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org>\n\n\
81 This is free software; see the source for copying conditions.\n\
82 There is NO WARRANTY, to the extent permitted by law.\n")"
85 # write a list entry
86 # arg1 - Entry name
87 # arg2 - List
88 # arg3 - File to write to
89 write_list_entry() {
90 if [[ -n $2 ]]; then
91 echo "%$1%" >>$3
92 echo -e $2 >>$3
96 find_pkgentry() {
97 local pkgname=$1
98 local pkgentry
99 for pkgentry in $tmpdir/$pkgname*; do
100 name=${pkgentry##*/}
101 if [[ ${name%-*-*} = $pkgname ]]; then
102 echo $pkgentry
103 return 0
105 done
106 return 1
109 # Get the package name from the delta filename
110 get_delta_pkgname() {
111 local tmp
113 tmp=${1##*/}
114 echo ${tmp%-*-*_to*}
117 # write a delta entry
118 # arg1 - path to delta file
119 db_write_delta() {
120 deltafile="$1"
121 pkgname="$(get_delta_pkgname $deltafile)"
123 pkgentry=$(find_pkgentry $pkgname)
124 if [[ -z $pkgentry ]]; then
125 error "$(gettext "No database entry for package '%s'.")" "$pkgname"
126 return 1
128 deltas="$pkgentry/deltas"
129 if [[ ! -f $deltas ]]; then
130 echo -e "%DELTAS%" >$deltas
132 # get md5sum and compressed size of package
133 md5sum="$(openssl dgst -md5 "$deltafile")"
134 md5sum="${md5sum##* }"
135 csize=$(@SIZECMD@ "$deltafile")
137 oldfile=$(xdelta3 printhdr $deltafile | grep "XDELTA filename (source)" | sed 's/.*: *//')
138 newfile=$(xdelta3 printhdr $deltafile | grep "XDELTA filename (output)" | sed 's/.*: *//')
140 if grep -q "$oldfile.*$newfile" $deltas; then
141 sed -i.backup "/$oldfile.*$newfile/d" $deltas && rm -f $deltas.backup
143 msg2 "$(gettext "Adding 'deltas' entry : %s -> %s")" "$oldfile" "$newfile"
144 echo ${deltafile##*/} $md5sum $csize $oldfile $newfile >> $deltas
146 return 0
147 } # end db_write_delta
149 # remove a delta entry
150 # arg1 - path to delta file
151 db_remove_delta() {
152 deltafile="$1"
153 filename=${deltafile##*/}
154 pkgname="$(get_delta_pkgname $deltafile)"
156 pkgentry=$(find_pkgentry $pkgname)
157 if [[ -z $pkgentry ]]; then
158 return 1
160 deltas="$pkgentry/deltas"
161 if [[ ! -f $deltas ]]; then
162 return 1
164 if grep -q "$filename" $deltas; then
165 sed -i.backup "/$filename/d" $deltas && rm -f $deltas.backup
166 msg2 "$(gettext "Removing existing entry '%s'...")" "$filename"
167 return 0
170 return 1
171 } # end db_remove_delta
173 check_gpg() {
174 if ! type -p gpg >/dev/null; then
175 error "$(gettext "Cannot find the gpg binary! Is gnupg installed?")"
176 exit 1 # $E_MISSING_PROGRAM
180 # sign the package database once repackaged
181 create_signature() {
182 (( ! SIGN )) && return
183 local dbfile="$1"
184 local ret=0
185 msg "$(gettext "Signing database...")"
187 local SIGNWITHKEY=""
188 if [[ -n $GPGKEY ]]; then
189 SIGNWITHKEY="-u ${GPGKEY}"
191 gpg --detach-sign --use-agent ${SIGNWITHKEY} "$dbfile" &>/dev/null || ret=$?
193 if (( ! ret )); then
194 msg2 "$(gettext "Created signature file %s.")" "$dbfile.sig"
195 else
196 warning "$(gettext "Failed to sign package database.")"
200 # verify the existing package database signature
201 verify_signature() {
202 (( ! VERIFY )) && return
203 local dbfile="$1"
204 local ret=0
205 msg "$(gettext "Verifying database signature...")"
207 if [[ ! -f $dbfile.sig ]]; then
208 warning "$(gettext "No existing signature found, skipping verification.")"
209 return
211 gpg --verify "$dbfile.sig" || ret=$?
212 if (( ! ret )); then
213 msg2 "$(gettext "Database signature file verified.")"
214 else
215 error "$(gettext "Database signature was NOT valid!")"
216 exit 1
220 # write an entry to the pacman database
221 # arg1 - path to package
222 db_write_entry() {
223 # blank out all variables
224 local pkgfile="$1"
225 local pkgname pkgver pkgdesc csize size url arch builddate packager \
226 _groups _licenses _replaces _depends _conflicts _provides _optdepends \
227 md5sum sha256sum pgpsig
229 local OLDIFS="$IFS"
230 # IFS (field separator) is only the newline character
231 IFS="
234 # read info from the zipped package
235 local line var val
236 for line in $(bsdtar -xOqf "$pkgfile" .PKGINFO |
237 grep -v '^#' | sed 's|\(\w*\)\s*=\s*\(.*\)|\1 \2|'); do
238 # bash awesomeness here- var is always one word, val is everything else
239 var=${line%% *}
240 val=${line#* }
241 declare $var="$val"
242 case "$var" in
243 group) _groups="$_groups$group\n" ;;
244 license) _licenses="$_licenses$license\n" ;;
245 replaces) _replaces="$_replaces$replaces\n" ;;
246 depend) _depends="$_depends$depend\n" ;;
247 conflict) _conflicts="$_conflicts$conflict\n" ;;
248 provides) _provides="$_provides$provides\n" ;;
249 optdepend) _optdepends="$_optdepends$optdepend\n" ;;
250 esac
251 done
253 IFS=$OLDIFS
255 csize=$(@SIZECMD@ "$pkgfile")
257 # compute checksums
258 msg2 "$(gettext "Computing checksums...")"
259 md5sum="$(openssl dgst -md5 "$pkgfile")"
260 md5sum="${md5sum##* }"
261 sha256sum="$(openssl dgst -sha256 "$pkgfile")"
262 sha256sum="${sha256sum##* }"
264 # compute base64'd PGP signature
265 if [[ -f "$pkgfile.sig" ]]; then
266 pgpsig=$(openssl base64 -in "$pkgfile.sig" | tr -d '\n')
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 pushd "$tmpdir" >/dev/null
276 if [[ -d $pkgname-$pkgver ]]; then
277 warning "$(gettext "An entry for '%s' already existed")" "$pkgname-$pkgver"
278 else
279 if (( DELTA )); then
280 pkgentry=$(find_pkgentry $pkgname)
281 if [[ -n $pkgentry ]]; then
282 local oldfilename=$(grep -A1 FILENAME $pkgentry/desc | tail -n1)
283 local oldfile="$(dirname $1)/$oldfilename"
288 # remove an existing entry if it exists, ignore failures
289 db_remove_entry "$pkgname"
291 # create package directory
292 mkdir "$pkgname-$pkgver"
293 pushd "$pkgname-$pkgver" >/dev/null
295 # restore an eventual deltas file
296 [[ -f ../$pkgname.deltas ]] && mv "../$pkgname.deltas" deltas
298 # create desc entry
299 msg2 "$(gettext "Creating '%s' db entry...")" 'desc'
300 echo -e "%FILENAME%\n${1##*/}\n" >>desc
301 echo -e "%NAME%\n$pkgname\n" >>desc
302 [[ -n $pkgbase ]] && echo -e "%BASE%\n$pkgbase\n" >>desc
303 echo -e "%VERSION%\n$pkgver\n" >>desc
304 [[ -n $pkgdesc ]] && echo -e "%DESC%\n$pkgdesc\n" >>desc
305 write_list_entry "GROUPS" "$_groups" "desc"
306 [[ -n $csize ]] && echo -e "%CSIZE%\n$csize\n" >>desc
307 [[ -n $size ]] && echo -e "%ISIZE%\n$size\n" >>desc
309 # add checksums
310 echo -e "%MD5SUM%\n$md5sum\n" >>desc
311 echo -e "%SHA256SUM%\n$sha256sum\n" >>desc
313 # add PGP sig
314 [[ -n $pgpsig ]] && echo -e "%PGPSIG%\n$pgpsig\n" >>desc
316 [[ -n $url ]] && echo -e "%URL%\n$url\n" >>desc
317 write_list_entry "LICENSE" "$_licenses" "desc"
318 [[ -n $arch ]] && echo -e "%ARCH%\n$arch\n" >>desc
319 [[ -n $builddate ]] && echo -e "%BUILDDATE%\n$builddate\n" >>desc
320 [[ -n $packager ]] && echo -e "%PACKAGER%\n$packager\n" >>desc
321 write_list_entry "REPLACES" "$_replaces" "desc"
323 # create depends entry
324 msg2 "$(gettext "Creating '%s' db entry...")" 'depends'
325 # create the file even if it will remain empty
326 touch "depends"
327 write_list_entry "DEPENDS" "$_depends" "depends"
328 write_list_entry "CONFLICTS" "$_conflicts" "depends"
329 write_list_entry "PROVIDES" "$_provides" "depends"
330 write_list_entry "OPTDEPENDS" "$_optdepends" "depends"
332 popd >/dev/null
333 popd >/dev/null
335 # create files file if wanted
336 if (( WITHFILES )); then
337 msg2 "$(gettext "Creating '%s' db entry...")" 'files'
338 local files_path="$tmpdir/$pkgname-$pkgver/files"
339 echo "%FILES%" >$files_path
340 bsdtar --exclude='^.*' -tf "$pkgfile" >>$files_path
343 # create a delta file
344 if (( DELTA )); then
345 if [[ -n $oldfilename ]]; then
346 if [[ -f $oldfile ]]; then
347 delta=$(pkgdelta -q $oldfile $1)
348 if [[ -f $delta ]]; then
349 db_write_delta $delta
351 else
352 warning "$(gettext "Old package file not found: %s")" "$oldfilename"
357 return 0
358 } # end db_write_entry
360 # remove existing entries from the DB
361 # arg1 - package name
362 db_remove_entry() {
363 local pkgname=$1
364 local notfound=1
365 local pkgentry=$(find_pkgentry $pkgname)
366 while [[ -n $pkgentry ]]; do
367 notfound=0
368 if [[ -f $pkgentry/deltas ]]; then
369 mv "$pkgentry/deltas" "$tmpdir/$pkgname.deltas"
371 msg2 "$(gettext "Removing existing entry '%s'...")" \
372 "${pkgentry##*/}"
373 rm -rf $pkgentry
374 pkgentry=$(find_pkgentry $pkgname)
375 done
376 return $notfound
377 } # end db_remove_entry
379 check_repo_db() {
380 local repodir
382 # ensure the path to the DB exists
383 if [[ "$LOCKFILE" == /* ]]; then
384 repodir=${LOCKFILE%/*}/
385 else
386 repodir=$PWD/$LOCKFILE
387 repodir=${repodir%/*}/
390 if [[ ! -d "$repodir" ]]; then
391 error "$(gettext "%s does not exist or is not a directory.")" "$repodir"
392 exit 1
395 # check lock file
396 if ( set -o noclobber; echo "$$" > "$LOCKFILE") 2> /dev/null; then
397 CLEAN_LOCK=1
398 else
399 error "$(gettext "Failed to acquire lockfile: %s.")" "$LOCKFILE"
400 [[ -f $LOCKFILE ]] && error "$(gettext "Held by process %s")" "$(cat $LOCKFILE)"
401 exit 1
404 if [[ -f $REPO_DB_FILE ]]; then
405 # there are two situations we can have here- a DB with some entries,
406 # or a DB with no contents at all.
407 if ! bsdtar -tqf "$REPO_DB_FILE" '*/desc' >/dev/null 2>&1; then
408 # check empty case
409 if [[ -n $(bsdtar -tqf "$REPO_DB_FILE" '*' 2>/dev/null) ]]; then
410 error "$(gettext "Repository file '%s' is not a proper pacman database.")" "$REPO_DB_FILE"
411 exit 1
414 verify_signature "$REPO_DB_FILE"
415 msg "$(gettext "Extracting database to a temporary location...")"
416 bsdtar -xf "$REPO_DB_FILE" -C "$tmpdir"
417 else
418 case "$cmd" in
419 repo-remove)
420 error "$(gettext "Repository file '%s' was not found.")" "$REPO_DB_FILE"
421 exit 1
423 repo-add)
424 # check if the file can be created (write permission, directory existence, etc)
425 if ! touch "$REPO_DB_FILE"; then
426 error "$(gettext "Repository file '%s' could not be created.")" "$REPO_DB_FILE"
427 exit 1
429 rm -f "$REPO_DB_FILE"
431 esac
435 add() {
436 if [[ ! -f $1 ]]; then
437 error "$(gettext "File '%s' not found.")" "$1"
438 return 1
441 if [[ ${1##*.} == "delta" ]]; then
442 deltafile=$1
443 msg "$(gettext "Adding delta '%s'")" "$deltafile"
444 if ! type xdelta3 &>/dev/null; then
445 error "$(gettext "Cannot find the xdelta3 binary! Is xdelta3 installed?")"
446 exit 1
448 if db_write_delta "$deltafile"; then
449 return 0
450 else
451 return 1
455 pkgfile=$1
456 if ! bsdtar -tqf "$pkgfile" .PKGINFO >/dev/null 2>&1; then
457 error "$(gettext "'%s' is not a package file, skipping")" "$pkgfile"
458 return 1
461 msg "$(gettext "Adding package '%s'")" "$pkgfile"
463 db_write_entry "$pkgfile"
466 remove() {
467 if [[ ${1##*.} == "delta" ]]; then
468 deltafile=$1
469 msg "$(gettext "Searching for delta '%s'...")" "$deltafile"
470 if db_remove_delta "$deltafile"; then
471 return 0
472 else
473 error "$(gettext "Delta matching '%s' not found.")" "$deltafile"
474 return 1
478 pkgname=$1
479 msg "$(gettext "Searching for package '%s'...")" "$pkgname"
481 if db_remove_entry "$pkgname"; then
482 rm -f "$tmpdir/$pkgname.deltas"
483 return 0
484 else
485 error "$(gettext "Package matching '%s' not found.")" "$pkgname"
486 return 1
490 trap_exit() {
491 echo
492 error "$@"
493 exit 1
496 clean_up() {
497 local exit_code=$?
499 [[ -d $tmpdir ]] && rm -rf "$tmpdir"
500 (( CLEAN_LOCK )) && [[ -f $LOCKFILE ]] && rm -f "$LOCKFILE"
502 exit $exit_code
505 # PROGRAM START
507 # determine whether we have gettext; make it a no-op if we do not
508 if ! type gettext &>/dev/null; then
509 gettext() {
510 echo "$@"
514 case "$1" in
515 -h|--help) usage; exit 0;;
516 -V|--version) version; exit 0;;
517 esac
519 # figure out what program we are
520 cmd=${0##*/}
521 if [[ $cmd != "repo-add" && $cmd != "repo-remove" ]]; then
522 error "$(gettext "Invalid command name '%s' specified.")" "$cmd"
523 exit 1
526 tmpdir=$(mktemp -d /tmp/repo-tools.XXXXXXXXXX) || (\
527 error "$(gettext "Cannot create temp directory for database building.")"; \
528 exit 1)
530 trap 'clean_up' EXIT
531 trap 'trap_exit "$(gettext "TERM signal caught. Exiting...")"' TERM HUP QUIT
532 trap 'trap_exit "$(gettext "Aborted by user! Exiting...")"' INT
533 trap 'trap_exit "$(gettext "An unknown error has occured. Exiting...")"' ERR
535 success=0
536 # parse arguments
537 while [[ $# > 0 ]]; do
538 case "$1" in
539 -q|--quiet) QUIET=1;;
540 -d|--delta) DELTA=1;;
541 -f|--files) WITHFILES=1;;
542 -s|--sign)
543 check_gpg
544 SIGN=1
545 if ! gpg --list-key ${GPGKEY} &>/dev/null; then
546 if [[ ! -z $GPGKEY ]]; then
547 error "$(gettext "The key ${GPGKEY} does not exist in your keyring.")"
548 else
549 error "$(gettext "There is no key in your keyring.")"
551 exit 1
554 -k|--key)
555 check_gpg
556 shift
557 GPGKEY="$1"
558 if ! gpg --list-key ${GPGKEY} &>/dev/null; then
559 error "$(gettext "The key ${GPGKEY} does not exist in your keyring.")"
560 exit 1
563 -v|--verify)
564 check_gpg
565 VERIFY=1
568 if [[ -z $REPO_DB_FILE ]]; then
569 REPO_DB_FILE="$1"
570 LOCKFILE="$REPO_DB_FILE.lck"
571 check_repo_db
572 else
573 case "$cmd" in
574 repo-add) add $1 && success=1 ;;
575 repo-remove) remove $1 && success=1 ;;
576 esac
579 esac
580 shift
581 done
583 # if at least one operation was a success, re-zip database
584 if (( success )); then
585 msg "$(gettext "Creating updated database file '%s'")" "$REPO_DB_FILE"
587 case "$REPO_DB_FILE" in
588 *.tar.gz) TAR_OPT="z" ;;
589 *.tar.bz2) TAR_OPT="j" ;;
590 *.tar.xz) TAR_OPT="J" ;;
591 *.tar) TAR_OPT="" ;;
592 *) warning "$(gettext "'%s' does not have a valid archive extension.")" \
593 "$REPO_DB_FILE" ;;
594 esac
596 filename=${REPO_DB_FILE##*/}
598 pushd "$tmpdir" >/dev/null
599 if [[ -n $(ls) ]]; then
600 bsdtar -c${TAR_OPT}f "$filename" *
601 else
602 # we have no packages remaining? zip up some emptyness
603 warning "$(gettext "No packages remain, creating empty database.")"
604 bsdtar -c${TAR_OPT}f "$filename" -T /dev/null
606 create_signature "$filename"
608 popd >/dev/null
610 [[ -f $REPO_DB_FILE ]] && mv -f "$REPO_DB_FILE" "${REPO_DB_FILE}.old"
611 [[ -f $REPO_DB_FILE.sig ]] && rm -f "$REPO_DB_FILE.sig"
612 [[ -f $tmpdir/$filename ]] && mv "$tmpdir/$filename" "$REPO_DB_FILE"
613 [[ -f $tmpdir/$filename.sig ]] && mv "$tmpdir/$filename.sig" "$REPO_DB_FILE.sig"
614 dblink="${REPO_DB_FILE%.tar*}"
615 target=${REPO_DB_FILE##*/}
616 ln -sf "$target" "$dblink" 2>/dev/null || \
617 ln -f "$target" "$dblink" 2>/dev/null || \
618 cp "$REPO_DB_FILE" "$dblink"
619 if [[ -f "$target.sig" ]]; then
620 ln -sf "$target.sig" "$dblink.sig" 2>/dev/null || \
621 ln -f "$target.sig" "$dblink.sig" 2>/dev/null || \
622 cp "$REPO_DB_FILE.sig" "$dblink.sig"
624 else
625 msg "$(gettext "No packages modified, nothing to do.")"
626 exit 1
629 exit 0
630 # vim: set ts=2 sw=2 noet: