repo-add: only attempt to create deltas when asked
[pacman-ng.git] / scripts / repo-add.sh.in
blobc3db7d9f45fabe31abb09111c8e8fceda10466fe
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-2008 Aaron Griffin <aaron@archlinux.org>
8 # Copyright (c) 2007-2008 Dan McGee <dan@archlinux.org>
10 # This program is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with this program. If not, see <http://www.gnu.org/licenses/>.
23 # gettext initialization
24 export TEXTDOMAIN='pacman'
25 export TEXTDOMAINDIR='@localedir@'
27 myver='@PACKAGE_VERSION@'
28 confdir='@sysconfdir@'
30 QUIET=0
31 DELTA=0
32 WITHFILES=0
33 REPO_DB_FILE=
34 LOCKFILE=
35 CLEAN_LOCK=0
37 # ensure we have a sane umask set
38 umask 0022
40 msg() {
41 (( QUIET )) && return
42 local mesg=$1; shift
43 printf "==> ${mesg}\n" "$@" >&1
46 msg2() {
47 (( QUIET )) && return
48 local mesg=$1; shift
49 printf " -> ${mesg}\n" "$@" >&1
52 warning() {
53 local mesg=$1; shift
54 printf "==> $(gettext "WARNING:") ${mesg}\n" "$@" >&2
57 error() {
58 local mesg=$1; shift
59 printf "==> $(gettext "ERROR:") ${mesg}\n" "$@" >&2
62 # print usage instructions
63 usage() {
64 printf "repo-add, repo-remove (pacman) %s\n\n" "$myver"
65 printf "$(gettext "Usage: repo-add [-d] [-f] [-q] <path-to-db> <package|delta> ...\n")"
66 printf "$(gettext "Usage: repo-remove [-q] <path-to-db> <packagename|delta> ...\n\n")"
67 printf "$(gettext "\
68 repo-add will update a package database by reading a package file.\n\
69 Multiple packages to add can be specified on the command line.\n\n")"
70 printf "$(gettext "\
71 repo-remove will update a package database by removing the package name\n\
72 specified on the command line from the given repo database. Multiple\n\
73 packages to remove can be specified on the command line.\n\n")"
74 printf "$(gettext "\
75 Use the -q/--quiet flag to minimize output to basic messages, warnings,\n\
76 and errors.\n\n")"
77 printf "$(gettext "\
78 Use the -d/--delta flag to automatically generate and add a delta file\n\
79 between the old entry and the new one, if the old package file is found\n\
80 next to the new one.\n\n")"
81 printf "$(gettext "\
82 Use the -f/--files flag to update a database including file entries.\n\n")"
83 echo "$(gettext "Example: repo-add /path/to/repo.db.tar.gz pacman-3.0.0.pkg.tar.gz")"
84 echo "$(gettext "Example: repo-remove /path/to/repo.db.tar.gz kernel26")"
87 version() {
88 printf "repo-add, repo-remove (pacman) %s\n\n" "$myver"
89 printf "$(gettext "\
90 Copyright (C) 2006-2008 Aaron Griffin <aaron@archlinux.org>.\n\
91 Copyright (c) 2007-2008 Dan McGee <dan@archlinux.org>.\n\n\
92 This is free software; see the source for copying conditions.\n\
93 There is NO WARRANTY, to the extent permitted by law.\n")"
96 # write a list entry
97 # arg1 - Entry name
98 # arg2 - List
99 # arg3 - File to write to
100 write_list_entry() {
101 if [[ -n $2 ]]; then
102 echo "%$1%" >>$3
103 echo -e $2 >>$3
107 find_pkgentry()
109 local pkgname=$1
110 local pkgentry
111 for pkgentry in $tmpdir/$pkgname*; do
112 name=${pkgentry##*/}
113 if [[ ${name%-*-*} = $pkgname ]]; then
114 echo $pkgentry
115 return 0
117 done
118 return 1
121 # Get the package name from the delta filename
122 get_delta_pkgname() {
123 local tmp
125 tmp=${1##*/}
126 echo ${tmp%-*-*_to*}
129 # write a delta entry
130 # arg1 - path to delta file
131 db_write_delta()
133 deltafile="$1"
134 pkgname="$(get_delta_pkgname $deltafile)"
136 pkgentry=$(find_pkgentry $pkgname)
137 if [[ -z $pkgentry ]]; then
138 error "$(gettext "No database entry for package '%s'.")" "$pkgname"
139 return 1
141 deltas="$pkgentry/deltas"
142 if [[ ! -f $deltas ]]; then
143 echo -e "%DELTAS%" >$deltas
145 # get md5sum and compressed size of package
146 md5sum="$(openssl dgst -md5 "$deltafile")"
147 md5sum="${md5sum##* }"
148 csize=$(@SIZECMD@ "$deltafile")
150 oldfile=$(xdelta3 printhdr $deltafile | grep "XDELTA filename (source)" | sed 's/.*: *//')
151 newfile=$(xdelta3 printhdr $deltafile | grep "XDELTA filename (output)" | sed 's/.*: *//')
153 if grep -q "$oldfile.*$newfile" $deltas; then
154 sed -i.backup "/$oldfile.*$newfile/d" $deltas && rm -f $deltas.backup
156 msg2 "$(gettext "Adding 'deltas' entry : %s -> %s")" "$oldfile" "$newfile"
157 echo ${deltafile##*/} $md5sum $csize $oldfile $newfile >> $deltas
159 return 0
160 } # end db_write_delta
162 # remove a delta entry
163 # arg1 - path to delta file
164 db_remove_delta()
166 deltafile="$1"
167 filename=${deltafile##*/}
168 pkgname="$(get_delta_pkgname $deltafile)"
170 pkgentry=$(find_pkgentry $pkgname)
171 if [[ -z $pkgentry ]]; then
172 return 1
174 deltas="$pkgentry/deltas"
175 if [[ ! -f $deltas ]]; then
176 return 1
178 if grep -q "$filename" $deltas; then
179 sed -i.backup "/$filename/d" $deltas && rm -f $deltas.backup
180 msg2 "$(gettext "Removing existing entry '%s'...")" "$filename"
181 return 0
184 return 1
185 } # end db_remove_delta
187 # write an entry to the pacman database
188 # arg1 - path to package
189 db_write_entry()
191 # blank out all variables
192 local pkgfile="$1"
193 local pkgname pkgver pkgdesc epoch csize size md5sum url arch builddate packager force \
194 _groups _licenses _replaces _depends _conflicts _provides _optdepends
196 local OLDIFS="$IFS"
197 # IFS (field separator) is only the newline character
198 IFS="
201 # read info from the zipped package
202 local line var val
203 for line in $(bsdtar -xOqf "$pkgfile" .PKGINFO |
204 grep -v '^#' | sed 's|\(\w*\)\s*=\s*\(.*\)|\1 \2|'); do
205 # bash awesomeness here- var is always one word, val is everything else
206 var=${line%% *}
207 val=${line#* }
208 declare $var="$val"
209 case "$var" in
210 group) _groups="$_groups$group\n" ;;
211 license) _licenses="$_licenses$license\n" ;;
212 replaces) _replaces="$_replaces$replaces\n" ;;
213 depend) _depends="$_depends$depend\n" ;;
214 conflict) _conflicts="$_conflicts$conflict\n" ;;
215 provides) _provides="$_provides$provides\n" ;;
216 optdepend) _optdepends="$_optdepends$optdepend\n" ;;
217 esac
218 done
220 IFS=$OLDIFS
222 # get md5sum and compressed size of package
223 md5sum="$(openssl dgst -md5 "$pkgfile")"
224 md5sum="${md5sum##* }"
225 csize=$(@SIZECMD@ "$pkgfile")
227 # ensure $pkgname and $pkgver variables were found
228 if [[ -z $pkgname || -z $pkgver ]]; then
229 error "$(gettext "Invalid package file '%s'.")" "$pkgfile"
230 return 1
233 pushd "$tmpdir" >/dev/null
234 if [[ -d $pkgname-$pkgver ]]; then
235 warning "$(gettext "An entry for '%s' already existed")" "$pkgname-$pkgver"
236 else
237 if (( DELTA )); then
238 pkgentry=$(find_pkgentry $pkgname)
239 if [[ -n $pkgentry ]]; then
240 local oldfilename=$(grep -A1 FILENAME $pkgentry/desc | tail -n1)
241 local oldfile="$(dirname $1)/$oldfilename"
246 # remove an existing entry if it exists, ignore failures
247 db_remove_entry "$pkgname"
249 # create package directory
250 mkdir "$pkgname-$pkgver"
251 pushd "$pkgname-$pkgver" >/dev/null
253 # restore an eventual deltas file
254 [[ -f ../$pkgname.deltas ]] && mv "../$pkgname.deltas" deltas
256 # create desc entry
257 msg2 "$(gettext "Creating 'desc' db entry...")"
258 echo -e "%FILENAME%\n$(basename "$1")\n" >>desc
259 echo -e "%NAME%\n$pkgname\n" >>desc
260 [[ -n $pkgbase ]] && echo -e "%BASE%\n$pkgbase\n" >>desc
261 echo -e "%VERSION%\n$pkgver\n" >>desc
262 [[ -n $pkgdesc ]] && echo -e "%DESC%\n$pkgdesc\n" >>desc
263 write_list_entry "GROUPS" "$_groups" "desc"
264 [[ -n $csize ]] && echo -e "%CSIZE%\n$csize\n" >>desc
265 [[ -n $size ]] && echo -e "%ISIZE%\n$size\n" >>desc
267 # compute checksums
268 msg2 "$(gettext "Computing md5 checksums...")"
269 echo -e "%MD5SUM%\n$md5sum\n" >>desc
271 [[ -n $url ]] && echo -e "%URL%\n$url\n" >>desc
272 write_list_entry "LICENSE" "$_licenses" "desc"
273 [[ -n $arch ]] && echo -e "%ARCH%\n$arch\n" >>desc
274 [[ -n $builddate ]] && echo -e "%BUILDDATE%\n$builddate\n" >>desc
275 [[ -n $packager ]] && echo -e "%PACKAGER%\n$packager\n" >>desc
276 write_list_entry "REPLACES" "$_replaces" "desc"
277 # remain backward-compatible for now; put a force entry in the database
278 if [[ -n $epoch ]]; then
279 echo -e "%EPOCH%\n#epoch\n" >>desc
280 echo -e "%FORCE%\n" >>desc
282 [[ -n $force ]] && echo -e "%FORCE%\n" >>desc
284 # create depends entry
285 msg2 "$(gettext "Creating 'depends' db entry...")"
286 # create the file even if it will remain empty
287 touch "depends"
288 write_list_entry "DEPENDS" "$_depends" "depends"
289 write_list_entry "CONFLICTS" "$_conflicts" "depends"
290 write_list_entry "PROVIDES" "$_provides" "depends"
291 write_list_entry "OPTDEPENDS" "$_optdepends" "depends"
293 popd >/dev/null
294 popd >/dev/null
296 # create files file if wanted
297 if (( WITHFILES )); then
298 msg2 "$(gettext "Creating 'files' db entry...")"
299 local files_path="$tmpdir/$pkgname-$pkgver/files"
300 echo "%FILES%" >$files_path
301 bsdtar --exclude='.*' -tf "$pkgfile" >>$files_path
304 # create a delta file
305 if (( DELTA )); then
306 if [[ -n $oldfilename ]]; then
307 if [[ -f $oldfile ]]; then
308 delta=$(pkgdelta -q $oldfile $1)
309 if [[ -f $delta ]]; then
310 db_write_delta $delta
312 else
313 warning "$(gettext "Old package file not found: %s")" "$oldfilename"
318 return 0
319 } # end db_write_entry
321 # remove existing entries from the DB
322 # arg1 - package name
323 db_remove_entry() {
324 local pkgname=$1
325 local notfound=1
326 local pkgentry=$(find_pkgentry $pkgname)
327 while [[ -n $pkgentry ]]; do
328 notfound=0
329 if [[ -f $pkgentry/deltas ]]; then
330 mv "$pkgentry/deltas" "$tmpdir/$pkgname.deltas"
332 msg2 "$(gettext "Removing existing entry '%s'...")" \
333 "$(basename $pkgentry)"
334 rm -rf $pkgentry
335 pkgentry=$(find_pkgentry $pkgname)
336 done
337 return $notfound
338 } # end db_remove_entry
340 check_repo_db()
342 # check lock file
343 if ( set -o noclobber; echo "$$" > "$LOCKFILE") 2> /dev/null; then
344 CLEAN_LOCK=1
345 else
346 error "$(gettext "Failed to acquire lockfile: %s.")" "$LOCKFILE"
347 [[ -f $LOCKFILE ]] && error "$(gettext "Held by process %s")" "$(cat $LOCKFILE)"
348 exit 1
351 if [[ -f $REPO_DB_FILE ]]; then
352 # there are two situations we can have here- a DB with some entries,
353 # or a DB with no contents at all.
354 if ! bsdtar -tqf "$REPO_DB_FILE" '*/desc' >/dev/null 2>&1; then
355 # check empty case
356 if [[ -n $(bsdtar -tqf "$REPO_DB_FILE" '*' 2>/dev/null) ]]; then
357 error "$(gettext "Repository file '%s' is not a proper pacman database.")" "$REPO_DB_FILE"
358 exit 1
361 msg "$(gettext "Extracting database to a temporary location...")"
362 bsdtar -xf "$REPO_DB_FILE" -C "$tmpdir"
363 else
364 case "$cmd" in
365 repo-remove)
366 error "$(gettext "Repository file '%s' was not found.")" "$REPO_DB_FILE"
367 exit 1
369 repo-add)
370 # check if the file can be created (write permission, directory existence, etc)
371 if ! touch "$REPO_DB_FILE"; then
372 error "$(gettext "Repository file '%s' could not be created.")" "$REPO_DB_FILE"
373 exit 1
375 rm -f "$REPO_DB_FILE"
377 esac
381 add()
383 if [[ ! -f $1 ]]; then
384 error "$(gettext "File '%s' not found.")" "$1"
385 return 1
388 if [[ ${1##*.} == "delta" ]]; then
389 deltafile=$1
390 msg "$(gettext "Adding delta '%s'")" "$deltafile"
391 if ! type xdelta3 &>/dev/null; then
392 error "$(gettext "Cannot find the xdelta3 binary! Is xdelta3 installed?")"
393 exit 1
395 if db_write_delta "$deltafile"; then
396 return 0
397 else
398 return 1
402 pkgfile=$1
403 if ! bsdtar -tqf "$pkgfile" .PKGINFO >/dev/null 2>&1; then
404 error "$(gettext "'%s' is not a package file, skipping")" "$pkgfile"
405 return 1
408 msg "$(gettext "Adding package '%s'")" "$pkgfile"
410 db_write_entry "$pkgfile"
413 remove()
415 if [[ ${1##*.} == "delta" ]]; then
416 deltafile=$1
417 msg "$(gettext "Searching for delta '%s'...")" "$deltafile"
418 if db_remove_delta "$deltafile"; then
419 return 0
420 else
421 error "$(gettext "Delta matching '%s' not found.")" "$deltafile"
422 return 1
426 pkgname=$1
427 msg "$(gettext "Searching for package '%s'...")" "$pkgname"
429 if db_remove_entry "$pkgname"; then
430 rm -f "$tmpdir/$pkgname.deltas"
431 return 0
432 else
433 error "$(gettext "Package matching '%s' not found.")" "$pkgname"
434 return 1
438 trap_exit()
440 echo
441 error "$@"
442 exit 1
445 clean_up() {
446 local exit_code=$?
448 [[ -d $tmpdir ]] && rm -rf "$tmpdir"
449 (( CLEAN_LOCK )) && [[ -f $LOCKFILE ]] && rm -f "$LOCKFILE"
451 exit $exit_code
454 # PROGRAM START
456 # determine whether we have gettext; make it a no-op if we do not
457 if ! type gettext &>/dev/null; then
458 gettext() {
459 echo "$@"
463 case "$1" in
464 -h|--help) usage; exit 0;;
465 -V|--version) version; exit 0;;
466 esac
468 # figure out what program we are
469 cmd="$(basename $0)"
470 if [[ $cmd != "repo-add" && $cmd != "repo-remove" ]]; then
471 error "$(gettext "Invalid command name '%s' specified.")" "$cmd"
472 exit 1
475 tmpdir=$(mktemp -d /tmp/repo-tools.XXXXXXXXXX) || (\
476 error "$(gettext "Cannot create temp directory for database building.")"; \
477 exit 1)
479 trap 'clean_up' EXIT
480 trap 'trap_exit "$(gettext "TERM signal caught. Exiting...")"' TERM HUP QUIT
481 trap 'trap_exit "$(gettext "Aborted by user! Exiting...")"' INT
482 trap 'trap_exit "$(gettext "An unknown error has occured. Exiting...")"' ERR
484 success=0
485 # parse arguments
486 for arg in "$@"; do
487 case "$arg" in
488 -q|--quiet) QUIET=1;;
489 -d|--delta) DELTA=1;;
490 -f|--files) WITHFILES=1;;
492 if [[ -z $REPO_DB_FILE ]]; then
493 REPO_DB_FILE="$arg"
494 LOCKFILE="$REPO_DB_FILE.lck"
495 check_repo_db
496 else
497 case "$cmd" in
498 repo-add) add $arg && success=1 ;;
499 repo-remove) remove $arg && success=1 ;;
500 esac
503 esac
504 done
506 # if at least one operation was a success, re-zip database
507 if (( success )); then
508 msg "$(gettext "Creating updated database file '%s'")" "$REPO_DB_FILE"
510 case "$REPO_DB_FILE" in
511 *tar.gz) TAR_OPT="z" ;;
512 *tar.bz2) TAR_OPT="j" ;;
513 *tar.xz) TAR_OPT="J" ;;
514 *) warning "$(gettext "'%s' does not have a valid archive extension.")" \
515 "$REPO_DB_FILE" ;;
516 esac
518 filename=$(basename "$REPO_DB_FILE")
520 pushd "$tmpdir" >/dev/null
521 if [[ -n $(ls) ]]; then
522 bsdtar -c${TAR_OPT}f "$filename" *
523 else
524 # we have no packages remaining? zip up some emptyness
525 warning "$(gettext "No packages remain, creating empty database.")"
526 bsdtar -c${TAR_OPT}f "$filename" -T /dev/null
528 popd >/dev/null
530 [[ -f $REPO_DB_FILE ]] && mv -f "$REPO_DB_FILE" "${REPO_DB_FILE}.old"
531 [[ -f $tmpdir/$filename ]] && mv "$tmpdir/$filename" "$REPO_DB_FILE"
532 dblink="${REPO_DB_FILE%.tar.*}"
533 target=${REPO_DB_FILE##*/}
534 ln -sf "$target" "$dblink" 2>/dev/null || \
535 ln -f "$target" "$dblink" 2>/dev/null || \
536 cp "$REPO_DB_FILE" "$dblink"
537 else
538 msg "$(gettext "No packages modified, nothing to do.")"
539 exit 1
542 exit 0
543 # vim: set ts=2 sw=2 noet: