Use sysconfdir, localstatedir, BASH instead of hardcoded values
[pacman-ng.git] / scripts / repo-add.sh.in
blob9bc70696e509dc38dafb6ac86f8d24fd64c15483
1 #!@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-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 REPO_DB_FILE=
33 LOCKFILE=
34 CLEAN_LOCK=0
35 startdir="$PWD"
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] [-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 echo "$(gettext "Example: repo-add /path/to/repo.db.tar.gz pacman-3.0.0.pkg.tar.gz")"
82 echo "$(gettext "Example: repo-remove /path/to/repo.db.tar.gz kernel26")"
85 version() {
86 printf "repo-add, repo-remove (pacman) %s\n\n" "$myver"
87 printf "$(gettext "\
88 Copyright (C) 2006-2008 Aaron Griffin <aaron@archlinux.org>.\n\
89 Copyright (c) 2007-2008 Dan McGee <dan@archlinux.org>.\n\n\
90 This is free software; see the source for copying conditions.\n\
91 There is NO WARRANTY, to the extent permitted by law.\n")"
94 # write a list entry
95 # arg1 - Entry name
96 # arg2 - List
97 # arg3 - File to write to
98 write_list_entry() {
99 if [[ -n $2 ]]; then
100 echo "%$1%" >>$3
101 echo -e $2 >>$3
105 find_pkgentry()
107 local pkgname=$1
108 local pkgentry
109 for pkgentry in $tmpdir/$pkgname*; do
110 name=${pkgentry##*/}
111 if [[ ${name%-*-*} = $pkgname ]]; then
112 echo $pkgentry
113 return 0
115 done
116 return 1
119 # Get the package name from the delta filename
120 get_delta_pkgname() {
121 local tmp
123 tmp=${1##*/}
124 echo ${tmp%-*-*_to*}
127 # write a delta entry
128 # arg1 - path to delta file
129 db_write_delta()
131 deltafile="$1"
132 pkgname="$(get_delta_pkgname $deltafile)"
134 pkgentry=$(find_pkgentry $pkgname)
135 if [[ -z $pkgentry ]]; then
136 error "$(gettext "No database entry for package '%s'.")" "$pkgname"
137 return 1
139 deltas="$pkgentry/deltas"
140 if [[ ! -f $deltas ]]; then
141 echo -e "%DELTAS%" >$deltas
143 # get md5sum and compressed size of package
144 md5sum="$(openssl dgst -md5 "$deltafile")"
145 md5sum="${md5sum##* }"
146 csize=$(@SIZECMD@ "$deltafile")
148 oldfile=$(xdelta3 printhdr $deltafile | grep "XDELTA filename (source)" | sed 's/.*: *//')
149 newfile=$(xdelta3 printhdr $deltafile | grep "XDELTA filename (output)" | sed 's/.*: *//')
151 if grep -q "$oldfile.*$newfile" $deltas; then
152 sed -i.backup "/$oldfile.*$newfile/d" $deltas && rm -f $deltas.backup
154 msg2 "$(gettext "Adding 'deltas' entry : %s -> %s")" "$oldfile" "$newfile"
155 echo ${deltafile##*/} $md5sum $csize $oldfile $newfile >> $deltas
157 return 0
158 } # end db_write_delta
160 # remove a delta entry
161 # arg1 - path to delta file
162 db_remove_delta()
164 deltafile="$1"
165 filename=${deltafile##*/}
166 pkgname="$(get_delta_pkgname $deltafile)"
168 pkgentry=$(find_pkgentry $pkgname)
169 if [[ -z $pkgentry ]]; then
170 return 1
172 deltas="$pkgentry/deltas"
173 if [[ ! -f $deltas ]]; then
174 return 1
176 if grep -q "$filename" $deltas; then
177 sed -i.backup "/$filename/d" $deltas && rm -f $deltas.backup
178 msg2 "$(gettext "Removing existing entry '%s'...")" "$filename"
179 return 0
182 return 1
183 } # end db_remove_delta
185 # write an entry to the pacman database
186 # arg1 - path to package
187 db_write_entry()
189 # blank out all variables
190 local pkgfile="$1"
191 local pkgname pkgver pkgdesc csize size md5sum url arch builddate packager force \
192 _groups _licenses _replaces _depends _conflicts _provides _optdepends
194 local OLDIFS="$IFS"
195 # IFS (field separator) is only the newline character
196 IFS="
199 # read info from the zipped package
200 local line var val
201 for line in $(bsdtar -xOqf "$pkgfile" .PKGINFO |
202 grep -v '^#' | sed 's|\(\w*\)\s*=\s*\(.*\)|\1 \2|'); do
203 # bash awesomeness here- var is always one word, val is everything else
204 var=${line%% *}
205 val=${line#* }
206 declare $var="$val"
207 case "$var" in
208 group) _groups="$_groups$group\n" ;;
209 license) _licenses="$_licenses$license\n" ;;
210 replaces) _replaces="$_replaces$replaces\n" ;;
211 depend) _depends="$_depends$depend\n" ;;
212 conflict) _conflicts="$_conflicts$conflict\n" ;;
213 provides) _provides="$_provides$provides\n" ;;
214 optdepend) _optdepends="$_optdepends$optdepend\n" ;;
215 esac
216 done
218 IFS=$OLDIFS
220 # get md5sum and compressed size of package
221 md5sum="$(openssl dgst -md5 "$pkgfile")"
222 md5sum="${md5sum##* }"
223 csize=$(@SIZECMD@ "$pkgfile")
225 # ensure $pkgname and $pkgver variables were found
226 if [[ -z $pkgname || -z $pkgver ]]; then
227 error "$(gettext "Invalid package file '%s'.")" "$pkgfile"
228 return 1
231 cd "$tmpdir"
233 if [[ -d $pkgname-$pkgver ]]; then
234 warning "$(gettext "An entry for '%s' already existed")" "$pkgname-$pkgver"
235 else
236 if [ $DELTA -eq 1 ]; then
237 pkgentry=$(find_pkgentry $pkgname)
238 if [ -n "$pkgentry" ]; then
239 local oldfilename=$(grep -A1 FILENAME $pkgentry/desc | tail -n1)
240 local oldfile="$(dirname $1)/$oldfilename"
245 # remove an existing entry if it exists, ignore failures
246 db_remove_entry "$pkgname"
248 # create package directory
249 mkdir "$pkgname-$pkgver"
250 cd "$pkgname-$pkgver"
252 # restore an eventual deltas file
253 [[ -f ../$pkgname.deltas ]] && mv "../$pkgname.deltas" deltas
255 # create desc entry
256 msg2 "$(gettext "Creating 'desc' db entry...")"
257 echo -e "%FILENAME%\n$(basename "$1")\n" >>desc
258 echo -e "%NAME%\n$pkgname\n" >>desc
259 [[ -n $pkgbase ]] && echo -e "%BASE%\n$pkgbase\n" >>desc
260 echo -e "%VERSION%\n$pkgver\n" >>desc
261 [[ -n $pkgdesc ]] && echo -e "%DESC%\n$pkgdesc\n" >>desc
262 write_list_entry "GROUPS" "$_groups" "desc"
263 [[ -n $csize ]] && echo -e "%CSIZE%\n$csize\n" >>desc
264 [[ -n $size ]] && echo -e "%ISIZE%\n$size\n" >>desc
266 # compute checksums
267 msg2 "$(gettext "Computing md5 checksums...")"
268 echo -e "%MD5SUM%\n$md5sum\n" >>desc
270 [[ -n $url ]] && echo -e "%URL%\n$url\n" >>desc
271 write_list_entry "LICENSE" "$_licenses" "desc"
272 [[ -n $arch ]] && echo -e "%ARCH%\n$arch\n" >>desc
273 [[ -n $builddate ]] && echo -e "%BUILDDATE%\n$builddate\n" >>desc
274 [[ -n $packager ]] && echo -e "%PACKAGER%\n$packager\n" >>desc
275 write_list_entry "REPLACES" "$_replaces" "desc"
276 [[ -n $force ]] && echo -e "%FORCE%\n" >>desc
278 # create depends entry
279 msg2 "$(gettext "Creating 'depends' db entry...")"
280 # create the file even if it will remain empty
281 touch "depends"
282 write_list_entry "DEPENDS" "$_depends" "depends"
283 write_list_entry "CONFLICTS" "$_conflicts" "depends"
284 write_list_entry "PROVIDES" "$_provides" "depends"
285 write_list_entry "OPTDEPENDS" "$_optdepends" "depends"
287 cd "$startdir"
289 # create a delta file
290 if [ -n "$oldfilename" -a -f "$oldfile" ]; then
291 delta=$(pkgdelta -q $oldfile $1)
292 if [ -f "$delta" ]; then
293 db_write_delta $delta
294 else
295 warning "$(gettext "Old package file not found : %s")" "$oldfilename"
299 return 0
300 } # end db_write_entry
302 # remove existing entries from the DB
303 # arg1 - package name
304 db_remove_entry() {
305 local pkgname=$1
306 local notfound=1
307 local pkgentry=$(find_pkgentry $pkgname)
308 while [[ -n $pkgentry ]]; do
309 notfound=0
310 if [[ -f $pkgentry/deltas ]]; then
311 mv "$pkgentry/deltas" "$tmpdir/$pkgname.deltas"
313 msg2 "$(gettext "Removing existing entry '%s'...")" \
314 "$(basename $pkgentry)"
315 rm -rf $pkgentry
316 pkgentry=$(find_pkgentry $pkgname)
317 done
318 return $notfound
319 } # end db_remove_entry
321 check_repo_db()
323 # check lock file
324 if ( set -o noclobber; echo "$$" > "$LOCKFILE") 2> /dev/null; then
325 CLEAN_LOCK=1
326 else
327 error "$(gettext "Failed to acquire lockfile: %s.")" "$LOCKFILE"
328 [[ -f $LOCKFILE ]] && error "$(gettext "Held by process %s")" "$(cat $LOCKFILE)"
329 exit 1
332 if [[ -f $REPO_DB_FILE ]]; then
333 # there are two situations we can have here- a DB with some entries,
334 # or a DB with no contents at all.
335 if ! bsdtar -tqf "$REPO_DB_FILE" '*/desc' >/dev/null 2>&1; then
336 # check empty case
337 if [[ -n $(bsdtar -tqf "$REPO_DB_FILE" '*' 2>/dev/null) ]]; then
338 error "$(gettext "Repository file '%s' is not a proper pacman database.")" "$REPO_DB_FILE"
339 exit 1
342 msg "$(gettext "Extracting database to a temporary location...")"
343 bsdtar -xf "$REPO_DB_FILE" -C "$tmpdir"
344 else
345 case "$cmd" in
346 repo-remove)
347 error "$(gettext "Repository file '%s' was not found.")" "$REPO_DB_FILE"
348 exit 1
350 repo-add)
351 # check if the file can be created (write permission, directory existence, etc)
352 if ! touch "$REPO_DB_FILE"; then
353 error "$(gettext "Repository file '%s' could not be created.")" "$REPO_DB_FILE"
354 exit 1
356 rm -f "$REPO_DB_FILE"
358 esac
362 add()
364 if [[ ! -f $1 ]]; then
365 error "$(gettext "File '%s' not found.")" "$1"
366 return 1
369 if [[ ${1##*.} == "delta" ]]; then
370 deltafile=$1
371 msg "$(gettext "Adding delta '%s'")" "$deltafile"
372 if ! type xdelta3 &>/dev/null; then
373 error "$(gettext "Cannot find the xdelta3 binary! Is xdelta3 installed?")"
374 exit 1
376 if db_write_delta "$deltafile"; then
377 return 0
378 else
379 return 1
383 pkgfile=$1
384 if ! bsdtar -tqf "$pkgfile" .PKGINFO >/dev/null 2>&1; then
385 error "$(gettext "'%s' is not a package file, skipping")" "$pkgfile"
386 return 1
389 msg "$(gettext "Adding package '%s'")" "$pkgfile"
391 db_write_entry "$pkgfile"
394 remove()
396 if [[ ${1##*.} == "delta" ]]; then
397 deltafile=$1
398 msg "$(gettext "Searching for delta '%s'...")" "$deltafile"
399 if db_remove_delta "$deltafile"; then
400 return 0
401 else
402 error "$(gettext "Delta matching '%s' not found.")" "$deltafile"
403 return 1
407 pkgname=$1
408 msg "$(gettext "Searching for package '%s'...")" "$pkgname"
410 if db_remove_entry "$pkgname"; then
411 rm -f "$tmpdir/$pkgname.deltas"
412 return 0
413 else
414 error "$(gettext "Package matching '%s' not found.")" "$pkgname"
415 return 1
419 trap_exit()
421 echo
422 error "$@"
423 exit 1
426 clean_up() {
427 local exit_code=$?
429 cd "$startdir"
430 [[ -d $tmpdir ]] && rm -rf "$tmpdir"
431 (( CLEAN_LOCK )) && [[ -f $LOCKFILE ]] && rm -f "$LOCKFILE"
433 exit $exit_code
436 # PROGRAM START
438 # determine whether we have gettext; make it a no-op if we do not
439 if ! type gettext &>/dev/null; then
440 gettext() {
441 echo "$@"
445 case "$1" in
446 -h|--help) usage; exit 0;;
447 -V|--version) version; exit 0;;
448 esac
450 # figure out what program we are
451 cmd="$(basename $0)"
452 if [[ $cmd != "repo-add" && $cmd != "repo-remove" ]]; then
453 error "$(gettext "Invalid command name '%s' specified.")" "$cmd"
454 exit 1
457 tmpdir=$(mktemp -d /tmp/repo-tools.XXXXXXXXXX) || (\
458 error "$(gettext "Cannot create temp directory for database building.")"; \
459 exit 1)
461 trap 'clean_up' EXIT
462 trap 'trap_exit "$(gettext "TERM signal caught. Exiting...")"' TERM HUP QUIT
463 trap 'trap_exit "$(gettext "Aborted by user! Exiting...")"' INT
464 trap 'trap_exit "$(gettext "An unknown error has occured. Exiting...")"' ERR
466 success=0
467 # parse arguments
468 for arg in "$@"; do
469 case "$arg" in
470 -q|--quiet) QUIET=1;;
471 -d|--delta) DELTA=1;;
473 if [[ -z $REPO_DB_FILE ]]; then
474 REPO_DB_FILE="$arg"
475 LOCKFILE="$REPO_DB_FILE.lck"
476 check_repo_db
477 else
478 case "$cmd" in
479 repo-add) add $arg && success=1 ;;
480 repo-remove) remove $arg && success=1 ;;
481 esac
484 esac
485 done
487 # if at least one operation was a success, re-zip database
488 if (( success )); then
489 msg "$(gettext "Creating updated database file '%s'")" "$REPO_DB_FILE"
491 case "$REPO_DB_FILE" in
492 *tar.gz) TAR_OPT="z" ;;
493 *tar.bz2) TAR_OPT="j" ;;
494 *tar.xz) TAR_OPT="J" ;;
495 *) warning "$(gettext "'%s' does not have a valid archive extension.")" \
496 "$REPO_DB_FILE" ;;
497 esac
499 filename=$(basename "$REPO_DB_FILE")
501 cd "$tmpdir"
502 if [[ -n $(ls) ]]; then
503 bsdtar -c${TAR_OPT}f "$filename" *
504 else
505 # we have no packages remaining? zip up some emptyness
506 warning "$(gettext "No packages remain, creating empty database.")"
507 bsdtar -c${TAR_OPT}f "$filename" -T /dev/null
509 cd "$startdir"
511 [[ -f $REPO_DB_FILE ]] && mv -f "$REPO_DB_FILE" "${REPO_DB_FILE}.old"
512 [[ -f $tmpdir/$filename ]] && mv "$tmpdir/$filename" "$REPO_DB_FILE"
513 dblink="${REPO_DB_FILE%.tar.*}"
514 target=${REPO_DB_FILE##*/}
515 ln -sf "$target" "$dblink" 2>/dev/null || \
516 ln -f "$target" "$dblink" 2>/dev/null || \
517 cp "$REPO_DB_FILE" "$dblink"
518 else
519 msg "$(gettext "No packages modified, nothing to do.")"
520 exit 1
523 exit 0
524 # vim: set ts=2 sw=2 noet: