From f8bfe729e49fe5d7fd7df80733ad3d2d1c61160d Mon Sep 17 00:00:00 2001 From: Judd Vinet Date: Wed, 3 Sep 2003 02:09:29 +0000 Subject: Imported from pacman-2.6.tar.gz --- ChangeLog | 27 +- Makefile.in | 2 +- TODO | 14 - cnvpkg | 19 - doc/makepkg.8.in | 84 +++-- doc/pacman.8.in | 20 +- etc/makepkg.conf | 14 +- etc/pacman.conf | 38 +- scripts/gensync | 75 +++- scripts/makepkg | 278 +++++++++++---- scripts/makeworld | 189 +++++----- src/convertdb.c | 2 +- src/db.c | 112 +++++- src/db.h | 5 +- src/list.c | 6 +- src/list.h | 2 +- src/package.c | 57 +-- src/package.h | 5 +- src/pacman.c | 1029 +++++++++++++++++++++++++++++++++++++++++++---------- src/pacman.h | 9 +- src/pacsync.c | 129 +++---- src/pacsync.h | 7 +- src/rpmvercmp.c | 2 +- src/rpmvercmp.h | 2 +- src/util.c | 15 +- src/util.h | 2 +- src/vercmp.c | 2 +- 27 files changed, 1600 insertions(+), 546 deletions(-) delete mode 100755 cnvpkg diff --git a/ChangeLog b/ChangeLog index 7765bfda..a147606b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,27 @@ VERSION DESCRIPTION ------------------------------------------------------------------- -2.5.1 - Added retries to the downloader to get around some - transient network errors. (this will likely be an option - in pacman.conf for later versions) +----------------------------------------------------------------------------- +2.6 - Added group handling, so one can run 'pacman -S kde' and + install all files from the KDE group + - Fixed a duplication bug in cascade package removal + - Added support for virtual provisions with "provides" tags + - When conflicts are encountered, pacman now offers the chance + to remove the conflicting packages (provides or literals) + - Added support for renamed/combined packages with a "replaces" + tag + - Added --nostrip option to makepkg + - Improved --search to list all packages from all repos when + a search term is omitted + - Added logging support through syslog() + - Added fakeroot support to makepkg (RomanK) + - Added MD5sum generation/validation to makepkg (RomanK) + - Fixed a progress bar bug (Aurelien Foret) + - Sorted makepkg's .FILELISTs (Aurelien Foret) + - Targets are now re-ordered w.r.t. dependencies when + using -A/-U + - Modified --search to work when called as -Sys + - Modified abs to use ABS_ROOT from /etc/abs/abs.conf (Aurelien) + - Other bug fixes +2.5.1 - Minor bug fixes 2.5 - Added an URL tag to package info - Sped up package load times by about 500% by introducing a .FILELIST into the package diff --git a/Makefile.in b/Makefile.in index c4d66c31..fb0ab827 100644 --- a/Makefile.in +++ b/Makefile.in @@ -34,7 +34,7 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) INSTALL_DATA = @INSTALL_DATA@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ -PACVER = 2.5.1 +PACVER = 2.6 TOPDIR = @srcdir@ SRCDIR = $(TOPDIR)/src/ diff --git a/TODO b/TODO index add6b4be..90e03b36 100644 --- a/TODO +++ b/TODO @@ -1,29 +1,15 @@ - fix the broken pipe bug -- add some logging mechanism (/var/log/pacman.log) - handle version comparators in makepkg dep resolution (eg, glibc>=2.2.5) -- have "group" designations - record md5sums of all files in a package -- add a way to clean /var/cache/pacman/src -- duplicate dep checks occur with sync (one in sync, one in add) -- if a package is removed with --nodeps and re-installed, the requiredby - fields of it's required packages are not updated -- add an option equivalent to 'pacman -Ql pkg | grep filename' -- add other options to config file: db location, overwrite behaviour, etc. -- use the COLUMNS env var for the progress bar ? use 'set -e' in makepkg? x if a package fails, ask before aborting the full operation - can't -- further dependent packages may fail b/c of the first failure -? ask, then remove conflicting packages with --sync -? use a provides tag (instead of an OR operator in depends) -- add a 'cascade' option to --remove that will remove a package and - all requiredby packages under it - check $PACCONF env var ? use a 'trust pacman' config option for downgrading? ? build-time (source) dependencies in makepkg ? run ldd on every executable in a newly built package to find required so's - add a --pretend option - add a consistency/sanity check operation (md5 tracking for all files) -- add a --dbpath option - use package caches more for performance - clean up output a bit (message queue?) - use a files.cache db for --owns and db_find_conflicts diff --git a/cnvpkg b/cnvpkg deleted file mode 100755 index 2a164c12..00000000 --- a/cnvpkg +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -TMPDIR=/tmp/.pkgcnv -TMPFILE=/tmp/.pkgcnvf -tl=`pwd` - -for fn in $*; do - rm -rf $TMPDIR; - mkdir -p $TMPDIR; - echo "Converting $fn" - cd $TMPDIR - tar zxvf $tl/$fn | grep -v '^.PKGINFO' | grep -v '._install' >$TMPFILE - mv $TMPFILE ./.FILELIST - if [ -f ._install ]; then - tar cfz /new/$fn .PKGINFO .FILELIST ._install * - else - tar cfz /new/$fn .PKGINFO .FILELIST * - fi -done diff --git a/doc/makepkg.8.in b/doc/makepkg.8.in index d6a7994f..5f86f795 100644 --- a/doc/makepkg.8.in +++ b/doc/makepkg.8.in @@ -8,7 +8,7 @@ makepkg \- package build utility a build-capable linux platform, wget, and some build scripts. The advantage to a script-based build is that you only really do the work once. Once you have the build script for a package, you just need to run makepkg and it -will do the rest: download source files, check dependencies, +will do the rest: download and validate source files, check dependencies, configure the buildtime settings, build the package, install the package into a temporary root, make customizations, generate meta-info, and package the whole thing up for \fBpacman\fP to use. @@ -31,20 +31,23 @@ the \fBabs\fP script included with pacman/makepkg. .RS .nf pkgname=modutils -pkgver=2.4.13 +pkgver=2.4.25 pkgrel=1 pkgdesc="Utilities for inserting and removing modules from the linux kernel" url="http://www.kernel.org" backup=(etc/modules.conf) -depends=('glibc>=2.2.5' 'bash' 'zlib') -source=(ftp://ftp.server.com/$pkgname-$pkgver.tar.gz modules.conf) +depends=('mawk' 'bash' 'glibc' 'zlib') +source=(ftp://ftp.kernel.org/pub/linux/utils/kernel/$pkgname/v2.4/$pkgname-$pkgver.tar.bz2 \\ + modules.conf) +md5sums=('2c0cca3ef6330a187c6ef4fe41ecaa4d' \\ + '35175bee593a7cc7d6205584a94d8625') build() { cd $startdir/src/$pkgname-$pkgver - ./configure --prefix=/usr + ./configure --prefix=/usr --enable-insmod-static make || return 1 make prefix=$startdir/pkg/usr install - # copy our custom modules.conf into the package root + mv $startdir/pkg/usr/sbin $startdir/pkg mkdir -p $startdir/pkg/etc cp ../modules.conf $startdir/pkg/etc } @@ -61,10 +64,14 @@ The line with \fIbackup=\fP specifies files that should be treated specially when removing or upgrading packages. See \fBHANDLING CONFIG FILES\fP in the \fIpacman\fP manpage for more information on this. -The sixth line lists the dependencies for this package. In order to build/run +The seventh line lists the dependencies for this package. In order to build/run the package, all dependencies must be satisifed first. makepkg will check this before attempting to build the package. +The \fIsource\fP array tells makepkg which files to download/extract before compiling +begins. The \fImd5sums\fP array provides md5sums for each of these files. These +are used to validate the integrity of the source files. + Once your PKGBUILD is created, you can run \fImakepkg\fP from the build directory. makepkg will then check dependencies and look for the source files required to build. If some are missing it will attempt to download them, provided there is @@ -193,7 +200,28 @@ This field contains an optional URL that is associated with the piece of softwar being packaged. This is typically the project's website. .TP -.B backup +.B install +Specifies a special install script that is to be included in the package. +This file should reside in the same directory as the PKGBUILD, and will be +copied into the package by makepkg. It does not need to be included in the +\fIsource\fP array. (eg, install=modutils.install) + +.TP +.B source \fI(array)\fP +The \fIsource\fP line is an array of source files required to build the +package. Source files must reside in the same directory as the PKGBUILD +file, unless they have a fully-qualified URL. Then if the source file +does not already exist in /var/cache/pacman/src, the file is downloaded +by wget. + +.TP +.B groups \fI(array)\fP +This is an array of symbolic names that represent groups of packages, allowing +you to install multiple packages by requesting a single target. For example, +one could install all KDE packages by installing the 'kde' group. + +.TP +.B backup \fI(array)\fP A space-delimited array of filenames (without a preceding slash). The \fIbackup\fP line will be propagated to the package meta-info file for pacman. This will designate all files listed there to be backed up if this @@ -201,14 +229,7 @@ package is ever removed from a system. See \fBHANDLING CONFIG FILES\fP in the \fIpacman\fP manpage for more information. .TP -.B install -Specified a special install script that is to be included in the package. -This file should reside in the same directory as the PKGBUILD, and will be -copied into the package by makepkg. It does not need to be included in the -\fIsource\fP array. (eg, install=modutils.install) - -.TP -.B depends +.B depends \fI(array)\fP An array of packages that this package depends on to build and run. Packages in this list should be surrounded with single quotes and contain at least the package name. They can also include a version requirement of the form @@ -217,18 +238,24 @@ package name. They can also include a version requirement of the form See the PKGBUILD example above for an example of the \fIdepends\fP directive. .TP -.B conflicts +.B conflicts \fI(array)\fP An array of packages that will conflict with this package (ie, they cannot both be installed at the same time). This directive follows the same format as \fIdepends\fP except you cannot specify versions here, only package names. .TP -.B source -The \fIsource\fP line is an array of source files required to build the -package. Source files must reside in the same directory as the PKGBUILD -file, unless they have a fully-qualified URL. Then if the source file -does not already exist in /var/cache/pacman/src, the file is downloaded -by wget. +.B provides \fI(array)\fP +An array of "virtual provisions" that this package provides. This allows a package +to provide dependency names other than it's own package name. For example, the +kernel-scsi and kernel-ide packages can each provide 'kernel' which allows packages +to simply depend on 'kernel' rather than "kernel-scsi OR kernel-ide OR ..." + +.TP +.B replaces \fI(array)\fP +This is an array of packages that this package should replace, and can be used to handle +renamed/combined packages. For example, if the kernel package gets renamed +to kernel-ide, then subsequent 'pacman -Syu' calls will not pick up the upgrade, due +to the differing package names. \fIreplaces\fP handles this. .SH MAKEPKG OPTIONS .TP @@ -255,9 +282,20 @@ process if all of the dependencies aren't installed. file already exists in the build directory. You can override this behaviour with the \fB--force\fP switch. .TP +.B "\-g, \-\-genmd5" +Download all source files (if required) and use \fImd5sum\fP to generate md5 hashes +for each of them. You can then redirect the output into your PKGBUILD for source +validation (makepkg -g >>PKGBUILD). +.TP +.B "\-h, \-\-help" +Output syntax and commandline options. +.TP .B "\-i, \-\-install" Install/Upgrade the package after a successful build. .TP +.B "\-n, \-\-nostrip" +Do not strip binaries and libraries. +.TP .B "\-p " Read the package script \fI\fP instead of the default (\fIPKGBUILD\fP). .TP diff --git a/doc/pacman.8.in b/doc/pacman.8.in index 2113fc21..95b77154 100644 --- a/doc/pacman.8.in +++ b/doc/pacman.8.in @@ -96,6 +96,10 @@ Remove packages from the cache. When pacman downloads packages, it saves them in \fI/var/cache/pacman/pkg\fP. If you need to free up diskspace, you can remove these packages by using the --clean option. .TP +.B "\-g, \-\-groups" +Display all the members for each package group specified. If no group +names are provided, all groups and members will be listed. +.TP .B "\-s, \-\-search " This will search each package in the package list for names or descriptions that contains . @@ -117,6 +121,10 @@ defined in \fI/etc/pacman.conf\fP. This should typically be used each time you use \fB--sysupgrade\fP. .SH QUERY OPTIONS .TP +.B "\-g, \-\-groups" +Display all groups that a specified package is part of. If no package +names are provided, all groups and members will be listed. +.TP .B "\-i, \-\-info" Display information on a given package. If it is used with the \fB-p\fP option then the .PKGINFO file will be printed. @@ -179,15 +187,15 @@ Server = ftp://ftp.archlinux.org/current Server = ftp://ftp.mirror.com/archlinux/current [custom] -Server = local:///home/pkgs +Server = file:///home/pkgs .fi .RE .SH CONFIG: OPTIONS .TP -.B "DBPath = /path/to/db/dir" +.B "DBPath = path/to/db/dir" Overrides the default location of the toplevel database directory. The default is -\fI/var/lib/pacman\fP. +\fIvar/lib/pacman\fP. .TP .B "IgnorePkg = [package] ..." Instructs pacman to ignore any upgrades for this package when performing a @@ -199,13 +207,17 @@ Disables passive ftp connections when downloading packages. (aka Active Mode) .B "NoUpgrade = [file] ..." All files listed with a \fBNoUpgrade\fP directive will never be touched during a package install/upgrade. \fINote:\fP do not include the leading slash when specifying files. +.TP +.B "UseSyslog" +Log action messages through syslog(). This will insert pacman log entries into your +/var/log/messages or equivalent. .SH CONFIG: REPOSITORIES Each repository section defines a section name and at least one location where the packages can be found. The section name is defined by the string within square brackets (eg, the two above are 'current' and 'custom'). Locations are defined with the \fIServer\fP directive and follow a URL naming structure. Currently only ftp is supported for remote servers. If you -want to use a local directory, you can specify the full path with a 'local://' prefix, as +want to use a local directory, you can specify the full path with a 'file://' prefix, as shown above. .SH USING YOUR OWN REPOSITORY Let's say you have a bunch of custom packages in \fI/home/pkgs\fP and their respective PKGBUILD diff --git a/etc/makepkg.conf b/etc/makepkg.conf index b2d8a893..77e62936 100644 --- a/etc/makepkg.conf +++ b/etc/makepkg.conf @@ -10,21 +10,23 @@ export FTPAGENT="/usr/bin/wget --continue --passive-ftp --tries=3 --waitretry=3" #export FTPAGENT="/usr/bin/snarf" #export FTPAGENT="/usr/bin/lftpget -c" -# Pentium Pro/Pentium II/Pentium III+/Pentium 4/Athlon optimized (but binaries -# will run on any x86 system) -#export CHOST="i686-pc-linux-gnu" -#export CFLAGS="-mcpu=i686 -O2 -pipe" -#export CXXFLAGS="-mcpu=i686 -O2 -pipe" - # Pentium Pro/Pentium II/Pentium III+/Pentium 4/Athlon exclusive (binaries # will use the P6 instruction set and only run on P6+ systems) export CHOST="i686-pc-linux-gnu" export CFLAGS="-march=i686 -O2 -pipe" export CXXFLAGS="-march=i686 -O2 -pipe" +# Pentium Pro/Pentium II/Pentium III+/Pentium 4/Athlon optimized (but binaries +# will run on any x86 system) +#export CHOST="i686-pc-linux-gnu" +#export CFLAGS="-mcpu=i686 -O2 -pipe" +#export CXXFLAGS="-mcpu=i686 -O2 -pipe" # SMP Systems #export MAKEFLAGS="-j 2" +# Enable fakeroot for building packages as a non-root user +export USE_FAKEROOT="y" + # if you want your name to show up in the packages you build, set this. #export PACKAGER="John Doe " diff --git a/etc/pacman.conf b/etc/pacman.conf index aacf95ba..a11b383d 100644 --- a/etc/pacman.conf +++ b/etc/pacman.conf @@ -8,9 +8,11 @@ # GENERAL OPTIONS # [options] +UseSyslog NoUpgrade = etc/passwd etc/group etc/shadow -NoUpgrade = etc/fstab etc/rc.conf etc/rc.local -NoUpgrade = etc/lilo.conf etc/raidtab +NoUpgrade = etc/fstab etc/raidtab +NoUpgrade = etc/rc.conf etc/rc.local +NoUpgrade = etc/lilo.conf boot/grub/menu.lst #IgnorePkg = lilo gcc # @@ -28,6 +30,20 @@ Server = ftp://gd.tuwien.ac.at/opsys/linux/archlinux/current Server = ftp://saule.mintis.lt/pub/linux/current Server = ftp://ftp.rez-gif.supelec.fr/pub/Linux/distrib/archlinux/current +# The "unofficial" package set +# +[unofficial] +Server = ftp://ftp.archlinux.org/unofficial +Server = ftp://ftp.ibiblio.org/pub/linux/distributions/archlinux/unofficial +Server = ftp://ftp.webtrek.com/pub/mirrors/archlinux/unofficial +Server = ftp://ftp.mpi-sb.mpg.de/pub/linux/mirror/ftp.ibiblio.org/pub/Linux/distributions/archlinux/unofficial +Server = ftp://ftp.oit.unc.edu/pub/Linux/distributions/archlinux/unofficial +Server = ftp://ftp.tu-chemnitz.de/pub/linux/sunsite.unc-mirror/distributions/archlinux/unofficial +Server = ftp://ftp.parrswood.net/Mirrors/ftp.archlinux.org/unofficial +Server = ftp://gd.tuwien.ac.at/opsys/linux/archlinux/unofficial +Server = ftp://saule.mintis.lt/pub/linux/unofficial +Server = ftp://ftp.rez-gif.supelec.fr/pub/Linux/distrib/archlinux/unofficial + # If you use the 'stable' tree, you should disable the 'current' # tree to avoid conflicts # @@ -43,26 +59,12 @@ Server = ftp://ftp.rez-gif.supelec.fr/pub/Linux/distrib/archlinux/current #Server = ftp://saule.mintis.lt/pub/linux/stable #Server = ftp://ftp.rez-gif.supelec.fr/pub/Linux/distrib/archlinux/stable -# Uncomment this block to access the 'unofficial' package set -# -#[unofficial] -#Server = ftp://ftp.ibiblio.org/pub/linux/distributions/archlinux/unofficial -#Server = ftp://ftp.webtrek.com/pub/mirrors/archlinux/unofficial -#Server = ftp://ftp.archlinux.org/unofficial -#Server = ftp://ftp.mpi-sb.mpg.de/pub/linux/mirror/ftp.ibiblio.org/pub/Linux/distributions/archlinux/unofficial -#Server = ftp://ftp.oit.unc.edu/pub/Linux/distributions/archlinux/unofficial -#Server = ftp://ftp.tu-chemnitz.de/pub/linux/sunsite.unc-mirror/distributions/archlinux/unofficial -#Server = ftp://ftp.parrswood.net/Mirrors/ftp.archlinux.org/unofficial -#Server = ftp://gd.tuwien.ac.at/opsys/linux/archlinux/unofficial -#Server = ftp://saule.mintis.lt/pub/linux/unofficial -#Server = ftp://ftp.rez-gif.supelec.fr/pub/Linux/distrib/archlinux/unofficial - # Uncomment this block to access the 'unstable' package set # #[unstable] +#Server = ftp://ftp.archlinux.org/unstable #Server = ftp://ftp.ibiblio.org/pub/linux/distributions/archlinux/unstable #Server = ftp://ftp.webtrek.com/pub/mirrors/archlinux/unstable -#Server = ftp://ftp.archlinux.org/unstable #Server = ftp://ftp.mpi-sb.mpg.de/pub/linux/mirror/ftp.ibiblio.org/pub/Linux/distributions/archlinux/unstable #Server = ftp://ftp.oit.unc.edu/pub/Linux/distributions/archlinux/unstable #Server = ftp://ftp.tu-chemnitz.de/pub/linux/sunsite.unc-mirror/distributions/archlinux/unstable @@ -74,5 +76,5 @@ Server = ftp://ftp.rez-gif.supelec.fr/pub/Linux/distrib/archlinux/current # An example of a custom package repository. See the pacman manpage for # tips on creating your own repositories. #[custom] -#Server = local:///home/custompkgs +#Server = file:///home/custompkgs diff --git a/scripts/gensync b/scripts/gensync index ef528543..e6bf8982 100755 --- a/scripts/gensync +++ b/scripts/gensync @@ -1,6 +1,26 @@ #!/bin/bash +# +# gensync +# +# Copyright (c) 2002-2003 by Judd Vinet +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +# USA. +# -myver='2.5.1' +myver='2.6' usage() { echo "gensync $myver" @@ -24,14 +44,15 @@ usage() { db_write_entry() { - unset pkgname pkgver pkgrel - unset depends conflicts + unset pkgname pkgver pkgrel pkgdesc + unset groups replaces provides depends conflicts source $1 || return 1 cd /tmp/.gensync mkdir $pkgname-$pkgver-$pkgrel cd $pkgname-$pkgver-$pkgrel # desc - echo "%NAME%" >desc + : >desc + echo "%NAME%" >>desc echo "$pkgname" >>desc echo "" >>desc echo "%VERSION%" >>desc @@ -40,17 +61,43 @@ db_write_entry() echo "%DESC%" >>desc echo "$pkgdesc" >>desc echo "" >>desc + if [ ${#groups[*]} -gt 0 ]; then + echo "%GROUPS%" >>desc + for it in "${groups[@]}"; do + echo "$it" >>desc + done + echo "" >>desc + fi + if [ ${#replaces[*]} -gt 0 ]; then + echo "%REPLACES%" >>desc + for it in "${replaces[@]}"; do + echo "$it" >>desc + done + echo "" >>desc + fi # depends - echo "%DEPENDS%" >depends - for depend in "${depends[@]}"; do - echo "$depend" >>depends - done - echo "" >>depends - echo "%CONFLICTS%" >>depends - for conflict in "${conflicts[@]}"; do - echo "$conflict" >>depends - done - echo "" >>depends + : >depends + if [ ${#depends[*]} -gt 0 ]; then + echo "%DEPENDS%" >>depends + for it in "${depends[@]}"; do + echo "$it" >>depends + done + echo "" >>depends + fi + if [ ${#conflicts[*]} -gt 0 ]; then + echo "%CONFLICTS%" >>depends + for it in "${conflicts[@]}"; do + echo "$it" >>depends + done + echo "" >>depends + fi + if [ ${#provides[*]} -gt 0 ]; then + echo "%PROVIDES%" >>depends + for it in "${provides[@]}"; do + echo "$it" >>depends + done + echo "" >>depends + fi } if [ $# -lt 2 ]; then diff --git a/scripts/makepkg b/scripts/makepkg index 89e9ee22..a620d139 100755 --- a/scripts/makepkg +++ b/scripts/makepkg @@ -1,17 +1,68 @@ #!/bin/bash - -myver='2.5.1' +# +# makepkg +# +# Copyright (c) 2002-2003 by Judd Vinet +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +# USA. +# + +myver='2.6' startdir=`pwd` +msg() { + echo "$1" >&2 +} + [ -f /etc/makepkg.conf ] && source /etc/makepkg.conf +INFAKEROOT= +if [ "$1" = "-F" ]; then + INFAKEROOT=1 + shift +fi + +if [ "`id -u`" != "0" ]; then + if [ "$USE_FAKEROOT" = "y" -o "$USE_FAKEROOT" = "Y" ]; then + if [ `type -p fakeroot` ]; then + msg "==> Entering fakeroot environment" + fakeroot -- $0 -F $@ + exit $? + else + msg "==> WARNING: Fakeroot is not installed. Building as an unprivileged user" + msg "==> will result in non-root ownership of the packaged files." + msg "==> Install the fakeroot package to correctly build as a non-root" + msg "==> user." + msg "" + sleep 1 + fi + else + msg "==> WARNING: Running makepkg as an unprivileged user will result in non-root" + msg "==> ownership of the packaged files. Try using the fakeroot" + msg "==> environment. (USE_FAKEROOT=y in makepkg.conf)" + msg "" + sleep 1 + fi +fi + + strip_url() { echo $1 | sed 's|^.*://.*/||g' } -msg() { - echo $* >&2 -} checkdeps() { local missdep=`pacman -T $*` @@ -50,31 +101,34 @@ usage() { echo "makepkg version $myver" echo "usage: $0 [options]" echo "options:" + echo " -b, --builddeps Build missing dependencies from source" echo " -c, --clean Clean up work files after build" echo " -C, --cleancache Clean up source files from the cache" - echo " -s, --syncdeps Install missing dependencies with pacman" - echo " -b, --builddeps Build missing dependencies from source" echo " -d, --nodeps Skip all dependency checks" - echo " -i, --install Install package after successful build" echo " -f, --force Overwrite existing package" - echo " -w Write package to instead of the working dir" - echo " -p Use an alternate build script (instead of PKGBUILD)" + echo " -g, --genmd5 Generate MD5sums for source files" echo " -h, --help This help" + echo " -i, --install Install package after successful build" + echo " -n, --nostrip Do not strip binaries/libraries" + echo " -p Use an alternate build script (instead of PKGBUILD)" + echo " -s, --syncdeps Install missing dependencies with pacman" + echo " -w Write package to instead of the working dir" echo echo " if -p is not specified, makepkg will look for a PKGBUILD" echo " file in the current directory." echo - exit 0 } # Options CLEANUP=0 CLEANCACHE=0 INSTALL=0 +GENMD5=0 DEP_BIN=0 DEP_SRC=0 NODEPS=0 FORCE=0 +NOSTRIP=0 PKGDEST=$startdir BUILDSCRIPT="./PKGBUILD" @@ -87,12 +141,18 @@ while [ "$#" -ne "0" ]; do --nodeps) NODEPS=1 ;; --install) INSTALL=1 ;; --force) FORCE=1 ;; + --nostrip) NOSTRIP=1 ;; + --genmd5) GENMD5=1 ;; + --help) + usage + exit 0 + ;; --*) usage exit 1 ;; -*) - while getopts "cCsbdifp:w:-" opt; do + while getopts "cCsbdhifgnp:w:-" opt; do case $opt in c) CLEANUP=1 ;; C) CLEANCACHE=1 ;; @@ -100,9 +160,15 @@ while [ "$#" -ne "0" ]; do b) DEP_SRC=1 ;; d) NODEPS=1 ;; i) INSTALL=1 ;; + g) GENMD5=1 ;; f) FORCE=1 ;; + n) NOSTRIP=1 ;; w) PKGDEST=$OPTARG ;; p) BUILDSCRIPT=$OPTARG ;; + h) + usage + exit 0 + ;; -) OPTIND=0 break @@ -122,13 +188,18 @@ while [ "$#" -ne "0" ]; do done if [ "$CLEANCACHE" = "1" ]; then - msg "==> Cleaning up source files from the cache" - rm -rf /var/cache/pacman/src/* - exit 0 + if [ "`id -u`" = "0" -a "$INFAKEROOT" != "1" ]; then + msg "==> Cleaning up source files from the cache." + rm -rf /var/cache/pacman/src/* + exit 0 + else + msg "==> You must be root to clean the cache." + exit 1 + fi fi -unset pkgname pkgver pkgrel pkgdesc url -unset depends conflicts backup source install build +unset pkgname pkgver pkgrel pkgdesc url groups provides md5sums +unset replaces depends conflicts backup source install build umask 0022 if [ ! -f $BUILDSCRIPT ]; then @@ -148,11 +219,13 @@ if [ `echo $pkgrel | grep '-'` ]; then exit 1 fi -if [ -f $PKGDEST/${pkgname}-${pkgver}-${pkgrel}.pkg.tar.gz -a "$FORCE" = "0" ]; then +if [ -f $PKGDEST/${pkgname}-${pkgver}-${pkgrel}.pkg.tar.gz -a "$FORCE" = "0" -a "$GENMD5" = "0" ]; then msg "==> ERROR: a package has already been built. (use -f to overwrite)" exit 1 fi +msg "==> Making package: $pkgname (`date`)" + unset deplist if [ `type -p pacman` -a "$NODEPS" = "0" ]; then msg "==> Checking Dependencies..." @@ -210,21 +283,19 @@ else msg "==> WARNING: pacman was not found in PATH. skipping dependency checks." fi -d=`date` cd $startdir -msg "==> Making package $pkgname ($d)" # extract source -msg "==> Acquiring/Extracting Sources..." +msg "==> Retrieving Sources..." mkdir -p src cd $startdir/src for netfile in ${source[@]}; do file=`strip_url $netfile` if [ -f ../$file ]; then - msg "==> Found $file in build dir" + msg " |=> Found $file in build dir" cp ../$file . elif [ -f /var/cache/pacman/src/$file ]; then - msg "==> Using local copy of $file" + msg " |=> Using local copy of $file" cp /var/cache/pacman/src/$file . else # check for a download utility @@ -245,39 +316,108 @@ for netfile in ${source[@]}; do msg "==> Aborting..." exit 1 fi - msg "==> Downloading $file" + msg " |=> Downloading $file" $FTPAGENT $netfile 2>&1 if [ ! -f $file ]; then msg "==> ERROR: Failed to download $file" msg "==> Aborting..." exit 1 fi - mkdir -p /var/cache/pacman/src && cp $file /var/cache/pacman/src + if [ "`id -u`" = "0" -a "$INFAKEROOT" != "1" ]; then + mkdir -p /var/cache/pacman/src && cp $file /var/cache/pacman/src + else + cp $file .. + fi fi - unset cmd - case $file in - *.tar.gz|*.tar.Z|*.tgz) - cmd="tar --use-compress-program=gzip -xf $file" ;; - *.tar.bz2) - cmd="tar --use-compress-program=bzip2 -xf $file" ;; - *.tar) - cmd="tar -xf $file" ;; - *.zip) - cmd="unzip -qq $file" ;; - *.gz) - cmd="gunzip $file" ;; - esac - if [ "$cmd" != "" ]; then - msg "==> $cmd" - $cmd - if [ $? -ne 0 ]; then - msg "==> ERROR: Failed to extract $file" - msg "==> Aborting..." - exit 1 + if [ "$GENMD5" = "0" ]; then + unset cmd + case $file in + *.tar.gz|*.tar.Z|*.tgz) + cmd="tar --use-compress-program=gzip -xf $file" ;; + *.tar.bz2) + cmd="tar --use-compress-program=bzip2 -xf $file" ;; + *.tar) + cmd="tar -xf $file" ;; + *.zip) + cmd="unzip -qq $file" ;; + *.gz) + cmd="gunzip $file" ;; + esac + if [ "$cmd" != "" ]; then + msg " |=> $cmd" + $cmd + if [ $? -ne 0 ]; then + msg "==> ERROR: Failed to extract $file" + msg "==> Aborting..." + exit 1 + fi fi fi done +if [ "$GENMD5" = "1" ]; then + if [ ! `type -p md5sum` ]; then + msg "==> ERROR: Cannot find the md5sum program." + exit 1 + fi + msg "==> Generating MD5sums for source files" + msg "" + ct=0 + numsrc=${#source[@]} + for netfile in ${source[@]}; do + file=`strip_url $netfile` + sum=`md5sum $file | cut -d' ' -f 1` + if [ $ct -eq 0 ]; then + echo -n "md5sums=(" + else + echo -ne "\t" + fi + echo -n "'$sum'" + ct=$(($ct+1)) + if [ $ct -eq $numsrc ]; then + echo ')' + else + echo ' \' + fi + done + msg "" + exit 0 +fi + +# MD5 Validation +if [ ${#md5sums[@]} -ne ${#source[@]} ]; then + msg "==> WARNING: MD5sums are missing or incomplete. Cannot verify source integrity." +# sleep 1 +elif [ `type -p md5sum` ]; then + msg "==> Validating source files with MD5sums" + errors=0 + idx=0 + for netfile in ${source[@]}; do + file=`strip_url $netfile` + echo -n " |=> $file ... " >&2 + echo "${md5sums[$idx]} $file" | md5sum -c - >/dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "FAILED" >&2 + errors=1 + else + echo "Passed" >&2 + fi + idx=$(($idx+1)) + done + if [ $errors -gt 0 ]; then + msg "==> ERROR: One or more files did not pass the validity check!" + exit 1 + fi +else + msg "==> WARNING: The md5sum program is missing. Cannot verify source files!" + sleep 1 +fi + +if [ "`id -u`" = "0" ]; then + # chown all source files to root.root + chown -R root.root $startdir/src +fi + # check for existing pkg directory if [ -d $startdir/pkg ]; then msg "==> Removing existing pkg directory..." @@ -286,7 +426,7 @@ fi mkdir -p $startdir/pkg # build -msg "==> Building Package..." +msg "==> Starting build()..." build 2>&1 if [ $? -gt 0 ]; then msg "==> Build Failed. Aborting..." @@ -330,15 +470,18 @@ if [ -d pkg/usr/man ]; then done fi -# strip binaries cd $startdir -msg "==> Stripping debugging symbols from libraries..." -find pkg/{,usr,usr/local,opt/*}/lib -type f -exec /usr/bin/strip --strip-debug '{}' ';' 2>&1 -msg "==> Stripping symbols from binaries..." -find pkg/{,usr,usr/local,opt/*}/{bin,sbin} -type f -exec /usr/bin/strip '{}' ';' 2>&1 + +# strip binaries +if [ "$NOSTRIP" = "0" ]; then + msg "==> Stripping debugging symbols from libraries..." + find pkg/{,usr,usr/local,opt/*}/lib -type f -exec /usr/bin/strip --strip-debug '{}' \; 2>&1 + msg "==> Stripping symbols from binaries..." + find pkg/{,usr,usr/local,opt/*}/{bin,sbin} -type f -exec /usr/bin/strip '{}' \; 2>&1 +fi # get some package meta info -builddate=`date -u "+%a %b %d %k:%M:%S %Y"` +builddate=`LC_ALL= ; date -u "+%a %b %d %k:%M:%S %Y"` if [ "$PACKAGER" != "" ]; then packager="$PACKAGER" else @@ -360,14 +503,23 @@ echo "builddate = $builddate" >>.PKGINFO echo "packager = $packager" >>.PKGINFO echo "size = $size" >>.PKGINFO -for depend in "${depends[@]}"; do - echo "depend = $depend" >>.PKGINFO +for it in "${replaces[@]}"; do + echo "replaces = $it" >>.PKGINFO +done +for it in "${groups[@]}"; do + echo "group = $it" >>.PKGINFO +done +for it in "${depends[@]}"; do + echo "depend = $it" >>.PKGINFO +done +for it in "${conflicts[@]}"; do + echo "conflict = $it" >>.PKGINFO done -for conflict in "${conflicts[@]}"; do - echo "conflict = $conflict" >>.PKGINFO +for it in "${provides[@]}"; do + echo "provides = $it" >>.PKGINFO done -for bakfile in "${backup[@]}"; do - echo "backup = $bakfile" >>.PKGINFO +for it in "${backup[@]}"; do + echo "backup = $it" >>.PKGINFO done # check for an install script @@ -377,9 +529,11 @@ if [ "$install" != "" ]; then fi # build a filelist -msg "==> Building filelist..." +msg "==> Generating .FILELIST file..." cd $startdir/pkg -tar cv * >/dev/null 2>.FILELIST +tar cv * >/dev/null 2>.FILELIST.tmp +cat .FILELIST.tmp | sort >.FILELIST +rm -f .FILELIST.tmp # tar it up msg "==> Compressing package..." @@ -389,7 +543,7 @@ if [ -f $startdir/pkg/.INSTALL ]; then else cmd="tar czvf $PKGDEST/$pkgname-$pkgver-$pkgrel.pkg.tar.gz .PKGINFO .FILELIST *" fi -$cmd >../filelist +$cmd | sort >../filelist cd $startdir if [ "$CLEANUP" = "1" ]; then @@ -397,7 +551,7 @@ if [ "$CLEANUP" = "1" ]; then rm -rf src pkg filelist fi -msg "==> Finished making $pkgname (`date`)" +msg "==> Finished making: $pkgname (`date`)" if [ "$INSTALL" = "1" ]; then msg "==> Running pacman --upgrade" diff --git a/scripts/makeworld b/scripts/makeworld index 94a522e2..aee9792c 100755 --- a/scripts/makeworld +++ b/scripts/makeworld @@ -1,102 +1,130 @@ #!/bin/bash +# +# makeworld +# +# Copyright (c) 2002-2003 by Judd Vinet +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +# USA. +# toplevel=`pwd` -version="2.5.1" +version="2.6" usage() { - echo "makeworld version $version" - echo "usage: $0 [options] [category] ..." - echo "options:" - echo " -b, --builddeps Build missing dependencies from source" - echo " -c, --clean Clean up work files after build" - echo " -d, --nodeps Skip all dependency checks" - echo " -f, --force Overwrite existing packages" - echo " -i, --install Install package after successful build" - echo " -h, --help This help" - echo " -s, --syncdeps Install missing dependencies with pacman" - echo - echo " where is one or more directory names under the ABS root" - echo " eg: makeworld -c /packages base lib editors" - echo - echo " this should be run from the toplevel directory of ABS (usually /usr/abs)" + echo "makeworld version $version" + echo "usage: $0 [options] [category] ..." + echo "options:" + echo " -b, --builddeps Build missing dependencies from source" + echo " -c, --clean Clean up work files after build" + echo " -d, --nodeps Skip all dependency checks" + echo " -f, --force Overwrite existing packages" + echo " -i, --install Install package after successful build" + echo " -h, --help This help" + echo " -s, --syncdeps Install missing dependencies with pacman" + echo + echo " where is one or more directory names under the ABS root" + echo " eg: makeworld -c /packages base lib editors" + echo + echo " this should be run from the toplevel directory of ABS (usually /usr/abs)" } -if [ $# -lt 2 -o "$1" = "--help" -o "$1" = "-h" ]; then - usage - exit 1 +if [ $# -lt 2 ]; then + usage + exit 1 fi MAKEPKG_OPTS= for arg in $*; do - case $arg in - --clean) MAKEPKG_OPTS="$MAKEPKG_OPTS -c" ;; - --install) MAKEPKG_OPTS="$MAKEPKG_OPTS -i" ;; - --syncdeps) MAKEPKG_OPTS="$MAKEPKG_OPTS -s" ;; - --builddeps) MAKEPKG_OPTS="$MAKEPKG_OPTS -b" ;; - --nodeps) MAKEPKG_OPTS="$MAKEPKG_OPTS -d" ;; - --force) MAKEPKG_OPTS="$MAKEPKG_OPTS -f" ;; - --*) - usage - exit 1 - ;; - -*) - while getopts "cisbdf-" opt; do - case $opt in - c) MAKEPKG_OPTS="$MAKEPKG_OPTS -c" ;; - i) MAKEPKG_OPTS="$MAKEPKG_OPTS -i" ;; - s) MAKEPKG_OPTS="$MAKEPKG_OPTS -s" ;; - b) MAKEPKG_OPTS="$MAKEPKG_OPTS -b" ;; - d) MAKEPKG_OPTS="$MAKEPKG_OPTS -d" ;; - f) MAKEPKG_OPTS="$MAKEPKG_OPTS -f" ;; - -) - OPTIND=0 - break - ;; - esac - done - ;; - *) - dest=$arg - shift - break - ;; - esac - shift - if [ "$dest" != "" ]; then - break; - fi + case $arg in + --clean) MAKEPKG_OPTS="$MAKEPKG_OPTS -c" ;; + --install) MAKEPKG_OPTS="$MAKEPKG_OPTS -i" ;; + --syncdeps) MAKEPKG_OPTS="$MAKEPKG_OPTS -s" ;; + --builddeps) MAKEPKG_OPTS="$MAKEPKG_OPTS -b" ;; + --nodeps) MAKEPKG_OPTS="$MAKEPKG_OPTS -d" ;; + --force) MAKEPKG_OPTS="$MAKEPKG_OPTS -f" ;; + --help) + usage + exit 0 + ;; + --*) + usage + exit 1 + ;; + -*) + while getopts "chisbdf-" opt; do + case $opt in + c) MAKEPKG_OPTS="$MAKEPKG_OPTS -c" ;; + i) MAKEPKG_OPTS="$MAKEPKG_OPTS -i" ;; + s) MAKEPKG_OPTS="$MAKEPKG_OPTS -s" ;; + b) MAKEPKG_OPTS="$MAKEPKG_OPTS -b" ;; + d) MAKEPKG_OPTS="$MAKEPKG_OPTS -d" ;; + f) MAKEPKG_OPTS="$MAKEPKG_OPTS -f" ;; + h) + usage + exit 0 + ;; + -) + OPTIND=0 + break + ;; + esac + done + ;; + *) + dest=$arg + shift + break + ;; + esac + shift + if [ "$dest" != "" ]; then + break + fi done if [ "$dest" = "" ]; then - usage - exit 1 + usage + exit 1 fi sd=`date +"[%b %d %H:%M]"` for category in $*; do - for port in `find $toplevel/$category -type d -maxdepth 1 -mindepth 1 | sort`; do - cd $port - if [ -f PKGBUILD ]; then - . PKGBUILD - buildstatus=0 - if [ ! -f $dest/$pkgname-$pkgver-$pkgrel.pkg.tar.gz ]; then - makepkg $MAKEPKG_OPTS -w $dest 2>>$toplevel/makepkg.log - if [ $? -gt 0 ]; then - buildstatus=2 - else - buildstatus=1 - fi - fi - d=`date +"[%b %d %H:%M]"` - echo -n "$d " >>$toplevel/build.log - case $buildstatus in - 0) echo "$pkgname already built -- skipping" >>$toplevel/build.log ;; - 1) echo "$pkgname was built successfully" >>$toplevel/build.log ;; - 2) echo "$pkgname build failed" >>$toplevel/build.log ;; - esac - fi - done + for port in `find $toplevel/$category -type d -maxdepth 1 -mindepth 1 | sort`; do + cd $port + if [ -f PKGBUILD ]; then + . PKGBUILD + buildstatus=0 + if [ ! -f $dest/$pkgname-$pkgver-$pkgrel.pkg.tar.gz ]; then + makepkg $MAKEPKG_OPTS -w $dest 2>>$toplevel/makepkg.log + if [ $? -gt 0 ]; then + buildstatus=2 + else + buildstatus=1 + fi + fi + d=`date +"[%b %d %H:%M]"` + echo -n "$d " >>$toplevel/build.log + case $buildstatus in + 0) echo "$pkgname already built -- skipping" >>$toplevel/build.log ;; + 1) echo "$pkgname was built successfully" >>$toplevel/build.log ;; + 2) echo "$pkgname build failed" >>$toplevel/build.log ;; + esac + fi + done done ed=`date +"[%b %d %H:%M]"` @@ -104,3 +132,4 @@ echo "makeworld complete." >>$toplevel/build.log echo " started: $sd" >>$toplevel/build.log echo " finished: $ed" >>$toplevel/build.log +exit 0 diff --git a/src/convertdb.c b/src/convertdb.c index d337dcca..0a414e54 100644 --- a/src/convertdb.c +++ b/src/convertdb.c @@ -1,5 +1,5 @@ /* - * pacman + * convertdb.c * * Copyright (c) 2002 by Judd Vinet * diff --git a/src/db.c b/src/db.c index bf7be9f9..aea78ccb 100644 --- a/src/db.c +++ b/src/db.c @@ -1,5 +1,5 @@ /* - * pacman + * db.c * * Copyright (c) 2002 by Judd Vinet * @@ -29,13 +29,7 @@ #include "util.h" #include "db.h" -/* Verify database integrity and build a list of - * installed packages - * - * returns: 0 on success - * 1 if db is not initialized - * 2 if db is corrupt - */ +/* Open a database and return a pacdb_t handle */ pacdb_t* db_open(char *root, char *pkgdir, char *treename) { pacdb_t *db = NULL; @@ -111,7 +105,7 @@ PMList* db_loadpkgs(pacdb_t *db, PMList *pkgcache) cache = list_add(cache, arr[i]); } - free(arr); + FREE(arr); return(cache); } @@ -216,6 +210,11 @@ pkginfo_t* db_read(pacdb_t *db, struct dirent *ent, unsigned int inforeq) return(NULL); } trim(info->desc); + } else if(!strcmp(line, "%GROUPS%")) { + while(fgets(line, 512, fp) && strlen(trim(line))) { + char *s = strdup(line); + info->groups = list_add(info->groups, s); + } } else if(!strcmp(line, "%URL%")) { if(fgets(info->url, sizeof(info->url), fp) == NULL) { return(NULL); @@ -243,6 +242,14 @@ pkginfo_t* db_read(pacdb_t *db, struct dirent *ent, unsigned int inforeq) } trim(tmp); info->size = atol(tmp); + } else if(!strcmp(line, "%REPLACES%")) { + /* the REPLACES tag is special -- it only appears in sync repositories, + * not the local one. + */ + while(fgets(line, 512, fp) && strlen(trim(line))) { + char *s = strdup(line); + info->replaces = list_add(info->replaces, s); + } } } fclose(fp); @@ -303,6 +310,12 @@ pkginfo_t* db_read(pacdb_t *db, struct dirent *ent, unsigned int inforeq) info->conflicts = list_add(info->conflicts, s); } } + if(!strcmp(line, "%PROVIDES%")) { + while(fgets(line, 512, fp) && strlen(trim(line))) { + char *s = strdup(line); + info->provides = list_add(info->provides, s); + } + } } fclose(fp); } @@ -347,6 +360,11 @@ int db_write(pacdb_t *db, pkginfo_t *info) fprintf(fp, "%s\n\n", info->version); fputs("%DESC%\n", fp); fprintf(fp, "%s\n\n", info->desc); + fputs("%GROUPS%\n", fp); + for(lp = info->groups; lp; lp = lp->next) { + fprintf(fp, "%s\n", (char*)lp->data); + } + fprintf(fp, "\n"); fputs("%URL%\n", fp); fprintf(fp, "%s\n\n", info->url); fputs("%BUILDDATE%\n", fp); @@ -400,6 +418,11 @@ int db_write(pacdb_t *db, pkginfo_t *info) fprintf(fp, "%s\n", (char*)lp->data); } fprintf(fp, "\n"); + fputs("%PROVIDES%\n", fp); + for(lp = info->provides; lp; lp = lp->next) { + fprintf(fp, "%s\n", (char*)lp->data); + } + fprintf(fp, "\n"); fclose(fp); /* INSTALL */ @@ -426,7 +449,6 @@ PMList* db_find_conflicts(pacdb_t *db, PMList *targets, char *root) * pkginfo_t *info = NULL; char *dbstr = NULL; - vprint("Checking database against targets...\n"); rewinddir(db->dir); while((info = db_scan(db, NULL, INFRQ_DESC | INFRQ_FILES)) != NULL) { for(i = info->files; i; i = i->next) { @@ -453,7 +475,6 @@ PMList* db_find_conflicts(pacdb_t *db, PMList *targets, char *root) }*/ /* CHECK 2: check every target against every target */ - /* orelien - vprint("Checking targets against targets...\n"); */ for(i = targets; i; i = i->next) { pkginfo_t *p1 = (pkginfo_t*)i->data; for(j = i; j; j = j->next) { @@ -480,7 +501,6 @@ PMList* db_find_conflicts(pacdb_t *db, PMList *targets, char *root) } /* CHECK 3: check every target against the filesystem */ - /* orelien - vprint("Checking targets against filesystem...\n"); */ for(i = targets; i; i = i->next) { pkginfo_t *p = (pkginfo_t*)i->data; pkginfo_t *dbpkg = NULL; @@ -509,4 +529,72 @@ PMList* db_find_conflicts(pacdb_t *db, PMList *targets, char *root) return(conflicts); } +PMList *whatprovides(pacdb_t *db, char* package) +{ + PMList *pkgs, *i = NULL; + pkginfo_t *info; + + rewinddir(db->dir); + while((info = db_scan(db, NULL, INFRQ_DESC | INFRQ_DEPENDS)) != NULL) { + if(is_in(package, info->provides)) { + i = list_add(i, strdup(info->name)); + } + freepkg(info); + } + pkgs = list_sort(i); + list_free(i); + + return(pkgs); +} + +/* + * return a list of all groups present in *db + * + */ +PMList *find_groups(pacdb_t *db) +{ + PMList *groups, *i = NULL; + PMList *lp = NULL; + pkginfo_t *info; + + rewinddir(db->dir); + while((info = db_scan(db, NULL, INFRQ_DESC)) != NULL) { + for(lp = info->groups; lp; lp = lp->next) { + if(!is_in((char*)lp->data, i)) { + i = list_add(i, strdup((char*)lp->data)); + } + } + freepkg(info); + } + groups = list_sort(i); + list_free(i); + + return(groups); +} + +/* + * return a list of all members of the specified group + * + */ +PMList *pkg_ingroup(pacdb_t *db, char *group) +{ + PMList *pkg, *i = NULL; + PMList *lp = NULL; + pkginfo_t *info; + + rewinddir(db->dir); + while((info = db_scan(db, NULL, INFRQ_DESC)) != NULL) { + for(lp = info->groups; lp; lp = lp->next) { + if(!strcmp((char*)lp->data, group)) { + i = list_add(i, strdup(info->name)); + } + } + freepkg(info); + } + pkg = list_sort(i); + list_free(i); + + return(pkg); +} + /* vim: set ts=2 sw=2 noet: */ diff --git a/src/db.h b/src/db.h index 11404134..8f1424ba 100644 --- a/src/db.h +++ b/src/db.h @@ -1,5 +1,5 @@ /* - * pacman + * db.h * * Copyright (c) 2002 by Judd Vinet * @@ -42,6 +42,9 @@ pkginfo_t* db_scan(pacdb_t *db, char *target, unsigned int inforeq); pkginfo_t* db_read(pacdb_t *db, struct dirent *ent, unsigned int inforeq); int db_write(pacdb_t *db, pkginfo_t *info); PMList* db_find_conflicts(pacdb_t *db, PMList* targets, char *root); +PMList *whatprovides(pacdb_t *db, char* package); +PMList *find_groups(pacdb_t *db); +PMList *pkg_ingroup(pacdb_t *db, char *group); #endif /* vim: set ts=2 sw=2 noet: */ diff --git a/src/list.c b/src/list.c index dfb8f630..37f96530 100644 --- a/src/list.c +++ b/src/list.c @@ -1,5 +1,5 @@ /* - * pacman + * list.c * * Copyright (c) 2002 by Judd Vinet * @@ -174,7 +174,9 @@ PMList *list_sort(PMList *list) lp = list_add(lp, strdup(arr[i])); } - free(arr); + if(arr) { + free(arr); + } return(lp); } diff --git a/src/list.h b/src/list.h index edfc6f97..16c7c19d 100644 --- a/src/list.h +++ b/src/list.h @@ -1,5 +1,5 @@ /* - * pacman + * list.h * * Copyright (c) 2002 by Judd Vinet * diff --git a/src/package.c b/src/package.c index d41e1d85..b4a26387 100644 --- a/src/package.c +++ b/src/package.c @@ -1,7 +1,7 @@ /* - * pacman + * package.c * - * Copyright (c) 2002 by Judd Vinet + * Copyright (c) 2002-2003 by Judd Vinet * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -181,6 +181,8 @@ int parse_descfile(char *descfile, pkginfo_t *info, PMList **backup, int output) strncpy(info->version, ptr, sizeof(info->version)); } else if(!strcmp(key, "PKGDESC")) { strncpy(info->desc, ptr, sizeof(info->desc)); + } else if(!strcmp(key, "GROUP")) { + info->groups = list_add(info->groups, strdup(ptr)); } else if(!strcmp(key, "URL")) { strncpy(info->url, ptr, sizeof(info->url)); } else if(!strcmp(key, "BUILDDATE")) { @@ -193,15 +195,16 @@ int parse_descfile(char *descfile, pkginfo_t *info, PMList **backup, int output) char tmp[32]; strncpy(tmp, ptr, sizeof(tmp)); info->size = atol(tmp); - } else if(!strcmp(key, "DEPEND")) { - char *s = strdup(ptr); - info->depends = list_add(info->depends, s); + } else if(!strcmp(key, "DEPEND")) { + info->depends = list_add(info->depends, strdup(ptr)); } else if(!strcmp(key, "CONFLICT")) { - char *s = strdup(ptr); - info->conflicts = list_add(info->conflicts, s); + info->conflicts = list_add(info->conflicts, strdup(ptr)); + } else if(!strcmp(key, "REPLACES")) { + info->replaces = list_add(info->replaces, strdup(ptr)); + } else if(!strcmp(key, "PROVIDES")) { + info->provides = list_add(info->provides, strdup(ptr)); } else if(!strcmp(key, "BACKUP")) { - char *s = strdup(ptr); - bak = list_add(bak, s); + bak = list_add(bak, strdup(ptr)); } else { fprintf(stderr, "%s: syntax error in description file line %d\n", info->name[0] != '\0' ? info->name : "error", linenum); @@ -235,6 +238,9 @@ pkginfo_t* newpkg() pkg->files = NULL; pkg->backup = NULL; pkg->depends = NULL; + pkg->groups = NULL; + pkg->provides = NULL; + pkg->replaces = NULL; return(pkg); } @@ -250,6 +256,9 @@ void freepkg(pkginfo_t *pkg) list_free(pkg->depends); list_free(pkg->conflicts); list_free(pkg->requiredby); + list_free(pkg->groups); + list_free(pkg->provides); + list_free(pkg->replaces); FREE(pkg); return; } @@ -298,24 +307,30 @@ void dump_pkg(pkginfo_t *info) return; } - printf("Name : %s\n", info->name); - printf("Version : %s\n", info->version); - printf("Packager : %s\n", info->packager); - printf("URL: : %s\n", info->url); - printf("Size : %ld\n", info->size); - printf("Build Date : %s %s\n", info->builddate, strlen(info->builddate) ? "UTC" : ""); - printf("Install Date : %s %s\n", info->installdate, strlen(info->installdate) ? "UTC" : ""); - printf("Install Script: %s\n", (info->scriptlet ? "yes" : "no")); + printf("Name : %s\n", info->name); + printf("Version : %s\n", info->version); + pm = list_sort(info->groups); + list_display("Groups : ", pm); + FREE(pm); + printf("Packager : %s\n", info->packager); + printf("URL : %s\n", (info->url ? info->url : "None")); + printf("Size : %ld\n", info->size); + printf("Build Date : %s %s\n", info->builddate, strlen(info->builddate) ? "UTC" : ""); + printf("Install Date : %s %s\n", info->installdate, strlen(info->installdate) ? "UTC" : ""); + printf("Install Script : %s\n", (info->scriptlet ? "Yes" : "No")); + pm = list_sort(info->provides); + list_display("Provides : ", pm); + FREE(pm); pm = list_sort(info->depends); - list_display("Depends On : ", pm); + list_display("Depends On : ", pm); FREE(pm); pm = list_sort(info->requiredby); - list_display("Required By : ", pm); + list_display("Required By : ", pm); FREE(pm); pm = list_sort(info->conflicts); - list_display("Conflicts With: ", pm); + list_display("Conflicts With : ", pm); FREE(pm); - printf("Description : %s\n", info->desc); + printf("Description : %s\n", info->desc); } /* vim: set ts=2 sw=2 noet: */ diff --git a/src/package.h b/src/package.h index 72b015c3..91d87e12 100644 --- a/src/package.h +++ b/src/package.h @@ -1,5 +1,5 @@ /* - * pacman + * package.h * * Copyright (c) 2002 by Judd Vinet * @@ -41,11 +41,14 @@ typedef struct __pkginfo_t { char packager[64]; unsigned long size; unsigned short scriptlet; + PMList *replaces; + PMList *groups; PMList *files; PMList *backup; PMList *depends; PMList *requiredby; PMList *conflicts; + PMList *provides; } pkginfo_t; typedef struct __depend_t { diff --git a/src/pacman.c b/src/pacman.c index 35421ff5..d393914b 100644 --- a/src/pacman.c +++ b/src/pacman.c @@ -1,5 +1,5 @@ /* - * pacman + * pacman.c * * Copyright (c) 2002 by Judd Vinet * @@ -33,6 +33,7 @@ #include #include #include +#include #include /* pacman */ #include "rpmvercmp.h" @@ -72,10 +73,12 @@ unsigned short pmo_s_downloadonly = 0; unsigned short pmo_s_sync = 0; unsigned short pmo_s_search = 0; unsigned short pmo_s_clean = 0; +unsigned short pmo_group = 0; /* configuration file options */ char *pmo_dbpath = NULL; PMList *pmo_noupgrade = NULL; PMList *pmo_ignorepkg = NULL; +unsigned short pmo_usesyslog = 0; unsigned short pmo_nopassiveftp = 0; @@ -89,6 +92,7 @@ PMList *pm_targets = NULL; char *lckfile = "/tmp/pacman.lck"; char *workfile = NULL; enum {READ_ONLY, READ_WRITE} pm_access; +int maxcols = 80; int main(int argc, char *argv[]) { @@ -96,6 +100,12 @@ int main(int argc, char *argv[]) char *ptr = NULL; char path[PATH_MAX]; pacdb_t *db_local = NULL; + char *cenv = NULL; + + cenv = getenv("COLUMNS"); + if(cenv) { + maxcols = atoi(cenv); + } /* default root */ MALLOC(pmo_root, PATH_MAX); @@ -118,7 +128,7 @@ int main(int argc, char *argv[]) /* check for permission */ pm_access = READ_ONLY; if(pmo_op != PM_MAIN && pmo_op != PM_QUERY && pmo_op != PM_DEPTEST) { - if(pmo_op == PM_SYNC && pmo_s_search) { + if(pmo_op == PM_SYNC && !pmo_s_sync && (pmo_s_search || pmo_group)) { /* special case: PM_SYNC can be used w/ pmo_s_search by any user */ } else { if(geteuid() != 0) { @@ -136,11 +146,6 @@ int main(int argc, char *argv[]) } } - vprint("Installation Root: %s\n", pmo_root); - if(pmo_verbose) { - list_display("Targets: ", pm_targets); - } - /* set signal handlers */ signal(SIGINT, cleanup); signal(SIGTERM, cleanup); @@ -151,6 +156,10 @@ int main(int argc, char *argv[]) cleanup(1); } + if(pmo_usesyslog) { + openlog("pacman", 0, LOG_USER); + } + /* check for db existence */ /* add a trailing '/' if there isn't one */ if(pmo_root[strlen(pmo_root)-1] != '/') { @@ -160,8 +169,13 @@ int main(int argc, char *argv[]) FREE(pmo_root); pmo_root = ptr; } + + vprint("Installation Root: %s\n", pmo_root); /* db location */ vprint("Top-level DB Path: %s%s\n", pmo_root, pmo_dbpath); + if(pmo_verbose) { + list_display("Targets: ", pm_targets); + } db_local = db_open(pmo_root, pmo_dbpath, "local"); if(db_local == NULL) { @@ -281,10 +295,10 @@ int pacman_deptest(pacdb_t *db, PMList *targets) int pacman_sync(pacdb_t *db, PMList *targets) { int allgood = 1, confirm = 0; - int cols; PMList *i, *j, *k; - PMList *final = NULL; - PMList *trail = NULL; + PMList *rmtargs = NULL; /* conflicting packages to remove */ + PMList *final = NULL; /* packages to upgrade */ + PMList *trail = NULL; /* a breadcrumb trail to avoid running in circles */ PMList *databases = NULL; if(!list_count(pmc_syncs)) { @@ -302,7 +316,7 @@ int pacman_sync(pacdb_t *db, PMList *targets) } oldmask = umask(0000); - if(makepath("/var/cache/pacman/pkg")) { + if(makepath("/var/cache/pacman/pkg")) { fprintf(stderr, "error: could not create new cache directory: %s\n", strerror(errno)); return(1); } @@ -312,9 +326,12 @@ int pacman_sync(pacdb_t *db, PMList *targets) return(0); } - if(pmo_s_sync && !pmo_s_search) { + if(pmo_s_sync) { /* grab a fresh package list */ printf(":: Synchronizing package databases... \n"); + if(pmo_usesyslog) { + syslog(LOG_INFO, "synchronizing package lists"); + } sync_synctree(); } @@ -324,7 +341,7 @@ int pacman_sync(pacdb_t *db, PMList *targets) dbsync_t *dbs = NULL; sync_t *sync = (sync_t*)i->data; - db_sync = db_open(pmo_root, PKGDIR, sync->treename); + db_sync = db_open(pmo_root, pmo_dbpath, sync->treename); if(db_sync == NULL) { fprintf(stderr, "error: could not open sync database: %s\n", sync->treename); fprintf(stderr, " have you used --refresh yet?\n"); @@ -344,45 +361,157 @@ int pacman_sync(pacdb_t *db, PMList *targets) if(pmo_s_search) { /* search sync databases */ - for(i = targets; i; i = i->next) { - char *targ = strdup(i->data); - strtoupper(targ); - for(j = databases; j; j = j->next) { - dbsync_t *dbs = (dbsync_t*)j->data; - for(k = dbs->pkgcache; k; k = k->next) { - pkginfo_t *pkg = (pkginfo_t*)k->data; - char *haystack; - /* check name */ - haystack = strdup(pkg->name); - strtoupper(haystack); - if(strstr(haystack, targ)) { - printf("%s/%s %s\n ", dbs->sync->treename, pkg->name, pkg->version); - indentprint(pkg->desc, 4); - printf("\n"); - } else { - /* check description */ - FREE(haystack); - haystack = strdup(pkg->desc); + if(targets) { + for(i = targets; i; i = i->next) { + char *targ = strdup(i->data); + strtoupper(targ); + for(j = databases; j; j = j->next) { + dbsync_t *dbs = (dbsync_t*)j->data; + for(k = dbs->pkgcache; k; k = k->next) { + pkginfo_t *pkg = (pkginfo_t*)k->data; + char *haystack; + /* check name */ + haystack = strdup(pkg->name); strtoupper(haystack); if(strstr(haystack, targ)) { printf("%s/%s %s\n ", dbs->sync->treename, pkg->name, pkg->version); indentprint(pkg->desc, 4); printf("\n"); + } else { + /* check description */ + FREE(haystack); + haystack = strdup(pkg->desc); + strtoupper(haystack); + if(strstr(haystack, targ)) { + printf("%s/%s %s\n ", dbs->sync->treename, pkg->name, pkg->version); + indentprint(pkg->desc, 4); + printf("\n"); + } } + FREE(haystack); + } + } + FREE(targ); + } + } else { + for(j = databases; j; j = j->next) { + dbsync_t *dbs = (dbsync_t*)j->data; + for(k = dbs->pkgcache; k; k = k->next) { + pkginfo_t *pkg = (pkginfo_t*)k->data; + printf("%s/%s %s\n ", dbs->sync->treename, pkg->name, pkg->version); + indentprint(pkg->desc, 4); + printf("\n"); + } + } + } + } else if(pmo_group) { + PMList *pm, *allgroups, *groups; + i = NULL; + /* fetch the list of existing groups */ + for(j = databases; j; j = j->next) { + dbsync_t *dbs = (dbsync_t*)j->data; + k = find_groups(dbs->db); + for(pm = k; pm; pm = pm->next) { + if(!is_in((char *)pm->data, i)) { + i = list_add(i, strdup((char *)pm->data)); + } + } + list_free(k); + } + allgroups = list_sort(i); + list_free(i); + if(targets) { + groups = NULL; + for(j = targets; j; j = j->next) { + if(is_in((char *)j->data, allgroups)) { + groups = list_add(groups, (char *)j->data); + } + } + list_free(allgroups); + } else { + groups = allgroups; + } + /* display the packages belonging to groups */ + for(pm = groups; pm; pm = pm->next) { + PMList *pkg; + printf("%s\n", (char *)pm->data); + i = NULL; + for(j = databases; j; j = j->next) { + PMList *lp; + dbsync_t *dbs = (dbsync_t*)j->data; + k = pkg_ingroup(dbs->db, (char *)pm->data); + for(lp = k; lp; lp = lp->next) { + if(!is_in((char *)lp->data, i)) { + i = list_add(i, strdup((char *)lp->data)); } - FREE(haystack); } + list_free(k); } - FREE(targ); + pkg = list_sort(i); + list_free(i); + list_display(" ", pkg); + list_free(pkg); } + list_free(groups); } else if(pmo_s_upgrade) { int newer = 0; int ignore = 0; + if(pmo_usesyslog) { + syslog(LOG_INFO, "starting full system upgrade"); + } + /* check for "recommended" package replacements */ + for(i = databases; i && allgood; i = i->next) { + dbsync_t *dbs = (dbsync_t*)i->data; + for(j = dbs->pkgcache; j; j = j->next) { + pkginfo_t *pkg = (pkginfo_t*)j->data; + for(k = pkg->replaces; k; k = k->next) { + PMList *m; + for(m = pm_packages; m; m = m->next) { + pkginfo_t *p = (pkginfo_t*)m->data; + if(!strcmp(k->data, p->name)) { + /* if confirmed, add this to the 'final' list, designating 'p' as + * the package to replace. + */ + if(yesno(":: replace %s with %s from \"%s\"? [Y/n] ", p->name, pkg->name, dbs->db->treename)) { + syncpkg_t *sync = NULL; + /* we save the dependency info so we can move p's requiredby stuff + * over to the replacing package + */ + pkginfo_t *q = db_scan(db, p->name, INFRQ_DESC | INFRQ_DEPENDS); + + /* check if pkg->name is already in the final list. */ + sync = find_pkginsync(pkg->name, final); + if(sync) { + /* found it -- just append to the replaces list */ + sync->replaces = list_add(sync->replaces, q); + } else { + /* none found -- enter pkg into the final sync list */ + MALLOC(sync, sizeof(syncpkg_t)); + sync->dbs = dbs; + sync->replaces = NULL; + sync->replaces = list_add(sync->replaces, q); + sync->pkg = db_scan(sync->dbs->db, pkg->name, INFRQ_DESC | INFRQ_DEPENDS); + /* add to the targets list */ + allgood = !resolvedeps(db, databases, sync, final, trail); + /* check again, as resolvedeps could have added our target for us */ + if(find_pkginsync(sync->pkg->name, final) == NULL) { + final = list_add(final, sync); + } + } + } + break; + } + } + } + } + } + /* match installed packages with the sync dbs and compare versions */ for(i = pm_packages; i && allgood; i = i->next) { int cmp, found = 0; pkginfo_t *local = (pkginfo_t*)i->data; syncpkg_t *sync = NULL; MALLOC(sync, sizeof(syncpkg_t)); + sync->replaces = NULL; for(j = databases; !found && j; j = j->next) { dbsync_t *dbs = (dbsync_t*)j->data; @@ -426,11 +555,11 @@ int pacman_sync(pacdb_t *db, PMList *targets) sync->pkg = db_scan(sync->dbs->db, sync->pkg->name, INFRQ_DESC | INFRQ_DEPENDS); /* add to the targets list */ - found = is_pkginsync(sync, final); + found = (find_pkginsync(sync->pkg->name, final) != NULL); if(!found) { allgood = !resolvedeps(db, databases, sync, final, trail); /* check again, as resolvedeps could have added our target for us */ - found = is_pkginsync(sync, final); + found = (find_pkginsync(sync->pkg->name, final) != NULL); if(!found) { final = list_add(final, sync); } @@ -443,17 +572,24 @@ int pacman_sync(pacdb_t *db, PMList *targets) /* process targets */ for(i = targets; i && allgood; i = i->next) { if(i->data) { - int cmp, found = 0; + int cmp, found = 0, group = 0; pkginfo_t *local; syncpkg_t *sync = NULL; MALLOC(sync, sizeof(syncpkg_t)); + sync->replaces = NULL; local = db_scan(db, (char*)i->data, INFRQ_DESC); for(j = databases; !found && j; j = j->next) { dbsync_t *dbs = (dbsync_t*)j->data; for(k = dbs->pkgcache; !found && k; k = k->next) { pkginfo_t *pkg = (pkginfo_t*)k->data; - if(!strcmp((char*)i->data, pkg->name)) { + if(is_in((char*)i->data, pkg->groups)) { + group = 1; + if(!yesno(":: install %s from group %s? [Y/n] ", pkg->name, (char*)i->data)) { + continue; + } + targets = list_add(targets, strdup(pkg->name)); + } else if(!strcmp((char*)i->data, pkg->name)) { found = 1; sync->dbs = dbs; /* re-fetch the package record with dependency info */ @@ -464,9 +600,11 @@ int pacman_sync(pacdb_t *db, PMList *targets) } } } - if(!found) { - fprintf(stderr, "%s: not found in sync db\n", (char*)i->data); - allgood = 0; + if(!found || group) { + if(!group) { + fprintf(stderr, "%s: not found in sync db\n", (char*)i->data); + allgood = 0; + } freepkg(local); FREE(sync); continue; @@ -494,11 +632,11 @@ int pacman_sync(pacdb_t *db, PMList *targets) } freepkg(local); - found = is_pkginsync(sync, final); + found = (find_pkginsync(sync->pkg->name, final) != NULL); if(!found && !pmo_nodeps) { allgood = !resolvedeps(db, databases, sync, final, trail); /* check again, as resolvedeps could have added our target for us */ - found = is_pkginsync(sync, final); + found = (find_pkginsync(sync->pkg->name, final) != NULL); } if(!found) { final = list_add(final, sync); @@ -509,25 +647,26 @@ int pacman_sync(pacdb_t *db, PMList *targets) if(allgood) { /* check for inter-conflicts and whatnot */ - PMList *deps = NULL; - PMList *list = NULL; + if(!pmo_nodeps && !pmo_s_downloadonly) { + int errorout = 0; + PMList *deps = NULL; + PMList *list = NULL; - for(i = final; i; i = i->next) { - syncpkg_t *s = (syncpkg_t*)i->data; - if(s) { - list = list_add(list, s->pkg); + for(i = final; i; i = i->next) { + syncpkg_t *s = (syncpkg_t*)i->data; + if(s) { + list = list_add(list, s->pkg); + } } - } - - if(!pmo_nodeps && !pmo_s_upgrade) { deps = checkdeps(db, PM_UPGRADE, list); if(deps) { - fprintf(stderr, "error: unresolvable conflicts/dependencies:\n"); for(i = deps; i; i = i->next) { depmissing_t *miss = (depmissing_t*)i->data; - if(miss->type == CONFLICT) { - fprintf(stderr, " %s: conflicts with %s\n", miss->target, miss->depend.name); - } else if(miss->type == DEPEND || miss->type == REQUIRED) { + if(miss->type == DEPEND || miss->type == REQUIRED) { + if(!errorout) { + fprintf(stderr, "error: unresolvable dependencies:\n"); + errorout = 1; + } fprintf(stderr, " %s: requires %s", miss->target, miss->depend.name); switch(miss->depend.mod) { case DEP_EQ: fprintf(stderr, "=%s", miss->depend.version); break; @@ -540,55 +679,137 @@ int pacman_sync(pacdb_t *db, PMList *targets) fprintf(stderr, "\n"); } } - FREE(miss); - i->data = NULL; + } + if(!errorout) { + int found; + errorout = 0; + /* no unresolvable deps, so look for conflicts */ + for(i = deps; i && !errorout; i = i->next) { + depmissing_t *miss = (depmissing_t*)i->data; + if(miss->type == CONFLICT) { + /* check if the conflicting package is one that's about to be removed/replaced. + * if so, then just ignore it + */ + found = 0; + for(j = final; j && !found; j = j->next) { + syncpkg_t *sync = (syncpkg_t*)j->data; + for(k = sync->replaces; k && !found; k = k->next) { + pkginfo_t *p = (pkginfo_t*)k->data; + if(!strcmp(p->name, miss->depend.name)) { + found = 1; + } + } + } + /* if we didn't find it in any sync->replaces lists, then it's a conflict */ + if(!found && !is_in(miss->depend.name, rmtargs)) { + pkginfo_t p1; + /* build a "fake" pkginfo_t so we can search with is_pkgin() */ + snprintf(p1.name, sizeof(p1.name), miss->depend.name); + sprintf(p1.version, "1.0-1"); + + if(is_pkgin(&p1, pm_packages)) { + if(yesno(":: %s conflicts with %s. Remove %s? [Y/n] ", + miss->target, miss->depend.name, miss->depend.name)) { + /* remove miss->depend.name */ + rmtargs = list_add(rmtargs, strdup(miss->depend.name)); + } else { + /* abort */ + fprintf(stderr, "\nerror: package conflicts detected\n"); + errorout = 1; + } + } else { + if(!is_in(miss->depend.name, rmtargs) & !is_in(miss->target, rmtargs)) { + fprintf(stderr, "\nerror: %s conflicts with %s\n", miss->target, + miss->depend.name); + errorout = 1; + } + } + } + } + } } list_free(deps); - /* abort mission */ - allgood = 0; + if(errorout) { + /* abort mission */ + allgood = 0; + } + } + /* cleanup */ + for(i = list; i; i = i->next) { + i->data = NULL; } + list_free(list); + list = NULL; } - /* cleanup */ - for(i = list; i; i = i->next) { + /* any packages in rmtargs need to be removed from final. */ + /* rather than ripping out nodes from final, we just copy over */ + /* our "good" nodes to a new list and reassign. */ + k = NULL; + for(i = final; i; i = i->next) { + syncpkg_t *s = (syncpkg_t*)i->data; + int keepit = 1; + for(j = rmtargs; j && keepit; j = j->next) { + if(!strcmp(j->data, s->pkg->name)) { + FREE(i->data); + keepit = 0; + } + } + if(keepit) { + k = list_add(k, s); + } i->data = NULL; } - list_free(list); - list = NULL; + list_free(final); + final = k; /* list targets */ - if(final && final->data) { - fprintf(stderr, "\nTargets: "); - } - cols = 9; - for(i = final; allgood && i; i = i->next) { - syncpkg_t *s = (syncpkg_t*)i->data; - if(s && s->pkg) { - char t[PATH_MAX]; - int len; - snprintf(t, PATH_MAX, "%s-%s ", s->pkg->name, s->pkg->version); - len = strlen(t); - if(len+cols > 78) { - cols = 9; - fprintf(stderr, "\n%9s", " "); + if(final && final->data && allgood) { + PMList *list = NULL; + for(i = rmtargs; i; i = i->next) { + list = list_add(list, strdup(i->data)); + } + for(i = final; i; i = i->next) { + syncpkg_t *s = (syncpkg_t*)i->data; + for(j = s->replaces; j; j = j->next) { + pkginfo_t *p = (pkginfo_t*)j->data; + list = list_add(list, strdup(p->name)); + } + } + if(list) { + printf("\nRemove: "); + indentprint(buildstring(list), 9); + printf("\n"); + list_free(list); + list = NULL; + } + for(i = final; i; i = i->next) { + syncpkg_t *s = (syncpkg_t*)i->data; + if(s && s->pkg) { + char *str = NULL; + MALLOC(str, strlen(s->pkg->name)+strlen(s->pkg->version)+2); + sprintf(str, "%s-%s", s->pkg->name, s->pkg->version); + list = list_add(list, str); } - fprintf(stderr, "%s", t); - cols += len; } + printf("\nTargets: "); + indentprint(buildstring(list), 9); + printf("\n"); + list_free(list); + list = NULL; } - printf("\n"); /* get confirmation */ confirm = 0; if(allgood && final && final->data) { if(pmo_s_downloadonly) { - confirm = yesno("\nDo you want to download these packages? [Y/n] "); + confirm = yesno("\nProceed with download? [Y/n] "); } else { /* don't get any confirmation if we're called from makepkg */ if(pmo_d_resolve) { confirm = 1; } else { - confirm = yesno("\nDo you want to install/upgrade these packages? [Y/n] "); + confirm = yesno("\nProceed with upgrade? [Y/n] "); } } } @@ -646,21 +867,21 @@ int pacman_sync(pacdb_t *db, PMList *targets) /* no cache directory.... try creating it */ snprintf(parent, PATH_MAX, "%svar/cache/pacman", pmo_root); - fprintf(stderr, "warning: no %s cache exists. creating...\n", ldir); + logaction("warning: no %s cache exists. creating...\n", ldir); oldmask = umask(0000); mkdir(parent, 0755); if(mkdir(ldir, 0755)) { /* couldn't mkdir the cache directory, so fall back to /tmp and unlink * the package afterwards. */ - fprintf(stderr, "warning: couldn't create package cache, using /tmp instead\n"); + logaction("warning: couldn't create package cache, using /tmp instead\n"); snprintf(ldir, PATH_MAX, "/tmp"); varcache = 0; } umask(oldmask); } if(downloadfiles(current->servers, ldir, files)) { - fprintf(stderr, "error: failed to retrieve some files from %s.\n", current->treename); + fprintf(stderr, "error: failed to retrieve some files from %s\n", current->treename); allgood = 0; } count += list_count(files); @@ -680,7 +901,21 @@ int pacman_sync(pacdb_t *db, PMList *targets) } if(!pmo_s_downloadonly) { - /* install targets */ + /* remove any conflicting packages (WITH dep checks) */ + if(rmtargs) { + int retcode; + retcode = pacman_remove(db, rmtargs); + list_free(rmtargs); + rmtargs = NULL; + if(retcode == 1) { + fprintf(stderr, "\nupgrade aborted.\n"); + allgood = 0; + } + /* reload package cache */ + pm_packages = db_loadpkgs(db, pm_packages); + } + list_free(rmtargs); + rmtargs = NULL; for(i = final; allgood && i; i = i->next) { char *str; syncpkg_t *sync = (syncpkg_t*)i->data; @@ -689,10 +924,58 @@ int pacman_sync(pacdb_t *db, PMList *targets) snprintf(str, PATH_MAX, "%s/%s-%s.pkg.tar.gz", ldir, sync->pkg->name, sync->pkg->version); files = list_add(files, str); } + for(j = sync->replaces; j; j = j->next) { + pkginfo_t *pkg = (pkginfo_t*)j->data; + rmtargs = list_add(rmtargs, pkg->name); + } + } + /* remove to-be-replaced packages */ + if(allgood && rmtargs) { + int oldval = pmo_nodeps; + /* we make pacman_remove() skip dependency checks by setting pmo_nodeps high */ + pmo_nodeps = 1; + allgood = !pacman_remove(db, rmtargs); + pmo_nodeps = oldval; + if(!allgood) { + fprintf(stderr, "package removal failed. aborting...\n"); + } } + /* install targets */ if(allgood) { allgood = !pacman_upgrade(db, files); } + /* propagate replaced packages' requiredby fields to their new owners */ + if(allgood) { + for(i = final; i; i = i->next) { + syncpkg_t *sync = (syncpkg_t*)i->data; + if(sync->replaces) { + pkginfo_t *new = db_scan(db, sync->pkg->name, INFRQ_ALL); + for(j = sync->replaces; j; j = j->next) { + pkginfo_t *old = (pkginfo_t*)j->data; + /* merge lists */ + for(k = old->requiredby; k; k = k->next) { + if(!is_in(k->data, new->requiredby)) { + /* replace old's name with new's name in the requiredby's dependency list */ + PMList *m; + pkginfo_t *depender = db_scan(db, k->data, INFRQ_ALL); + for(m = depender->depends; m; m = m->next) { + if(!strcmp(m->data, old->name)) { + FREE(m->data); + m->data = strdup(new->name); + } + } + db_write(db, depender); + + /* add the new requiredby */ + new->requiredby = list_add(new->requiredby, k->data); + } + } + } + db_write(db, new); + freepkg(new); + } + } + } } if(!varcache && !pmo_s_downloadonly) { @@ -706,7 +989,14 @@ int pacman_sync(pacdb_t *db, PMList *targets) /* cleanup */ for(i = final; i; i = i->next) { syncpkg_t *sync = (syncpkg_t*)i->data; - if(sync) freepkg(sync->pkg); + if(sync) { + freepkg(sync->pkg); + for(j = sync->replaces; j; j = j->next) { + freepkg(j->data); + j->data = NULL; + } + list_free(sync->replaces); + } FREE(sync); i->data = NULL; } @@ -717,6 +1007,14 @@ int pacman_sync(pacdb_t *db, PMList *targets) for(i = databases; i; i = i->next) { dbsync_t *dbs = (dbsync_t*)i->data; db_close(dbs->db); + for(j = dbs->pkgcache; j; j = j->next) { + if(j->data) { + /* XXX: this freepkg() still locks up, but only when the "replaces" + * code has been run + freepkg(j->data);*/ + j->data = NULL; + } + } list_free(dbs->pkgcache); FREE(dbs); i->data = NULL; @@ -724,6 +1022,7 @@ int pacman_sync(pacdb_t *db, PMList *targets) list_free(databases); list_free(final); list_free(trail); + list_free(rmtargs); return(!allgood); } @@ -735,7 +1034,7 @@ int pacman_add(pacdb_t *db, PMList *targets) char pm_install[PATH_MAX]; pkginfo_t *info = NULL; struct stat buf; - PMList *targ, *file, *lp, *j; + PMList *targ, *file, *lp, *j, *k; PMList *alltargs = NULL; PMList *filenames = NULL; unsigned short real_pmo_upgrade; @@ -777,33 +1076,115 @@ int pacman_add(pacdb_t *db, PMList *targets) /* No need to check deps if pacman_add was called during a sync: * it is already done in pacman_sync. */ if(!pmo_nodeps && pmo_op != PM_SYNC) { + int errorout = 0; vprint("checking dependencies...\n"); lp = checkdeps(db, (pmo_upgrade ? PM_UPGRADE : PM_ADD), alltargs); if(lp) { - fprintf(stderr, "error: unsatisfied dependencies:\n"); + /* look for unsatisfied dependencies */ for(j = lp; j; j = j->next) { depmissing_t* miss = (depmissing_t*)j->data; - printf(" %s: ", miss->target); if(miss->type == DEPEND || miss->type == REQUIRED) { - printf("requires %s", miss->depend.name); + if(!errorout) { + fprintf(stderr, "error: unsatisfied dependencies:\n"); + errorout = 1; + } + printf(" %s: requires %s", miss->target, miss->depend.name); switch(miss->depend.mod) { case DEP_EQ: printf("=%s", miss->depend.version); break; case DEP_GE: printf(">=%s", miss->depend.version); break; case DEP_LE: printf("<=%s", miss->depend.version); break; } printf("\n"); - } else if(miss->type == CONFLICT) { - printf("conflicts with %s\n", miss->depend.name); } } + if(!errorout) { + PMList *rmtargs = NULL; + errorout = 0; + /* no unsatisfied deps, so look for conflicts */ + for(j = lp; j && !errorout; j = j->next) { + depmissing_t* miss = (depmissing_t*)j->data; + if(miss->type == CONFLICT && !is_in(miss->depend.name, rmtargs)) { + pkginfo_t p1; + /* build a "fake" pkginfo_t so we can search with is_pkgin() */ + snprintf(p1.name, sizeof(p1.name), miss->depend.name); + sprintf(p1.version, "1.0-1"); + + if(is_pkgin(&p1, pm_packages)) { + if(yesno(":: %s conflicts with %s. Remove %s? [Y/n] ", + miss->target, miss->depend.name, miss->depend.name)) { + /* remove miss->depend.name */ + rmtargs = list_add(rmtargs, strdup(miss->depend.name)); + } else { + /* abort */ + fprintf(stderr, "\nerror: package conflicts detected\n"); + errorout = 1; + } + } else { + if(!is_in(miss->depend.name, rmtargs) & !is_in(miss->target, rmtargs)) { + fprintf(stderr, "\nerror: %s conflicts with %s\n", miss->target, + miss->depend.name); + errorout = 1; + } + } + } + } + if(rmtargs && !errorout) { + int retcode; + int oldupg = pmo_upgrade; + /* any packages in rmtargs need to be removed from alltargs. */ + /* rather than ripping out nodes from alltargs, we just copy over */ + /* our "good" nodes to a new list and reassign. */ + k = NULL; + for(lp = alltargs; lp; lp = lp->next) { + pkginfo_t *p = (pkginfo_t*)lp->data; + int keepit = 1; + for(j = rmtargs; j && keepit; j = j->next) { + if(!strcmp(j->data, p->name)) { + /* gone! */ + FREE(lp->data); + keepit = 0; + } + } + if(keepit) { + k = list_add(k, p); + } + lp->data = NULL; + } + list_free(alltargs); + alltargs = k; + /* make sure pacman_remove does it's own dependency check */ + pmo_upgrade = 0; + retcode = pacman_remove(db, rmtargs); + list_free(rmtargs); + if(retcode == 1) { + fprintf(stderr, "\n%s aborted.\n", oldupg ? "upgrade" : "install"); + return(1); + } + /* reload package cache */ + pm_packages = db_loadpkgs(db, pm_packages); + pmo_upgrade = oldupg; + } + } + if(errorout) { + list_free(lp); + return(1); + } list_free(lp); - return(1); } - list_free(lp); + + /* re-order w.r.t. dependencies */ + vprint("sorting by dependencies\n"); + lp = sortbydeps(alltargs); + /* free the old alltargs */ + for(j = alltargs; j; j = j->next) { + j->data = NULL; + } + list_free(alltargs); + alltargs = lp; } if(!pmo_force) { - printf("checking for conflicts... "); + printf("checking for file conflicts... "); fflush(stdout); lp = db_find_conflicts(db, alltargs, pmo_root); if(lp) { @@ -848,12 +1229,12 @@ int pacman_add(pacdb_t *db, PMList *targets) vprint("removing old package first...\n"); retcode = pacman_remove(db, tmp); list_free(tmp); - /* reload package cache */ - pm_packages = db_loadpkgs(db, pm_packages); if(retcode == 1) { fprintf(stderr, "\nupgrade aborted.\n"); return(1); } + /* reload package cache */ + pm_packages = db_loadpkgs(db, pm_packages); } else { /* no previous package version is installed, so this is actually just an * install @@ -887,7 +1268,7 @@ int pacman_add(pacdb_t *db, PMList *targets) if(!strcmp(pathname, "._install") || !strcmp(pathname, ".INSTALL")) { /* the install script goes inside the db */ snprintf(expath, PATH_MAX, "%s%s/%s/%s-%s/install", pmo_root, - PKGDIR, db->treename, info->name, info->version); + pmo_dbpath, db->treename, info->name, info->version); } else { /* build the new pathname relative to pmo_root */ snprintf(expath, PATH_MAX, "%s%s", pmo_root, pathname); @@ -919,7 +1300,7 @@ int pacman_add(pacdb_t *db, PMList *targets) temp = strdup("/tmp/pacman_XXXXXX"); mkstemp(temp); if(tar_extract_file(tar, temp)) { - fprintf(stderr, "could not extract %s: %s\n", pathname, strerror(errno)); + logaction("could not extract %s: %s\n", pathname, strerror(errno)); errors++; continue; } @@ -956,13 +1337,13 @@ int pacman_add(pacdb_t *db, PMList *targets) char newpath[PATH_MAX]; snprintf(newpath, PATH_MAX, "%s.pacorig", expath); if(rename(expath, newpath)) { - fprintf(stderr, "error: could not rename %s: %s\n", expath, strerror(errno)); + logaction("error: could not rename %s: %s\n", expath, strerror(errno)); } if(copyfile(temp, expath)) { - fprintf(stderr, "error: could not copy %s to %s: %s\n", temp, expath, strerror(errno)); + logaction("error: could not copy %s to %s: %s\n", temp, expath, strerror(errno)); errors++; } else { - fprintf(stderr, "warning: %s saved as %s\n", expath, newpath); + logaction("warning: %s saved as %s\n", expath, newpath); } } } else if(md5_orig) { @@ -989,9 +1370,9 @@ int pacman_add(pacdb_t *db, PMList *targets) installnew = 1; snprintf(newpath, PATH_MAX, "%s.pacsave", expath); if(rename(expath, newpath)) { - fprintf(stderr, "error: could not rename %s: %s\n", expath, strerror(errno)); + logaction("error: could not rename %s: %s\n", expath, strerror(errno)); } else { - fprintf(stderr, "warning: %s saved as %s\n", expath, newpath); + logaction("warning: %s saved as %s\n", expath, newpath); } } @@ -1015,11 +1396,11 @@ int pacman_add(pacdb_t *db, PMList *targets) } else { vprint("%s is in NoUpgrade - skipping\n", pathname); strncat(expath, ".pacnew", PATH_MAX); - fprintf(stderr, "warning: extracting %s%s as %s\n", pmo_root, pathname, expath); + logaction("warning: extracting %s%s as %s\n", pmo_root, pathname, expath); /*tar_skip_regfile(tar);*/ } if(tar_extract_file(tar, expath)) { - fprintf(stderr, "could not extract %s: %s\n", pathname, strerror(errno)); + logaction("could not extract %s: %s\n", pathname, strerror(errno)); errors++; } /* calculate an md5 hash if this is in info->backup */ @@ -1043,7 +1424,7 @@ int pacman_add(pacdb_t *db, PMList *targets) tar_close(tar); if(errors) { ret = 1; - fprintf(stderr, "errors occurred while %s %s\n", + logaction("errors occurred while %s %s\n", (pmo_upgrade ? "upgrading" : "installing"), info->name); /* XXX: this "else" is disabled so the db_write() ALWAYS occurs. If it doesn't @@ -1059,7 +1440,7 @@ int pacman_add(pacdb_t *db, PMList *targets) PMList *tmppm = NULL; tmpp = db_scan(db, ((pkginfo_t*)lp->data)->name, INFRQ_DEPENDS); - if (tmpp == NULL) { + if(tmpp == NULL) { continue; } for(tmppm = tmpp->depends; tmppm; tmppm = tmppm->next) { @@ -1078,10 +1459,18 @@ int pacman_add(pacdb_t *db, PMList *targets) /* make an install date (in UTC) */ strncpy(info->installdate, asctime(gmtime(&t)), sizeof(info->installdate)); if(db_write(db, info)) { - fprintf(stderr, "error updating database for %s!\n", info->name); + logaction("error updating database for %s!\n", info->name); return(1); } vprint("done.\n"); + if(pmo_usesyslog) { + if(pmo_upgrade) { + syslog(LOG_INFO, "upgraded %s (%s -> %s)\n", info->name, + oldpkg->version, info->version); + } else { + syslog(LOG_INFO, "installed %s (%s)\n", info->name, info->version); + } + } /* update dependency packages' REQUIREDBY fields */ for(lp = info->depends; lp; lp = lp->next) { @@ -1093,7 +1482,18 @@ int pacman_add(pacdb_t *db, PMList *targets) } depinfo = db_scan(db, depend.name, INFRQ_ALL); if(depinfo == NULL) { - continue; + /* look for a provides package */ + PMList *provides = whatprovides(db, depend.name); + if(provides) { + /* use the first one */ + depinfo = db_scan(db, provides->data, INFRQ_ALL); + if(depinfo == NULL) { + /* wtf */ + continue; + } + } else { + continue; + } } depinfo->requiredby = list_add(depinfo->requiredby, strdup(info->name)); db_write(db, depinfo); @@ -1102,10 +1502,10 @@ int pacman_add(pacdb_t *db, PMList *targets) printf("done.\n"); fflush(stdout); /* run the post-install script if it exists */ - snprintf(pm_install, PATH_MAX, "%s%s/%s/%s-%s/install", pmo_root, PKGDIR, db->treename, info->name, info->version); + snprintf(pm_install, PATH_MAX, "%s%s/%s/%s-%s/install", pmo_root, pmo_dbpath, db->treename, info->name, info->version); if(!stat(pm_install, &buf)) { char cmdline[PATH_MAX+1]; - snprintf(pm_install, PATH_MAX, "%s/%s/%s-%s/install", PKGDIR, db->treename, info->name, info->version); + snprintf(pm_install, PATH_MAX, "%s/%s/%s-%s/install", pmo_dbpath, db->treename, info->name, info->version); vprint("Executing post-install script...\n"); snprintf(cmdline, PATH_MAX, "chroot %s /bin/sh %s post_%s %s %s", pmo_root, pm_install, (pmo_upgrade ? "upgrade" : "install"), info->version, (pmo_upgrade ? oldpkg->version : "")); @@ -1159,43 +1559,46 @@ int pacman_remove(pacdb_t *db, PMList *targets) for(lp = targets; lp; lp = lp->next) { info = db_scan(db, (char*)lp->data, INFRQ_ALL); if(info == NULL) { + PMList *groups; + /* if the target is a group, ask if its packages should be removed */ + groups = find_groups(db); + if(is_in((char *)lp->data, groups)) { + PMList *pkg; + pkg = pkg_ingroup(db, (char *)lp->data); + for(j = pkg; j; j = j->next) { + if(yesno(":: Remove %s from group %s? [Y/n] ", (char *)j->data, (char *)lp->data)) { + info = db_scan(db, (char *)j->data, INFRQ_ALL); + alltargs = list_add(alltargs, info); + } + } + list_free(pkg); + list_free(groups); + continue; + } + list_free(groups); fprintf(stderr, "error: could not find %s in database\n", (char*)lp->data); return(1); } alltargs = list_add(alltargs, info); } if(!pmo_nodeps && !pmo_upgrade) { - vprint("Checking dependencies...\n"); + vprint("checking dependencies...\n"); lp = checkdeps(db, PM_REMOVE, alltargs); if(lp) { if(pmo_r_cascade) { - int cols; while(lp) { for(j = lp; j; j = j->next) { depmissing_t* miss = (depmissing_t*)j->data; - miss = (depmissing_t*)j->data; info = db_scan(db, miss->depend.name, INFRQ_ALL); - list_add(alltargs, info); + if(!is_pkgin(info, alltargs)) { + list_add(alltargs, info); + } } list_free(lp); lp = checkdeps(db, PM_REMOVE, alltargs); } /* list targets */ - fprintf(stderr, "\nTargets: "); - cols = 9; - for(j = alltargs; j; j = j->next) { - char t[PATH_MAX]; - int len; - snprintf(t, PATH_MAX, "%s ", (char*)j->data); - len = strlen(t); - if(len+cols > 78) { - cols = 9; - fprintf(stderr, "\n%9s", " "); - } - fprintf(stderr, "%s", t); - cols += len; - } - printf("\n"); + list_display("\nTargets: ", alltargs); /* get confirmation */ if(yesno("\nDo you want to remove these packages? [Y/n] ") == 0) { list_free(alltargs); @@ -1223,10 +1626,10 @@ int pacman_remove(pacdb_t *db, PMList *targets) printf("removing %s... ", info->name); fflush(stdout); /* run the pre-remove script if it exists */ - snprintf(pm_install, PATH_MAX, "%s%s/%s/%s-%s/install", pmo_root, PKGDIR, db->treename, info->name, info->version); + snprintf(pm_install, PATH_MAX, "%s%s/%s/%s-%s/install", pmo_root, pmo_dbpath, db->treename, info->name, info->version); if(!stat(pm_install, &buf)) { vprint("Executing pre-remove script...\n"); - snprintf(pm_install, PATH_MAX, "%s/%s/%s-%s/install", PKGDIR, db->treename, info->name, info->version); + snprintf(pm_install, PATH_MAX, "%s/%s/%s-%s/install", pmo_dbpath, db->treename, info->name, info->version); snprintf(line, PATH_MAX, "chroot %s /bin/sh %s pre_remove %s", pmo_root, pm_install, info->version); system(line); @@ -1265,7 +1668,7 @@ int pacman_remove(pacdb_t *db, PMList *targets) newpath = (char*)realloc(newpath, strlen(line)+strlen(".pacsave")+1); sprintf(newpath, "%s.pacsave", line); rename(line, newpath); - fprintf(stderr, "warning: %s saved as %s\n", line, newpath); + logaction("warning: %s saved as %s\n", line, newpath); } else { /*vprint(" unlinking %s\n", line);*/ if(unlink(line)) { @@ -1283,7 +1686,7 @@ int pacman_remove(pacdb_t *db, PMList *targets) } /* remove the package from the database */ - snprintf(line, PATH_MAX, "%s%s/%s/%s-%s", pmo_root, PKGDIR, db->treename, + snprintf(line, PATH_MAX, "%s%s/%s/%s-%s", pmo_root, pmo_dbpath, db->treename, info->name, info->version); /* DESC */ @@ -1310,7 +1713,20 @@ int pacman_remove(pacdb_t *db, PMList *targets) } depinfo = db_scan(db, depend.name, INFRQ_ALL); if(depinfo == NULL) { - continue; + /* look for a provides package */ + PMList *provides = whatprovides(db, depend.name); + if(provides) { + /* use the first one */ + depinfo = db_scan(db, provides->data, INFRQ_ALL); + list_free(provides); + if(depinfo == NULL) { + /* wtf */ + continue; + } + } else { + list_free(provides); + continue; + } } /* splice out this entry from requiredby */ last = list_last(depinfo->requiredby); @@ -1332,6 +1748,9 @@ int pacman_remove(pacdb_t *db, PMList *targets) } if(!pmo_upgrade) { printf("done.\n"); + if(pmo_usesyslog) { + syslog(LOG_INFO, "removed %s (%s)\n", info->name, info->version); + } } } @@ -1369,6 +1788,34 @@ int pacman_query(pacdb_t *db, PMList *targets) } package = (char*)targ->data; } + + /* looking for groups */ + if(pmo_group) { + PMList *pkg, *groups; + groups = find_groups(db); + if(package == NULL) { + for(lp = groups; lp; lp = lp->next) { + pkg = pkg_ingroup(db, (char *)lp->data); + for(q = pkg; q; q = q->next) { + printf("%s %s\n", (char *)lp->data, (char *)q->data); + } + list_free(pkg); + } + } else { + if(!is_in(package, groups)) { + fprintf(stderr, "Group \"%s\" was not found.\n", package); + return(2); + } + pkg = pkg_ingroup(db, package); + for(q = pkg; q; q = q->next) { + printf("%s %s\n", package, (char *)q->data); + } + list_free(pkg); + } + list_free(groups); + continue; + } + /* output info for a .tar.gz package */ if(pmo_q_isfile) { if(package == NULL) { @@ -1479,11 +1926,86 @@ int pacman_query(pacdb_t *db, PMList *targets) int pacman_upgrade(pacdb_t *db, PMList *targets) { /* this is basically just a remove-then-add process. pacman_add() will */ - /* handle it */ - pmo_upgrade = 1; + /* handle it */ + pmo_upgrade = 1; return(pacman_add(db, targets)); } +/* Re-order a list of target packages with respect to their dependencies. + * + * Example: + * A depends on C + * B depends on A + * Target order is A,B,C,D + * + * Should be re-ordered to C,A,B,D + * + * This function returns the new PMList* target list. + * + */ +PMList* sortbydeps(PMList *targets) +{ + PMList *newtargs = NULL; + PMList *i, *j, *k; + int change = 1; + int numscans = 0; + int numtargs = 0; + int clean = 0; + + /* count the number of targets */ + for(i = targets; i; i = i->next, numtargs++); + + while(change) { + change = 0; + if(numscans > numtargs) { + vprint("warning: possible dependency cycle detected\n"); + change = 0; + continue; + } + newtargs = NULL; + numscans++; + /* run thru targets, moving up packages as necessary */ + for(i = targets; i; i = i->next) { + pkginfo_t *p = (pkginfo_t*)i->data; + for(j = p->depends; j; j = j->next) { + depend_t dep; + int found = 0; + pkginfo_t *q = NULL; + + splitdep(j->data, &dep); + /* look for dep.name -- if it's farther down in the list, then + * move it up above p + */ + for(k = i->next; k && !found; k = k->next) { + q = (pkginfo_t*)k->data; + if(!strcmp(dep.name, q->name)) { + found = 1; + } + } + if(found) { + if(!is_pkgin(q, newtargs)) { + change = 1; + newtargs = list_add(newtargs, q); + } + } + } + if(!is_pkgin(p, newtargs)) { + newtargs = list_add(newtargs, p); + } + } + if(clean && change) { + /* free up targets -- it's local now */ + for(i = targets; i; i = i->next) { + i->data = NULL; + } + list_free(targets); + } + targets = newtargs; + clean = 1; + } + return(targets); +} + /* populates *list with packages that need to be installed to satisfy all * dependencies (recursive) for *syncpkg->pkg * @@ -1504,17 +2026,23 @@ int resolvedeps(pacdb_t *local, PMList *databases, syncpkg_t *syncpkg, PMList *l int found = 0; depmissing_t *miss = (depmissing_t*)i->data; - if(miss->type == CONFLICT) { + /* XXX: conflicts are now treated specially in the _add and _sync functions */ + + /*if(miss->type == CONFLICT) { fprintf(stderr, "error: cannot resolve dependencies for \"%s\":\n", miss->target); fprintf(stderr, " %s conflicts with %s\n", miss->target, miss->depend.name); return(1); - } else if(miss->type == DEPEND) { + } else*/ + if(miss->type == DEPEND) { syncpkg_t *sync = NULL; MALLOC(sync, sizeof(syncpkg_t)); + sync->replaces = NULL; /* find the package in one of the repositories */ for(j = databases; !found && j; j = j->next) { + PMList *provides; dbsync_t *dbs = (dbsync_t*)j->data; + /* check literals */ for(k = dbs->pkgcache; !found && k; k = k->next) { pkginfo_t *pkg = (pkginfo_t*)k->data; if(!strcmp(miss->depend.name, pkg->name)) { @@ -1524,6 +2052,17 @@ int resolvedeps(pacdb_t *local, PMList *databases, syncpkg_t *syncpkg, PMList *l sync->dbs = dbs; } } + /* check provides */ + if(!found) { + provides = whatprovides(dbs->db, miss->depend.name); + if(provides) { + found = 1; + /* re-fetch the package record with dependency info */ + sync->pkg = db_scan(dbs->db, provides->data, INFRQ_DESC | INFRQ_DEPENDS); + sync->dbs = dbs; + } + list_free(provides); + } } if(!found) { fprintf(stderr, "error: cannot resolve dependencies for \"%s\":\n", miss->target); @@ -1541,7 +2080,7 @@ int resolvedeps(pacdb_t *local, PMList *databases, syncpkg_t *syncpkg, PMList *l /* this dep is already in the target list */ continue; } - /*printf("resolving %s\n", sync->pkg->name); fflush(stdout);*/ + vprint("resolving %s\n", sync->pkg->name); found = 0; for(j = trail; j; j = j->next) { syncpkg_t *tmp = (syncpkg_t*)j->data; @@ -1554,11 +2093,11 @@ int resolvedeps(pacdb_t *local, PMList *databases, syncpkg_t *syncpkg, PMList *l if(resolvedeps(local, databases, sync, list, trail)) { return(1); } - vprint("adding %s-%s\n", sync->pkg->name, sync->pkg->version); + vprint("adding %s-%s\n", sync->pkg->name, sync->pkg->version); list_add(list, sync); } else { /* cycle detected -- skip it */ - /*printf("cycle detected\n"); fflush(stdout);*/ + vprint("dependency cycle detected: %s\n", sync->pkg->name); } } } @@ -1618,8 +2157,13 @@ PMList* checkdeps(pacdb_t *db, unsigned short op, PMList *targets) } } if(found == 0) { - /* not found */ - continue; + /* look for packages that list depend.name as a "provide" */ + PMList *provides = whatprovides(db, depend.name); + if(provides == NULL) { + /* not found */ + continue; + } + /* we found an installed package that provides depend.name */ } found = 0; if(depend.mod == DEP_ANY) { @@ -1647,7 +2191,9 @@ PMList* checkdeps(pacdb_t *db, unsigned short op, PMList *targets) strncpy(miss->target, p->name, 256); strncpy(miss->depend.name, depend.name, 256); strncpy(miss->depend.version, depend.version, 64); - baddeps = list_add(baddeps, miss); + if(!list_isin(baddeps, miss)) { + baddeps = list_add(baddeps, miss); + } } } freepkg(oldpkg); @@ -1673,7 +2219,9 @@ PMList* checkdeps(pacdb_t *db, unsigned short op, PMList *targets) miss->depend.version[0] = '\0'; strncpy(miss->target, tp->name, 256); strncpy(miss->depend.name, dp->name, 256); - baddeps = list_add(baddeps, miss); + if(!list_isin(baddeps, miss)) { + baddeps = list_add(baddeps, miss); + } } } /* check targets against targets */ @@ -1686,7 +2234,9 @@ PMList* checkdeps(pacdb_t *db, unsigned short op, PMList *targets) miss->depend.version[0] = '\0'; strncpy(miss->target, tp->name, 256); strncpy(miss->depend.name, a->name, 256); - baddeps = list_add(baddeps, miss); + if(!list_isin(baddeps, miss)) { + baddeps = list_add(baddeps, miss); + } } } } @@ -1701,21 +2251,45 @@ PMList* checkdeps(pacdb_t *db, unsigned short op, PMList *targets) miss->depend.version[0] = '\0'; strncpy(miss->target, tp->name, 256); strncpy(miss->depend.name, info->name, 256); + if(!list_isin(baddeps, miss)) { + baddeps = list_add(baddeps, miss); + } + } + } + } + + /* PROVIDES -- check to see if another package already provides what + * we offer + */ + for(j = tp->provides; j; j = j->next) { + PMList *provs = whatprovides(db, j->data); + for(k = provs; k; k = k->next) { + if(!strcmp(tp->name, k->data)) { + /* this is the same package -- skip it */ + continue; + } + /* we treat this just like a conflict */ + MALLOC(miss, sizeof(depmissing_t)); + miss->type = CONFLICT; + miss->depend.mod = DEP_ANY; + miss->depend.version[0] = '\0'; + strncpy(miss->target, tp->name, 256); + strncpy(miss->depend.name, k->data, 256); + if(!list_isin(baddeps, miss)) { baddeps = list_add(baddeps, miss); } } } - /* DEPENDENCIES */ - /* cycle thru this targets dependency list */ + /* DEPENDENCIES -- look for unsatisfied dependencies */ for(j = tp->depends; j; j = j->next) { /* split into name/version pairs */ if(splitdep((char*)j->data, &depend)) { - fprintf(stderr, "warning: invalid dependency in %s\n", (char*)tp->name); + logaction("warning: invalid dependency in %s\n", (char*)tp->name); continue; } found = 0; - /* check database */ + /* check database for literal packages */ for(k = pm_packages; k && !found; k = k->next) { pkginfo_t *p = (pkginfo_t*)k->data; if(!strcmp(p->name, depend.name)) { @@ -1745,7 +2319,8 @@ PMList* checkdeps(pacdb_t *db, unsigned short op, PMList *targets) /* check other targets */ for(k = targets; k && !found; k = k->next) { pkginfo_t *p = (pkginfo_t*)k->data; - if(!strcmp(p->name, depend.name)) { + /* see if the package names match OR if p provides depend.name */ + if(!strcmp(p->name, depend.name) || is_in(depend.name, p->provides)) { if(depend.mod == DEP_ANY) { /* accept any version */ found = 1; @@ -1768,10 +2343,45 @@ PMList* checkdeps(pacdb_t *db, unsigned short op, PMList *targets) FREE(ver); } } - /* TODO: switch positions in targets if one package should precede - * the other (wrt deps) - */ } + /* check database for provides matches */ + if(!found){ + k = whatprovides(db, depend.name); + if(k) { + /* grab the first one (there should only really be one, anyway) */ + pkginfo_t *p = db_scan(db, k->data, INFRQ_DESC); + if(p == NULL) { + /* wtf */ + fprintf(stderr, "data error: %s supposedly provides %s, but it was not found in db\n", + (char*)k->data, depend.name); + list_free(k); + continue; + } + if(depend.mod == DEP_ANY) { + /* accept any version */ + found = 1; + } else { + char *ver = strdup(p->version); + /* check for a release in depend.version. if it's + * missing remove it from p->version as well. + */ + if(!index(depend.version,'-')) { + char *ptr; + for(ptr = ver; *ptr != '-'; ptr++); + *ptr = '\0'; + } + cmp = rpmvercmp(ver, depend.version); + switch(depend.mod) { + case DEP_EQ: found = (cmp == 0); break; + case DEP_GE: found = (cmp >= 0); break; + case DEP_LE: found = (cmp <= 0); break; + } + FREE(ver); + } + } + list_free(k); + } + /* else if still not found... */ if(!found) { MALLOC(miss, sizeof(depmissing_t)); miss->type = DEPEND; @@ -1779,7 +2389,9 @@ PMList* checkdeps(pacdb_t *db, unsigned short op, PMList *targets) strncpy(miss->target, tp->name, 256); strncpy(miss->depend.name, depend.name, 256); strncpy(miss->depend.version, depend.version, 64); - baddeps = list_add(baddeps, miss); + if(!list_isin(baddeps, miss)) { + baddeps = list_add(baddeps, miss); + } } } } @@ -1799,7 +2411,9 @@ PMList* checkdeps(pacdb_t *db, unsigned short op, PMList *targets) miss->depend.version[0] = '\0'; strncpy(miss->target, tp->name, 256); strncpy(miss->depend.name, (char*)j->data, 256); - baddeps = list_add(baddeps, miss); + if(!list_isin(baddeps, miss)) { + baddeps = list_add(baddeps, miss); + } } } } @@ -1896,6 +2510,7 @@ int parseargs(int op, int argc, char **argv) {"vertest", no_argument, 0, 'Y'}, {"resolve", no_argument, 0, 'D'}, {"root", required_argument, 0, 'r'}, + {"dbpath", required_argument, 0, 'b'}, {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, @@ -1912,10 +2527,11 @@ int parseargs(int op, int argc, char **argv) {"downloadonly", no_argument, 0, 'w'}, {"refresh", no_argument, 0, 'y'}, {"cascade", no_argument, 0, 'c'}, + {"groups", no_argument, 0, 'g'}, {0, 0, 0, 0} }; - while((opt = getopt_long(argc, argv, "ARUFQSTDYr:vhscVfnoldpiuwy", opts, &option_index))) { + while((opt = getopt_long(argc, argv, "ARUFQSTDYr:b:vhscVfnoldpiuwyg", opts, &option_index))) { if(opt < 0) { break; } @@ -1945,10 +2561,12 @@ int parseargs(int op, int argc, char **argv) case 'y': pmo_s_sync = 1; break; case 's': pmo_s_search = 1; break; case 'c': pmo_s_clean = 1; pmo_r_cascade = 1; break; + case 'g': pmo_group = 1; break; case 'r': if(realpath(optarg, pmo_root) == NULL) { perror("bad root path"); return(1); } break; + case 'b': strcpy(pmo_dbpath, optarg); break; case '?': return(1); default: return(1); } @@ -2040,6 +2658,9 @@ int parseconfig(char *configfile) if(!strcmp(key, "NOPASSIVEFTP")) { pmo_nopassiveftp = 1; vprint("config: nopassiveftp\n"); + } else if(!strcmp(key, "USESYSLOG")) { + pmo_usesyslog = 1; + vprint("config: usesyslog\n"); } else { fprintf(stderr, "config: line %d: syntax error\n", linenum); return(1); @@ -2090,7 +2711,7 @@ int parseconfig(char *configfile) MALLOC(server, sizeof(server_t)); server->server = server->path = NULL; - server->islocal = 0; + server->protocol = NULL; p = strstr(ptr, "://"); if(p == NULL) { @@ -2103,8 +2724,8 @@ int parseconfig(char *configfile) fprintf(stderr, "config: line %d: bad server location\n", linenum); return(1); } - server->islocal = !strcmp(ptr, "local"); - if(!server->islocal) { + server->protocol = strdup(ptr); + if(strcmp(server->protocol, "file")) { char *slash; /* no http support yet */ if(strcmp(ptr, "ftp")) { @@ -2160,18 +2781,18 @@ int parseconfig(char *configfile) void usage(int op, char *myname) { if(op == PM_MAIN) { - printf("usage: %s {-h --help}\n", myname); - printf(" %s {-V --version}\n", myname); - printf(" %s {-A --add} [options] \n", myname); - printf(" %s {-R --remove} [options] \n", myname); - printf(" %s {-U --upgrade} [options] \n", myname); - printf(" %s {-F --freshen} [options] \n", myname); - printf(" %s {-Q --query} [options] [package]\n", myname); - printf(" %s {-S --sync} [options] [package]\n", myname); + printf("usage: %s {-h --help}\n", myname); + printf(" %s {-V --version}\n", myname); + printf(" %s {-A --add} [options] \n", myname); + printf(" %s {-R --remove} [options] \n", myname); + printf(" %s {-U --upgrade} [options] \n", myname); + printf(" %s {-F --freshen} [options] \n", myname); + printf(" %s {-Q --query} [options] [package]\n", myname); + printf(" %s {-S --sync} [options] [package]\n", myname); printf("\nuse '%s --help' with other options for more syntax\n\n", myname); } else { if(op == PM_ADD) { - printf("usage: %s {-A --add} [options] \n", myname); + printf("usage: %s {-A --add} [options] \n", myname); printf("options:\n"); printf(" -d, --nodeps skip dependency checks\n"); printf(" -f, --force force install, overwrite conflicting files\n"); @@ -2194,6 +2815,7 @@ void usage(int op, char *myname) printf("usage: %s {-Q --query} [options] [package]\n", myname); printf("options:\n"); printf(" -i, --info view package information\n"); + printf(" -g, --groups view all members of a package group\n"); printf(" -l, --list list the contents of the queried package\n"); printf(" -o, --owns query the package that owns \n"); printf(" -p, --file pacman will query the package file [package] instead of\n"); @@ -2204,6 +2826,7 @@ void usage(int op, char *myname) printf(" -c, --clean remove packages from cache directory to free up diskspace\n"); printf(" -d, --nodeps skip dependency checks\n"); printf(" -f, --force force install, overwrite conflicting files\n"); + printf(" -g, --groups view all members of a package group\n"); printf(" -s, --search search sync database for matching strings\n"); printf(" -u, --sysupgrade upgrade all packages that are out of date\n"); printf(" -w, --downloadonly download packages, but do not install/upgrade anything\n"); @@ -2211,6 +2834,7 @@ void usage(int op, char *myname) } printf(" -v, --verbose be verbose\n"); printf(" -r, --root set an alternate installation root\n"); + printf(" -b, --dbpath set an alternate database location\n"); } } @@ -2218,18 +2842,18 @@ void usage(int op, char *myname) */ void version(void) { - printf("\n"); - printf(" .--. Pacman v%s\n", PACVER); - printf("/ _.-' .-. .-. .-. Copyright (C) 2002 Judd Vinet \n"); - printf("\\ '-. '-' '-' '-' \n"); - printf(" '--' This program may be freely redistributed under\n"); - printf(" the terms of the GNU GPL\n\n"); + printf("\n"); + printf(" .--. Pacman v%s\n", PACVER); + printf("/ _.-' .-. .-. .-. Copyright (C) 2002-2003 Judd Vinet \n"); + printf("\\ '-. '-' '-' '-' \n"); + printf(" '--' This program may be freely redistributed under\n"); + printf(" the terms of the GNU General Public License\n\n"); } /* Check verbosity option and, if set, print the * string to stdout */ -int vprint(char *fmt, ...) +void vprint(char *fmt, ...) { va_list args; if(pmo_verbose) { @@ -2238,9 +2862,47 @@ int vprint(char *fmt, ...) va_end(args); fflush(stdout); } - return(0); } +/* Output a message to stderr, and (optionally) syslog */ +void logaction(char *fmt, ...) +{ + char msg[1024]; + va_list args; + va_start(args, fmt); + vsnprintf(msg, 1024, fmt, args); + va_end(args); + + fprintf(stderr, "%s", msg); + if(pmo_usesyslog) { + syslog(LOG_WARNING, "%s", msg); + } +} + +/* Condense a list of strings into one long (space-delimited) string + */ +char* buildstring(PMList *strlist) +{ + char* str; + int size = 1; + PMList *lp; + + for(lp = strlist; lp; lp = lp->next) { + size += strlen(lp->data) + 1; + } + MALLOC(str, size); + str[0] = '\0'; + for(lp = strlist; lp; lp = lp->next) { + strcat(str, lp->data); + strcat(str, " "); + } + /* shave off the last space */ + str[strlen(str)-1] = '\0'; + + return(str); +} + + int lckmk(char *file, int retries, unsigned int sleep_secs) { int fd, count = 0; @@ -2263,13 +2925,16 @@ int lckrm(char *file) void cleanup(int signum) { if(pm_access == READ_WRITE && lckrm(lckfile)) { - fprintf(stderr, "warning: could not remove lock file %s\n", lckfile); + logaction("warning: could not remove lock file %s\n", lckfile); } if(workfile) { /* remove the current file being downloaded (as it's not complete) */ unlink(workfile); FREE(workfile); } + if(pmo_usesyslog) { + closelog(); + } exit(signum); } diff --git a/src/pacman.h b/src/pacman.h index 3d749886..2dd0a9db 100644 --- a/src/pacman.h +++ b/src/pacman.h @@ -1,5 +1,5 @@ /* - * pacman + * pacman.h * * Copyright (c) 2002 by Judd Vinet * @@ -22,7 +22,7 @@ #define _PAC_PACMAN_H #ifndef PACVER -#define PACVER "2.5.1" +#define PACVER "2.6" #endif #ifndef PKGDIR @@ -51,6 +51,7 @@ int pacman_query(pacdb_t *db, PMList *targets); int pacman_sync(pacdb_t *db, PMList *targets); int pacman_deptest(pacdb_t *db, PMList *targets); +PMList* sortbydeps(PMList *targets); PMList* checkdeps(pacdb_t *db, unsigned short op, PMList *targets); int resolvedeps(pacdb_t *local, PMList *databases, syncpkg_t *sync, PMList *list, PMList *trail); int splitdep(char *depstr, depend_t *depend); @@ -62,7 +63,9 @@ int parseconfig(char *configfile); void usage(int op, char *myname); void version(void); -int vprint(char *fmt, ...); +void vprint(char *fmt, ...); +void logaction(char *fmt, ...); +char* buildstring(PMList *strlist); int lckmk(char *file, int retries, unsigned int sleep_secs); int lckrm(char *lckfile); void cleanup(int signum); diff --git a/src/pacsync.c b/src/pacsync.c index 91e62117..3d87013a 100644 --- a/src/pacsync.c +++ b/src/pacsync.c @@ -1,5 +1,5 @@ /* - * pacman + * pacsync.c * * Copyright (c) 2002 by Judd Vinet * @@ -21,6 +21,7 @@ #include "config.h" #include +#include #include #include #include @@ -39,10 +40,12 @@ static int offset; /* pacman options */ extern char *pmo_root; +extern char *pmo_dbpath; extern unsigned short pmo_nopassiveftp; /* sync servers */ extern PMList *pmc_syncs; +extern int maxcols; int sync_synctree() { @@ -52,10 +55,10 @@ int sync_synctree() PMList *files = NULL; PMList *i; int success = 0; - + for(i = pmc_syncs; i; i = i->next) { sync_t *sync = (sync_t*)i->data; - snprintf(ldir, PATH_MAX, "%s%s", pmo_root, PKGDIR); + snprintf(ldir, PATH_MAX, "%s%s", pmo_root, pmo_dbpath); /* build a one-element list */ snprintf(path, PATH_MAX, "%s.db.tar.gz", sync->treename); @@ -66,13 +69,12 @@ int sync_synctree() fprintf(stderr, "failed to synchronize %s\n", sync->treename); success = 0; } - /*printf("\n");*/ list_free(files); files = NULL; snprintf(path, PATH_MAX, "%s/%s.db.tar.gz", ldir, sync->treename); if(success) { - snprintf(ldir, PATH_MAX, "%s%s/%s", pmo_root, PKGDIR, sync->treename); + snprintf(ldir, PATH_MAX, "%s%s/%s", pmo_root, pmo_dbpath, sync->treename); /* remove the old dir */ vprint("removing %s (if it exists)\n", ldir); rmrf(ldir); @@ -111,7 +113,7 @@ int downloadfiles(PMList *servers, char *localpath, PMList *files) for(i = servers; i && !done; i = i->next) { server_t *server = (server_t*)i->data; - if(!server->islocal) { + if(!strcmp(server->protocol, "ftp")) { FtpInit(); if(!FtpConnect(server->server, &control)) { fprintf(stderr, "error: cannot connect to %s\n", server->server); @@ -153,50 +155,42 @@ int downloadfiles(PMList *servers, char *localpath, PMList *files) sync_fnm[j] = ' '; } sync_fnm[24] = '\0'; + offset = 0; - if(!server->islocal) { - int tries = 2; - while(tries) { - if(!FtpSize(fn, &fsz, FTPLIB_IMAGE, control)) { - fprintf(stderr, "warning: failed to get filesize for %s\n", fn); - } - offset = 0; - if(!stat(output, &st)) { - offset = (int)st.st_size; - } - if(offset) { - if(!FtpRestart(offset, control)) { - fprintf(stderr, "warning: failed to resume download -- restarting\n"); - /* can't resume: */ - /* unlink the file in order to restart download from scratch */ - unlink(output); - } + if(!strcmp(server->protocol, "ftp")) { + if(!FtpSize(fn, &fsz, FTPLIB_IMAGE, control)) { + fprintf(stderr, "warning: failed to get filesize for %s\n", fn); + } + if(!stat(output, &st)) { + offset = (int)st.st_size; + if(!FtpRestart(offset, control)) { + fprintf(stderr, "warning: failed to resume download -- restarting\n"); + /* can't resume: */ + /* unlink the file in order to restart download from scratch */ + unlink(output); } - /* set up our progress bar's callback */ - FtpOptions(FTPLIB_CALLBACK, (long)log_progress, control); - FtpOptions(FTPLIB_IDLETIME, (long)1000, control); - FtpOptions(FTPLIB_CALLBACKARG, (long)&fsz, control); - FtpOptions(FTPLIB_CALLBACKBYTES, (10*1024), control); + } + /* set up our progress bar's callback */ + FtpOptions(FTPLIB_CALLBACK, (long)log_progress, control); + FtpOptions(FTPLIB_IDLETIME, (long)1000, control); + FtpOptions(FTPLIB_CALLBACKARG, (long)&fsz, control); + FtpOptions(FTPLIB_CALLBACKBYTES, (10*1024), control); - if(!FtpGet(output, lp->data, FTPLIB_IMAGE, control)) { - fprintf(stderr, "\nfailed downloading %s from %s: %s\n", - fn, server->server, FtpLastResponse(control)); - /* we leave the partially downloaded file in place so it can be resumed later */ - /* try each file twice in case it was just one of those transient network errors */ - tries--; - } else { - char completefile[PATH_MAX]; - log_progress(control, fsz-offset, &fsz); - complete = list_add(complete, fn); - tries = 0; - /* rename "output.part" file to "output" file */ - snprintf(completefile, PATH_MAX, "%s/%s", localpath, fn); - rename(output, completefile); - } - printf("\n"); - fflush(stdout); + if(!FtpGet(output, lp->data, FTPLIB_IMAGE, control)) { + fprintf(stderr, "\nfailed downloading %s from %s: %s\n", + fn, server->server, FtpLastResponse(control)); + /* we leave the partially downloaded file in place so it can be resumed later */ + } else { + char completefile[PATH_MAX]; + log_progress(control, fsz-offset, &fsz); + complete = list_add(complete, fn); + /* rename "output.part" file to "output" file */ + snprintf(completefile, PATH_MAX, "%s/%s", localpath, fn); + rename(output, completefile); } - } else { + printf("\n"); + fflush(stdout); + } else if(!strcmp(server->protocol, "file")) { /* local repository, just copy the file */ char src[PATH_MAX], dest[PATH_MAX]; snprintf(src, PATH_MAX, "%s%s", server->path, fn); @@ -206,10 +200,10 @@ int downloadfiles(PMList *servers, char *localpath, PMList *files) fprintf(stderr, "failed copying %s\n", src); } else { char out[56]; - printf("%s [", sync_fnm); + printf(" %s [", sync_fnm); strncpy(out, server->path, 33); printf("%s", out); - for(j = strlen(out); j < 33; j++) { + for(j = strlen(out); j < maxcols-44; j++) { printf(" "); } fputs("] 100% | LOCAL\n", stdout); @@ -223,7 +217,7 @@ int downloadfiles(PMList *servers, char *localpath, PMList *files) done = 1; } - if(!server->islocal) { + if(!strcmp(server->protocol, "ftp")) { FtpQuit(control); } } @@ -235,37 +229,44 @@ static int log_progress(netbuf *ctl, int xfered, void *arg) { int fsz = *(int*)arg; int pct = ((float)(xfered+offset) / fsz) * 100; - int i; + int i, cur; + char *cenv = NULL; - printf("%s [", sync_fnm); - for(i = 0; i < (int)(pct/3); i++) { - printf("#"); + cenv = getenv("COLUMNS"); + if(cenv) { + maxcols = atoi(cenv); } - for(i = (int)(pct/3); i < (int)(100/3); i++) { - printf(" "); + + printf(" %s [", sync_fnm); + cur = (int)((maxcols-44)*pct/100); + for(i = 0; i < maxcols-44; i++) { + (i < cur) ? printf("#") : printf(" "); } - printf("] %3d%% | %6dK\r ", pct, ((xfered+offset)/1024)); + printf("] %3d%% | %6dK\r", pct, ((xfered+offset)/1024)); fflush(stdout); return(1); } -/* Test for existence of a package in a PMList* - * of syncpkg_t* +/* Test for existance of a package in a PMList* of syncpkg_t* + * If found, return a pointer to the respective syncpkg_t* */ -int is_pkginsync(syncpkg_t *needle, PMList *haystack) +syncpkg_t* find_pkginsync(char *needle, PMList *haystack) { - PMList *lp; + PMList *i; syncpkg_t *sync; int found = 0; - for(lp = haystack; lp && !found; lp = lp->next) { - sync = (syncpkg_t*)lp->data; - if(sync && !strcmp(sync->pkg->name, needle->pkg->name)) { + for(i = haystack; i && !found; i = i->next) { + sync = (syncpkg_t*)i->data; + if(sync && !strcmp(sync->pkg->name, needle)) { found = 1; } } + if(!found) { + sync = NULL; + } - return found; + return sync; } /* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacsync.h b/src/pacsync.h index abb657f8..fe9b5ba3 100644 --- a/src/pacsync.h +++ b/src/pacsync.h @@ -1,5 +1,5 @@ /* - * pacman + * pacsync.h * * Copyright (c) 2002 by Judd Vinet * @@ -23,7 +23,7 @@ /* Servers */ typedef struct __server_t { - unsigned short islocal; + char* protocol; char* server; char* path; } server_t; @@ -44,11 +44,12 @@ typedef struct __dbsync_t { typedef struct __syncpkg_t { pkginfo_t *pkg; dbsync_t *dbs; + PMList *replaces; } syncpkg_t; int sync_synctree(); int downloadfiles(PMList *servers, char *localpath, PMList *files); -int is_pkginsync(syncpkg_t *needle, PMList *haystack); +syncpkg_t* find_pkginsync(char *needle, PMList *haystack); #endif diff --git a/src/rpmvercmp.c b/src/rpmvercmp.c index de4aefe2..7805be0c 100644 --- a/src/rpmvercmp.c +++ b/src/rpmvercmp.c @@ -1,5 +1,5 @@ /* - * pacman + * rpmvercmp.c * * Copyright (c) 2002 by Judd Vinet * diff --git a/src/rpmvercmp.h b/src/rpmvercmp.h index 3b7df650..ab5594d8 100644 --- a/src/rpmvercmp.h +++ b/src/rpmvercmp.h @@ -1,5 +1,5 @@ /* - * pacman + * rpmvercmp.h * * Copyright (c) 2002 by Judd Vinet * diff --git a/src/util.c b/src/util.c index cd7978e9..32852816 100644 --- a/src/util.c +++ b/src/util.c @@ -1,5 +1,5 @@ /* - * pacman + * util.c * * Copyright (c) 2002 by Judd Vinet * @@ -117,6 +117,7 @@ int copyfile(char *src, char *dest) return(0); } +/* does the same thing as 'mkdir -p' */ int makepath(char *path) { char *orig, *str, *ptr; @@ -146,6 +147,7 @@ int makepath(char *path) return(0); } +/* does the same thing as 'rm -rf' */ int rmrf(char *path) { int errflag = 0; @@ -157,13 +159,13 @@ int rmrf(char *path) if(!unlink(path)) { return(0); } else { - if (errno == ENOENT) { + if(errno == ENOENT) { return(0); - } else if (errno == EPERM) { + } else if(errno == EPERM) { /* fallthrough */ - } else if (errno == EISDIR) { + } else if(errno == EISDIR) { /* fallthrough */ - } else if (errno == ENOTDIR) { + } else if(errno == ENOTDIR) { return(1); } else { /* not a directory */ @@ -174,7 +176,7 @@ int rmrf(char *path) return(1); } for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { - if (dp->d_ino) { + if(dp->d_ino) { sprintf(name, "%s/%s", path, dp->d_name); if(strcmp(dp->d_name, "..") && strcmp(dp->d_name, ".")) { errflag += rmrf(name); @@ -190,6 +192,7 @@ int rmrf(char *path) return(0); } +/* presents a prompt and gets a Y/N answer */ int yesno(char *fmt, ...) { char response[32]; diff --git a/src/util.h b/src/util.h index be2c6089..7c1556aa 100644 --- a/src/util.h +++ b/src/util.h @@ -1,5 +1,5 @@ /* - * pacman + * util.h * * Copyright (c) 2002 by Judd Vinet * diff --git a/src/vercmp.c b/src/vercmp.c index 43f8deab..25172086 100644 --- a/src/vercmp.c +++ b/src/vercmp.c @@ -1,5 +1,5 @@ /* - * pacman + * vercmp.c * * Copyright (c) 2002 by Judd Vinet * -- cgit v1.2.3-24-g4f1b