doc/PKGBUILD: document libdeps
[pacman-ng.git] / scripts / repo-add.sh.in
blob820db369d3430464f07bcea95268e66aeb65921c
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 msg() {
42 (( QUIET )) && return
43 local mesg=$1; shift
44 printf "==> ${mesg}\n" "$@" >&1
47 msg2() {
48 (( QUIET )) && return
49 local mesg=$1; shift
50 printf " -> ${mesg}\n" "$@" >&1
53 warning() {
54 local mesg=$1; shift
55 printf "==> $(gettext "WARNING:") ${mesg}\n" "$@" >&2
58 error() {
59 local mesg=$1; shift
60 printf "==> $(gettext "ERROR:") ${mesg}\n" "$@" >&2
63 # print usage instructions
64 usage() {
65 cmd="$(basename $0)"
66 printf "%s (pacman) %s\n\n" "$cmd" "$myver"
67 if [[ $cmd == "repo-add" ]] ; then
68 printf "$(gettext "Usage: repo-add [options] <path-to-db> <package|delta> ...\n")"
69 printf "$(gettext "\
70 repo-add will update a package database by reading a package file.\n\
71 Multiple packages to add can be specified on the command line.\n\n")"
72 printf "$(gettext "Options:\n")"
73 printf "$(gettext " -d, --delta generate and add delta for package update\n")"
74 printf "$(gettext " -f, --files update database's file list\n")"
75 elif [[ $cmd == "repo-remove" ]] ; then
76 printf "$(gettext "Usage: repo-remove [options] <path-to-db> <packagename|delta> ...\n\n")"
77 printf "$(gettext "\
78 repo-remove will update a package database by removing the package name\n\
79 specified on the command line from the given repo database. Multiple\n\
80 packages to remove can be specified on the command line.\n\n")"
81 printf "$(gettext "Options:\n")"
83 printf "$(gettext " -q, --quiet minimize output\n")"
84 printf "$(gettext " -s, --sign sign database with GnuPG after update\n")"
85 printf "$(gettext " -k, --key <key> use the specified key to sign the database\n")"
86 printf "$(gettext " -v, --verify verify database's signature before update\n")"
87 printf "$(gettext "\n\
88 See %s(8) for more details and descriptions of the available options.\n\n")" $cmd
89 if [[ $cmd == "repo-add" ]] ; then
90 echo "$(gettext "Example: repo-add /path/to/repo.db.tar.gz pacman-3.0.0-1-i686.pkg.tar.gz")"
91 elif [[ $cmd == "repo-remove" ]] ; then
92 echo "$(gettext "Example: repo-remove /path/to/repo.db.tar.gz kernel26")"
96 version() {
97 cmd="$(basename $0)"
98 printf "%s (pacman) %s\n\n" "$cmd" "$myver"
99 printf "$(gettext "\
100 Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org>\n\n\
101 This is free software; see the source for copying conditions.\n\
102 There is NO WARRANTY, to the extent permitted by law.\n")"
105 # write a list entry
106 # arg1 - Entry name
107 # arg2 - List
108 # arg3 - File to write to
109 write_list_entry() {
110 if [[ -n $2 ]]; then
111 echo "%$1%" >>$3
112 echo -e $2 >>$3
116 find_pkgentry()
118 local pkgname=$1
119 local pkgentry
120 for pkgentry in $tmpdir/$pkgname*; do
121 name=${pkgentry##*/}
122 if [[ ${name%-*-*} = $pkgname ]]; then
123 echo $pkgentry
124 return 0
126 done
127 return 1
130 # Get the package name from the delta filename
131 get_delta_pkgname() {
132 local tmp
134 tmp=${1##*/}
135 echo ${tmp%-*-*_to*}
138 # write a delta entry
139 # arg1 - path to delta file
140 db_write_delta()
142 deltafile="$1"
143 pkgname="$(get_delta_pkgname $deltafile)"
145 pkgentry=$(find_pkgentry $pkgname)
146 if [[ -z $pkgentry ]]; then
147 error "$(gettext "No database entry for package '%s'.")" "$pkgname"
148 return 1
150 deltas="$pkgentry/deltas"
151 if [[ ! -f $deltas ]]; then
152 echo -e "%DELTAS%" >$deltas
154 # get md5sum and compressed size of package
155 md5sum="$(openssl dgst -md5 "$deltafile")"
156 md5sum="${md5sum##* }"
157 csize=$(@SIZECMD@ "$deltafile")
159 oldfile=$(xdelta3 printhdr $deltafile | grep "XDELTA filename (source)" | sed 's/.*: *//')
160 newfile=$(xdelta3 printhdr $deltafile | grep "XDELTA filename (output)" | sed 's/.*: *//')
162 if grep -q "$oldfile.*$newfile" $deltas; then
163 sed -i.backup "/$oldfile.*$newfile/d" $deltas && rm -f $deltas.backup
165 msg2 "$(gettext "Adding 'deltas' entry : %s -> %s")" "$oldfile" "$newfile"
166 echo ${deltafile##*/} $md5sum $csize $oldfile $newfile >> $deltas
168 return 0
169 } # end db_write_delta
171 # remove a delta entry
172 # arg1 - path to delta file
173 db_remove_delta()
175 deltafile="$1"
176 filename=${deltafile##*/}
177 pkgname="$(get_delta_pkgname $deltafile)"
179 pkgentry=$(find_pkgentry $pkgname)
180 if [[ -z $pkgentry ]]; then
181 return 1
183 deltas="$pkgentry/deltas"
184 if [[ ! -f $deltas ]]; then
185 return 1
187 if grep -q "$filename" $deltas; then
188 sed -i.backup "/$filename/d" $deltas && rm -f $deltas.backup
189 msg2 "$(gettext "Removing existing entry '%s'...")" "$filename"
190 return 0
193 return 1
194 } # end db_remove_delta
196 check_gpg() {
197 if ! type -p gpg >/dev/null; then
198 error "$(gettext "Cannot find the gpg binary! Is gnupg installed?")"
199 exit 1 # $E_MISSING_PROGRAM
203 # sign the package database once repackaged
204 create_signature() {
205 (( ! SIGN )) && return
206 local dbfile="$1"
207 local ret=0
208 msg "$(gettext "Signing database...")"
210 local SIGNWITHKEY=""
211 if [[ -n $GPGKEY ]]; then
212 SIGNWITHKEY="-u ${GPGKEY}"
214 gpg --detach-sign --use-agent ${SIGNWITHKEY} "$dbfile" &>/dev/null || ret=$?
216 if (( ! ret )); then
217 msg2 "$(gettext "Created signature file %s.")" "$dbfile.sig"
218 else
219 warning "$(gettext "Failed to sign package database.")"
223 # verify the existing package database signature
224 verify_signature() {
225 (( ! VERIFY )) && return
226 local dbfile="$1"
227 local ret=0
228 msg "$(gettext "Verifying database signature...")"
230 if [[ ! -f $dbfile.sig ]]; then
231 warning "$(gettext "No existing signature found, skipping verification.")"
232 return
234 gpg --verify "$dbfile.sig" || ret=$?
235 if (( ! ret )); then
236 msg2 "$(gettext "Database signature file verified.")"
237 else
238 error "$(gettext "Database signature was NOT valid!")"
239 exit 1
243 # write an entry to the pacman database
244 # arg1 - path to package
245 db_write_entry()
247 # blank out all variables
248 local pkgfile="$1"
249 local pkgname pkgver pkgdesc csize size url arch builddate packager \
250 _groups _licenses _replaces _depends _conflicts _provides _optdepends \
251 md5sum sha256sum pgpsig
253 local OLDIFS="$IFS"
254 # IFS (field separator) is only the newline character
255 IFS="
258 # read info from the zipped package
259 local line var val
260 for line in $(bsdtar -xOqf "$pkgfile" .PKGINFO |
261 grep -v '^#' | sed 's|\(\w*\)\s*=\s*\(.*\)|\1 \2|'); do
262 # bash awesomeness here- var is always one word, val is everything else
263 var=${line%% *}
264 val=${line#* }
265 declare $var="$val"
266 case "$var" in
267 group) _groups="$_groups$group\n" ;;
268 license) _licenses="$_licenses$license\n" ;;
269 replaces) _replaces="$_replaces$replaces\n" ;;
270 depend) _depends="$_depends$depend\n" ;;
271 conflict) _conflicts="$_conflicts$conflict\n" ;;
272 provides) _provides="$_provides$provides\n" ;;
273 optdepend) _optdepends="$_optdepends$optdepend\n" ;;
274 esac
275 done
277 IFS=$OLDIFS
279 csize=$(@SIZECMD@ "$pkgfile")
281 # compute checksums
282 msg2 "$(gettext "Computing checksums...")"
283 md5sum="$(openssl dgst -md5 "$pkgfile")"
284 md5sum="${md5sum##* }"
285 sha256sum="$(openssl dgst -sha256 "$pkgfile")"
286 sha256sum="${sha256sum##* }"
288 # compute base64'd PGP signature
289 if [[ -f "$pkgfile.sig" ]]; then
290 pgpsig=$(openssl base64 -in "$pkgfile.sig" | tr -d '\n')
293 # ensure $pkgname and $pkgver variables were found
294 if [[ -z $pkgname || -z $pkgver ]]; then
295 error "$(gettext "Invalid package file '%s'.")" "$pkgfile"
296 return 1
299 pushd "$tmpdir" >/dev/null
300 if [[ -d $pkgname-$pkgver ]]; then
301 warning "$(gettext "An entry for '%s' already existed")" "$pkgname-$pkgver"
302 else
303 if (( DELTA )); then
304 pkgentry=$(find_pkgentry $pkgname)
305 if [[ -n $pkgentry ]]; then
306 local oldfilename=$(grep -A1 FILENAME $pkgentry/desc | tail -n1)
307 local oldfile="$(dirname $1)/$oldfilename"
312 # remove an existing entry if it exists, ignore failures
313 db_remove_entry "$pkgname"
315 # create package directory
316 mkdir "$pkgname-$pkgver"
317 pushd "$pkgname-$pkgver" >/dev/null
319 # restore an eventual deltas file
320 [[ -f ../$pkgname.deltas ]] && mv "../$pkgname.deltas" deltas
322 # create desc entry
323 msg2 "$(gettext "Creating '%s' db entry...")" 'desc'
324 echo -e "%FILENAME%\n$(basename "$1")\n" >>desc
325 echo -e "%NAME%\n$pkgname\n" >>desc
326 [[ -n $pkgbase ]] && echo -e "%BASE%\n$pkgbase\n" >>desc
327 echo -e "%VERSION%\n$pkgver\n" >>desc
328 [[ -n $pkgdesc ]] && echo -e "%DESC%\n$pkgdesc\n" >>desc
329 write_list_entry "GROUPS" "$_groups" "desc"
330 [[ -n $csize ]] && echo -e "%CSIZE%\n$csize\n" >>desc
331 [[ -n $size ]] && echo -e "%ISIZE%\n$size\n" >>desc
333 # add checksums
334 echo -e "%MD5SUM%\n$md5sum\n" >>desc
335 echo -e "%SHA256SUM%\n$sha256sum\n" >>desc
337 # add PGP sig
338 [[ -n $pgpsig ]] && echo -e "%PGPSIG%\n$pgpsig\n" >>desc
340 [[ -n $url ]] && echo -e "%URL%\n$url\n" >>desc
341 write_list_entry "LICENSE" "$_licenses" "desc"
342 [[ -n $arch ]] && echo -e "%ARCH%\n$arch\n" >>desc
343 [[ -n $builddate ]] && echo -e "%BUILDDATE%\n$builddate\n" >>desc
344 [[ -n $packager ]] && echo -e "%PACKAGER%\n$packager\n" >>desc
345 write_list_entry "REPLACES" "$_replaces" "desc"
347 # create depends entry
348 msg2 "$(gettext "Creating '%s' db entry...")" 'depends'
349 # create the file even if it will remain empty
350 touch "depends"
351 write_list_entry "DEPENDS" "$_depends" "depends"
352 write_list_entry "CONFLICTS" "$_conflicts" "depends"
353 write_list_entry "PROVIDES" "$_provides" "depends"
354 write_list_entry "OPTDEPENDS" "$_optdepends" "depends"
356 popd >/dev/null
357 popd >/dev/null
359 # create files file if wanted
360 if (( WITHFILES )); then
361 msg2 "$(gettext "Creating '%s' db entry...")" 'files'
362 local files_path="$tmpdir/$pkgname-$pkgver/files"
363 echo "%FILES%" >$files_path
364 bsdtar --exclude='.*' -tf "$pkgfile" >>$files_path
367 # create a delta file
368 if (( DELTA )); then
369 if [[ -n $oldfilename ]]; then
370 if [[ -f $oldfile ]]; then
371 delta=$(pkgdelta -q $oldfile $1)
372 if [[ -f $delta ]]; then
373 db_write_delta $delta
375 else
376 warning "$(gettext "Old package file not found: %s")" "$oldfilename"
381 return 0
382 } # end db_write_entry
384 # remove existing entries from the DB
385 # arg1 - package name
386 db_remove_entry() {
387 local pkgname=$1
388 local notfound=1
389 local pkgentry=$(find_pkgentry $pkgname)
390 while [[ -n $pkgentry ]]; do
391 notfound=0
392 if [[ -f $pkgentry/deltas ]]; then
393 mv "$pkgentry/deltas" "$tmpdir/$pkgname.deltas"
395 msg2 "$(gettext "Removing existing entry '%s'...")" \
396 "$(basename $pkgentry)"
397 rm -rf $pkgentry
398 pkgentry=$(find_pkgentry $pkgname)
399 done
400 return $notfound
401 } # end db_remove_entry
403 check_repo_db()
405 # check lock file
406 if ( set -o noclobber; echo "$$" > "$LOCKFILE") 2> /dev/null; then
407 CLEAN_LOCK=1
408 else
409 error "$(gettext "Failed to acquire lockfile: %s.")" "$LOCKFILE"
410 [[ -f $LOCKFILE ]] && error "$(gettext "Held by process %s")" "$(cat $LOCKFILE)"
411 exit 1
414 if [[ -f $REPO_DB_FILE ]]; then
415 # there are two situations we can have here- a DB with some entries,
416 # or a DB with no contents at all.
417 if ! bsdtar -tqf "$REPO_DB_FILE" '*/desc' >/dev/null 2>&1; then
418 # check empty case
419 if [[ -n $(bsdtar -tqf "$REPO_DB_FILE" '*' 2>/dev/null) ]]; then
420 error "$(gettext "Repository file '%s' is not a proper pacman database.")" "$REPO_DB_FILE"
421 exit 1
424 verify_signature "$REPO_DB_FILE"
425 msg "$(gettext "Extracting database to a temporary location...")"
426 bsdtar -xf "$REPO_DB_FILE" -C "$tmpdir"
427 else
428 case "$cmd" in
429 repo-remove)
430 error "$(gettext "Repository file '%s' was not found.")" "$REPO_DB_FILE"
431 exit 1
433 repo-add)
434 # check if the file can be created (write permission, directory existence, etc)
435 if ! touch "$REPO_DB_FILE"; then
436 error "$(gettext "Repository file '%s' could not be created.")" "$REPO_DB_FILE"
437 exit 1
439 rm -f "$REPO_DB_FILE"
441 esac
445 add()
447 if [[ ! -f $1 ]]; then
448 error "$(gettext "File '%s' not found.")" "$1"
449 return 1
452 if [[ ${1##*.} == "delta" ]]; then
453 deltafile=$1
454 msg "$(gettext "Adding delta '%s'")" "$deltafile"
455 if ! type xdelta3 &>/dev/null; then
456 error "$(gettext "Cannot find the xdelta3 binary! Is xdelta3 installed?")"
457 exit 1
459 if db_write_delta "$deltafile"; then
460 return 0
461 else
462 return 1
466 pkgfile=$1
467 if ! bsdtar -tqf "$pkgfile" .PKGINFO >/dev/null 2>&1; then
468 error "$(gettext "'%s' is not a package file, skipping")" "$pkgfile"
469 return 1
472 msg "$(gettext "Adding package '%s'")" "$pkgfile"
474 db_write_entry "$pkgfile"
477 remove()
479 if [[ ${1##*.} == "delta" ]]; then
480 deltafile=$1
481 msg "$(gettext "Searching for delta '%s'...")" "$deltafile"
482 if db_remove_delta "$deltafile"; then
483 return 0
484 else
485 error "$(gettext "Delta matching '%s' not found.")" "$deltafile"
486 return 1
490 pkgname=$1
491 msg "$(gettext "Searching for package '%s'...")" "$pkgname"
493 if db_remove_entry "$pkgname"; then
494 rm -f "$tmpdir/$pkgname.deltas"
495 return 0
496 else
497 error "$(gettext "Package matching '%s' not found.")" "$pkgname"
498 return 1
502 trap_exit()
504 echo
505 error "$@"
506 exit 1
509 clean_up() {
510 local exit_code=$?
512 [[ -d $tmpdir ]] && rm -rf "$tmpdir"
513 (( CLEAN_LOCK )) && [[ -f $LOCKFILE ]] && rm -f "$LOCKFILE"
515 exit $exit_code
518 # PROGRAM START
520 # determine whether we have gettext; make it a no-op if we do not
521 if ! type gettext &>/dev/null; then
522 gettext() {
523 echo "$@"
527 case "$1" in
528 -h|--help) usage; exit 0;;
529 -V|--version) version; exit 0;;
530 esac
532 # figure out what program we are
533 cmd="$(basename $0)"
534 if [[ $cmd != "repo-add" && $cmd != "repo-remove" ]]; then
535 error "$(gettext "Invalid command name '%s' specified.")" "$cmd"
536 exit 1
539 tmpdir=$(mktemp -d /tmp/repo-tools.XXXXXXXXXX) || (\
540 error "$(gettext "Cannot create temp directory for database building.")"; \
541 exit 1)
543 trap 'clean_up' EXIT
544 trap 'trap_exit "$(gettext "TERM signal caught. Exiting...")"' TERM HUP QUIT
545 trap 'trap_exit "$(gettext "Aborted by user! Exiting...")"' INT
546 trap 'trap_exit "$(gettext "An unknown error has occured. Exiting...")"' ERR
548 success=0
549 # parse arguments
550 while [[ $# > 0 ]]; do
551 case "$1" in
552 -q|--quiet) QUIET=1;;
553 -d|--delta) DELTA=1;;
554 -f|--files) WITHFILES=1;;
555 -s|--sign)
556 check_gpg
557 SIGN=1
558 if ! gpg --list-key ${GPGKEY} &>/dev/null; then
559 if [[ ! -z $GPGKEY ]]; then
560 error "$(gettext "The key ${GPGKEY} does not exist in your keyring.")"
561 else
562 error "$(gettext "There is no key in your keyring.")"
564 exit 1
567 -k|--key)
568 check_gpg
569 shift
570 GPGKEY="$1"
571 if ! gpg --list-key ${GPGKEY} &>/dev/null; then
572 error "$(gettext "The key ${GPGKEY} does not exist in your keyring.")"
573 exit 1
576 -v|--verify)
577 check_gpg
578 VERIFY=1
581 if [[ -z $REPO_DB_FILE ]]; then
582 REPO_DB_FILE="$1"
583 LOCKFILE="$REPO_DB_FILE.lck"
584 check_repo_db
585 else
586 case "$cmd" in
587 repo-add) add $1 && success=1 ;;
588 repo-remove) remove $1 && success=1 ;;
589 esac
592 esac
593 shift
594 done
596 # if at least one operation was a success, re-zip database
597 if (( success )); then
598 msg "$(gettext "Creating updated database file '%s'")" "$REPO_DB_FILE"
600 case "$REPO_DB_FILE" in
601 *tar.gz) TAR_OPT="z" ;;
602 *tar.bz2) TAR_OPT="j" ;;
603 *tar.xz) TAR_OPT="J" ;;
604 *) warning "$(gettext "'%s' does not have a valid archive extension.")" \
605 "$REPO_DB_FILE" ;;
606 esac
608 filename=$(basename "$REPO_DB_FILE")
610 pushd "$tmpdir" >/dev/null
611 if [[ -n $(ls) ]]; then
612 bsdtar -c${TAR_OPT}f "$filename" *
613 else
614 # we have no packages remaining? zip up some emptyness
615 warning "$(gettext "No packages remain, creating empty database.")"
616 bsdtar -c${TAR_OPT}f "$filename" -T /dev/null
618 create_signature "$filename"
620 popd >/dev/null
622 [[ -f $REPO_DB_FILE ]] && mv -f "$REPO_DB_FILE" "${REPO_DB_FILE}.old"
623 [[ -f $REPO_DB_FILE.sig ]] && rm -f "$REPO_DB_FILE.sig"
624 [[ -f $tmpdir/$filename ]] && mv "$tmpdir/$filename" "$REPO_DB_FILE"
625 [[ -f $tmpdir/$filename.sig ]] && mv "$tmpdir/$filename.sig" "$REPO_DB_FILE.sig"
626 dblink="${REPO_DB_FILE%.tar.*}"
627 target=${REPO_DB_FILE##*/}
628 ln -sf "$target" "$dblink" 2>/dev/null || \
629 ln -f "$target" "$dblink" 2>/dev/null || \
630 cp "$REPO_DB_FILE" "$dblink"
631 if [[ -f "$target.sig" ]]; then
632 ln -sf "$target.sig" "$dblink.sig" 2>/dev/null || \
633 ln -f "$target.sig" "$dblink.sig" 2>/dev/null || \
634 cp "$REPO_DB_FILE.sig" "$dblink.sig"
636 else
637 msg "$(gettext "No packages modified, nothing to do.")"
638 exit 1
641 exit 0
642 # vim: set ts=2 sw=2 noet: