summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorScott James Remnant <scott@netsplit.com>2005-03-11 09:00:14 +0000
committerScott James Remnant <scott@netsplit.com>2005-03-11 09:00:14 +0000
commit841a630143cd3a35dbf8bce0a111ebd39dd12f44 (patch)
tree0f85bbcb279f41e840bfcf6faceaab43770d2447 /src
parentac8c6923a8654da81cbc6730213147961026eb8f (diff)
downloaddpkg-841a630143cd3a35dbf8bce0a111ebd39dd12f44.tar.gz
dpkg (1.13.1.0.1) experimental; urgency=low
* Bin-MU; recompile against Debian unstable, to make dselect actually installable. -- Scott James Remnant <scott@netsplit.com> Fri, 11 Mar 2005 09:00:14 +0000
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am41
-rw-r--r--src/Makefile.in539
-rw-r--r--src/archives.c995
-rw-r--r--src/archives.h71
-rw-r--r--src/cleanup.c227
-rw-r--r--src/configure.c663
-rw-r--r--src/depcon.c464
-rw-r--r--src/enquiry.c459
-rw-r--r--src/errors.c132
-rw-r--r--src/filesdb.c610
-rw-r--r--src/filesdb.h148
-rw-r--r--src/help.c480
-rw-r--r--src/main.c563
-rw-r--r--src/main.h223
-rw-r--r--src/packages.c417
-rw-r--r--src/processarc.c1055
-rw-r--r--src/query.c553
-rw-r--r--src/remove.c584
-rw-r--r--src/select.c146
-rw-r--r--src/update.c128
20 files changed, 8498 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 000000000..bdccc631f
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,41 @@
+## Process this file with automake to produce Makefile.in
+
+localedir = $(datadir)/locale
+INCLUDES = \
+ -DLOCALEDIR=\"$(localedir)\" -I$(top_srcdir)/intl \
+ -DADMINDIR=\"$(admindir)\" \
+ -I$(top_srcdir)/lib
+
+
+bin_PROGRAMS = dpkg dpkg-query
+
+dpkg_SOURCES = \
+ archives.c archives.h \
+ cleanup.c \
+ configure.c \
+ depcon.c \
+ enquiry.c \
+ errors.c \
+ filesdb.c filesdb.h \
+ help.c \
+ main.c main.h \
+ packages.c \
+ processarc.c \
+ remove.c \
+ select.c \
+ update.c
+
+dpkg_LDADD = $(LIBINTL) ../lib/libdpkg.a $(ZLIB_LIBS) $(BZ2_LIBS)
+
+dpkg_query_SOURCES = \
+ errors.c \
+ filesdb.c filesdb.h \
+ query.c
+
+dpkg_query_LDADD = $(LIBINTL) ../lib/libdpkg.a $(ZLIB_LIBS) $(BZ2_LIBS)
+
+
+install-data-local:
+ $(mkdir_p) $(DESTDIR)$(admindir)/alternatives
+ $(mkdir_p) $(DESTDIR)$(admindir)/info
+ $(mkdir_p) $(DESTDIR)$(admindir)/updates
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 000000000..19db2106b
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,539 @@
+# Makefile.in generated by automake 1.8.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+SOURCES = $(dpkg_SOURCES) $(dpkg_query_SOURCES)
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+bin_PROGRAMS = dpkg$(EXEEXT) dpkg-query$(EXEEXT)
+subdir = src
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/arch.m4 \
+ $(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/compiler.m4 \
+ $(top_srcdir)/m4/funcs.m4 $(top_srcdir)/m4/gettext.m4 \
+ $(top_srcdir)/m4/glibc21.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/intdiv0.m4 $(top_srcdir)/m4/intmax.m4 \
+ $(top_srcdir)/m4/inttypes-pri.m4 $(top_srcdir)/m4/inttypes.m4 \
+ $(top_srcdir)/m4/inttypes_h.m4 $(top_srcdir)/m4/lcmessage.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libs.m4 \
+ $(top_srcdir)/m4/linker.m4 $(top_srcdir)/m4/longdouble.m4 \
+ $(top_srcdir)/m4/longlong.m4 $(top_srcdir)/m4/nls.m4 \
+ $(top_srcdir)/m4/perl.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/printf-posix.m4 $(top_srcdir)/m4/progtest.m4 \
+ $(top_srcdir)/m4/signed.m4 $(top_srcdir)/m4/size_max.m4 \
+ $(top_srcdir)/m4/stdint_h.m4 $(top_srcdir)/m4/types.m4 \
+ $(top_srcdir)/m4/uintmax_t.m4 $(top_srcdir)/m4/ulonglong.m4 \
+ $(top_srcdir)/m4/wchar_t.m4 $(top_srcdir)/m4/wint_t.m4 \
+ $(top_srcdir)/m4/xsize.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)"
+binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+PROGRAMS = $(bin_PROGRAMS)
+am_dpkg_OBJECTS = archives.$(OBJEXT) cleanup.$(OBJEXT) \
+ configure.$(OBJEXT) depcon.$(OBJEXT) enquiry.$(OBJEXT) \
+ errors.$(OBJEXT) filesdb.$(OBJEXT) help.$(OBJEXT) \
+ main.$(OBJEXT) packages.$(OBJEXT) processarc.$(OBJEXT) \
+ remove.$(OBJEXT) select.$(OBJEXT) update.$(OBJEXT)
+dpkg_OBJECTS = $(am_dpkg_OBJECTS)
+am__DEPENDENCIES_1 =
+dpkg_DEPENDENCIES = $(am__DEPENDENCIES_1) ../lib/libdpkg.a \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_dpkg_query_OBJECTS = errors.$(OBJEXT) filesdb.$(OBJEXT) \
+ query.$(OBJEXT)
+dpkg_query_OBJECTS = $(am_dpkg_query_OBJECTS)
+dpkg_query_DEPENDENCIES = $(am__DEPENDENCIES_1) ../lib/libdpkg.a \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/archives.Po ./$(DEPDIR)/cleanup.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/configure.Po ./$(DEPDIR)/depcon.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/enquiry.Po ./$(DEPDIR)/errors.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/filesdb.Po ./$(DEPDIR)/help.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/main.Po ./$(DEPDIR)/packages.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/processarc.Po ./$(DEPDIR)/query.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/remove.Po ./$(DEPDIR)/select.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/update.Po
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(dpkg_SOURCES) $(dpkg_query_SOURCES)
+DIST_SOURCES = $(dpkg_SOURCES) $(dpkg_query_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BUILD_INCLUDED_LIBINTL = @BUILD_INCLUDED_LIBINTL@
+BZ2_CFLAGS = @BZ2_CFLAGS@
+BZ2_LIBS = @BZ2_LIBS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CURSES_LIBS = @CURSES_LIBS@
+CXX = @CXX@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GENCAT = @GENCAT@
+GLIBC21 = @GLIBC21@
+GMSGFMT = @GMSGFMT@
+HAVE_ASPRINTF = @HAVE_ASPRINTF@
+HAVE_POSIX_PRINTF = @HAVE_POSIX_PRINTF@
+HAVE_SNPRINTF = @HAVE_SNPRINTF@
+HAVE_WPRINTF = @HAVE_WPRINTF@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLBISON = @INTLBISON@
+INTLLIBS = @INTLLIBS@
+INTLOBJS = @INTLOBJS@
+INTL_LIBTOOL_SUFFIX_PREFIX = @INTL_LIBTOOL_SUFFIX_PREFIX@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGMERGE = @MSGMERGE@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+POSUB = @POSUB@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SSD_LIBS = @SSD_LIBS@
+STRIP = @STRIP@
+USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WITH_DSELECT_FALSE = @WITH_DSELECT_FALSE@
+WITH_DSELECT_TRUE = @WITH_DSELECT_TRUE@
+WITH_START_STOP_DAEMON_FALSE = @WITH_START_STOP_DAEMON_FALSE@
+WITH_START_STOP_DAEMON_TRUE = @WITH_START_STOP_DAEMON_TRUE@
+XGETTEXT = @XGETTEXT@
+ZLIB_CFLAGS = @ZLIB_CFLAGS@
+ZLIB_LIBS = @ZLIB_LIBS@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+admindir = @admindir@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+localedir = $(datadir)/locale
+INCLUDES = \
+ -DLOCALEDIR=\"$(localedir)\" -I$(top_srcdir)/intl \
+ -DADMINDIR=\"$(admindir)\" \
+ -I$(top_srcdir)/lib
+
+dpkg_SOURCES = \
+ archives.c archives.h \
+ cleanup.c \
+ configure.c \
+ depcon.c \
+ enquiry.c \
+ errors.c \
+ filesdb.c filesdb.h \
+ help.c \
+ main.c main.h \
+ packages.c \
+ processarc.c \
+ remove.c \
+ select.c \
+ update.c
+
+dpkg_LDADD = $(LIBINTL) ../lib/libdpkg.a $(ZLIB_LIBS) $(BZ2_LIBS)
+dpkg_query_SOURCES = \
+ errors.c \
+ filesdb.c filesdb.h \
+ query.c
+
+dpkg_query_LDADD = $(LIBINTL) ../lib/libdpkg.a $(ZLIB_LIBS) $(BZ2_LIBS)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \
+ $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \
+ else :; fi; \
+ done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
+ rm -f "$(DESTDIR)$(bindir)/$$f"; \
+ done
+
+clean-binPROGRAMS:
+ -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+dpkg$(EXEEXT): $(dpkg_OBJECTS) $(dpkg_DEPENDENCIES)
+ @rm -f dpkg$(EXEEXT)
+ $(LINK) $(dpkg_LDFLAGS) $(dpkg_OBJECTS) $(dpkg_LDADD) $(LIBS)
+dpkg-query$(EXEEXT): $(dpkg_query_OBJECTS) $(dpkg_query_DEPENDENCIES)
+ @rm -f dpkg-query$(EXEEXT)
+ $(LINK) $(dpkg_query_LDFLAGS) $(dpkg_query_OBJECTS) $(dpkg_query_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/archives.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cleanup.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/configure.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/depcon.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/enquiry.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errors.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filesdb.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/help.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packages.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/processarc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/query.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/remove.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/select.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/update.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(mkdir_p) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am: install-data-local
+
+install-exec-am: install-binPROGRAMS
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-info-am
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \
+ clean-generic ctags distclean distclean-compile \
+ distclean-generic distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-binPROGRAMS \
+ install-data install-data-am install-data-local install-exec \
+ install-exec-am install-info install-info-am install-man \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-binPROGRAMS \
+ uninstall-info-am
+
+
+install-data-local:
+ $(mkdir_p) $(DESTDIR)$(admindir)/alternatives
+ $(mkdir_p) $(DESTDIR)$(admindir)/info
+ $(mkdir_p) $(DESTDIR)$(admindir)/updates
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/archives.c b/src/archives.c
new file mode 100644
index 000000000..76d61edc3
--- /dev/null
+++ b/src/archives.c
@@ -0,0 +1,995 @@
+/*
+ * dpkg - main program for package management
+ * archives.c - actions that process archive files, mainly unpack
+ *
+ * Copyright (C) 1994,1995 Ian Jackson <ian@chiark.greenend.org.uk>
+ * Copyright (C) 2000 Wichert Akkerman <wakkerma@debian.org>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 dpkg; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <config.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <utime.h>
+#include <assert.h>
+#include <time.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <obstack.h>
+#define obstack_chunk_alloc m_malloc
+#define obstack_chunk_free free
+
+#include <dpkg.h>
+#include <dpkg-db.h>
+#include <tarfn.h>
+#include <myopt.h>
+
+#include "filesdb.h"
+#include "main.h"
+#include "archives.h"
+
+/* We shouldn't need anymore than 10 conflictors */
+struct pkginfo *conflictor[20];
+int cflict_index = 0;
+
+/* snprintf(3) doesn't work if format contains %.<nnn>s and an argument has
+ * invalid char for locale, then it returns -1.
+ * ohshite() is ok, but fd_fd_copy(), which is used in tarobject() in this
+ * file, is not ok, because
+ * - fd_fd_copy() == buffer_copy_setup() [include/dpkg.h]
+ * - buffer_copy_setup() uses varbufvprintf(&v, desc, al); [lib/mlib.c]
+ * - varbufvpprintf() fails and memory exausted, because it call
+ * fmt = "backend dpkg-deb during `%.255s'
+ * arg may contain some invalid char, for example,
+ * /usr/share/doc/console-tools/examples/unicode/\342\231\252\342\231\254
+ * in console-tools.
+ * In this case, if user uses some locale which doesn't support \342\231...,
+ * vsnprintf() always return -1 and varbufextend() again and again
+ * and memory exausted and die.
+ *
+ * So, we need to escape invalid char, probably as in
+ * tar-1.13.19/lib/quotearg.c: quotearg_buffer_restyled()
+ * but here I escape all 8bit chars, in order to be simple.
+ * - ukai@debian.or.jp
+ */
+static char *
+quote_filename(char *buf, int size, char *s)
+{
+ char *r = buf;
+ while (size > 0) {
+ switch (*s) {
+ case '\0':
+ *buf = '\0';
+ return r;
+ case '\\':
+ *buf++ = '\\';
+ *buf++ = '\\';
+ size -= 2;
+ break;
+ default:
+ if (((*s)&0x80) == 0) {
+ *buf++ = *s++;
+ --size;
+ } else {
+ if (size > 4) {
+ sprintf(buf, "\\%03o",
+ *(unsigned char *)s);
+ size -= 4;
+ buf += 4;
+ s++;
+ } else {
+ /* buffer full */
+ *buf = '\0'; /* XXX */
+ return s;
+ }
+ }
+ }
+ }
+ *buf = '\0'; /* XXX */
+ return r;
+
+}
+
+/* special routine to handle partial reads from the tarfile */
+static int safe_read(int fd, void *buf, int len)
+{
+ int r, have= 0;
+ char *p = (char *)buf;
+ while (have < len) {
+ if ((r= read(fd,p,len-have))==-1) {
+ if (errno==EINTR || errno==EAGAIN) continue;
+ return r;
+ }
+ if (r==0)
+ break;
+ have+= r;
+ p+= r;
+ }
+ return have;
+}
+
+static struct obstack tar_obs;
+static int tarobs_init= 0;
+
+/* ensure the obstack is properly initialized */
+static void ensureobstackinit(void) {
+
+ if (!tarobs_init) {
+ obstack_init(&tar_obs);
+ tarobs_init= 1;
+ }
+}
+
+/* destroy the obstack */
+static void destroyobstack(void) {
+ if (tarobs_init) {
+ obstack_free(&tar_obs, 0);
+ tarobs_init= 0;
+ }
+}
+
+int filesavespackage(struct fileinlist *file, struct pkginfo *pkgtobesaved,
+ struct pkginfo *pkgbeinginstalled) {
+ struct pkginfo *divpkg, *thirdpkg;
+ struct filepackages *packageslump;
+ int i;
+
+ debug(dbg_eachfiledetail,"filesavespackage file `%s' package %s",
+ file->namenode->name,pkgtobesaved->name);
+ /* A package can only be saved by a file or directory which is part
+ * only of itself - it must be neither part of the new package being
+ * installed nor part of any 3rd package (this is important so that
+ * shared directories don't stop packages from disappearing).
+ */
+ /* Is the file in the package being installed ? If so then it can't save.
+ */
+ if (file->namenode->flags & fnnf_new_inarchive) {
+ debug(dbg_eachfiledetail,"filesavespackage ... in new archive -- no save");
+ return 0;
+ }
+ /* If the file is a contended one and it's overridden by either
+ * the package we're considering disappearing or the package
+ * we're installing then they're not actually the same file, so
+ * we can't disappear the package - it is saved by this file.
+ */
+ if (file->namenode->divert && file->namenode->divert->useinstead) {
+ divpkg= file->namenode->divert->pkg;
+ if (divpkg == pkgtobesaved || divpkg == pkgbeinginstalled) {
+ debug(dbg_eachfiledetail,"filesavespackage ... diverted -- save!");
+ return 1;
+ }
+ }
+ /* Look for a 3rd package which can take over the file (in case
+ * it's a directory which is shared by many packages.
+ */
+ for (packageslump= file->namenode->packages;
+ packageslump;
+ packageslump= packageslump->more) {
+ for (i=0; i < PERFILEPACKAGESLUMP && packageslump->pkgs[i]; i++) {
+ thirdpkg= packageslump->pkgs[i];
+ debug(dbg_eachfiledetail, "filesavespackage ... also in %s",
+ thirdpkg->name);
+ /* Is this not the package being installed or the one being
+ * checked for disappearance ?
+ */
+ if (thirdpkg == pkgbeinginstalled || thirdpkg == pkgtobesaved) continue;
+ /* If !fileslistvalid then we've already disappeared this one, so
+ * we shouldn't try to make it take over this shared directory.
+ */
+ debug(dbg_eachfiledetail,"filesavespackage ... is 3rd package");
+
+ if (!thirdpkg->clientdata->fileslistvalid) {
+ debug(dbg_eachfiledetail,
+ _("process_archive ... already disappeared !"));
+ continue;
+ }
+ /* We've found a package that can take this file. */
+ debug(dbg_eachfiledetail, "filesavespackage ... taken -- no save");
+ return 0;
+ }
+ }
+ debug(dbg_eachfiledetail, "filesavespackage ... not taken -- save !");
+ return 1;
+}
+
+void cu_pathname(int argc, void **argv) {
+ ensure_pathname_nonexisting((char*)(argv[0]));
+}
+
+int tarfileread(void *ud, char *buf, int len) {
+ struct tarcontext *tc= (struct tarcontext*)ud;
+ int r;
+ if ((r= safe_read(tc->backendpipe,buf,len)) == -1)
+ ohshite(_("error reading from dpkg-deb pipe"));
+ return r;
+}
+
+int fnameidlu;
+struct varbuf fnamevb;
+struct varbuf fnametmpvb;
+struct varbuf fnamenewvb;
+struct packageinlist *deconfigure= 0;
+
+static time_t currenttime;
+
+static int does_replace(struct pkginfo *newpigp,
+ struct pkginfoperfile *newpifp,
+ struct pkginfo *oldpigp) {
+ struct dependency *dep;
+
+ debug(dbg_depcon,"does_replace new=%s old=%s (%s)",newpigp->name,
+ oldpigp->name,versiondescribe(&oldpigp->installed.version,
+ vdew_always));
+ for (dep= newpifp->depends; dep; dep= dep->next) {
+ if (dep->type != dep_replaces || dep->list->ed != oldpigp) continue;
+ debug(dbg_depcondetail,"does_replace ... found old, version %s",
+ versiondescribe(&dep->list->version,vdew_always));
+ if (!versionsatisfied(&oldpigp->installed,dep->list)) continue;
+ debug(dbg_depcon,"does_replace ... yes");
+ return 1;
+ }
+ debug(dbg_depcon,"does_replace ... no");
+ return 0;
+}
+
+static void newtarobject_utime(const char *path, struct TarInfo *ti) {
+ struct utimbuf utb;
+ utb.actime= currenttime;
+ utb.modtime= ti->ModTime;
+ if (utime(path,&utb))
+ ohshite(_("error setting timestamps of `%.255s'"),ti->Name);
+}
+
+static void newtarobject_allmodes(const char *path, struct TarInfo *ti, struct filestatoverride* statoverride) {
+ if (chown(path,
+ statoverride ? statoverride->uid : ti->UserID,
+ statoverride ? statoverride->gid : ti->GroupID))
+ ohshite(_("error setting ownership of `%.255s'"),ti->Name);
+ if (chmod(path,(statoverride ? statoverride->mode : ti->Mode) & ~S_IFMT))
+ ohshite(_("error setting permissions of `%.255s'"),ti->Name);
+ newtarobject_utime(path,ti);
+}
+
+void setupfnamevbs(const char *filename) {
+ fnamevb.used= fnameidlu;
+ varbufaddstr(&fnamevb,filename);
+ varbufaddc(&fnamevb,0);
+
+ fnametmpvb.used= fnameidlu;
+ varbufaddstr(&fnametmpvb,filename);
+ varbufaddstr(&fnametmpvb,DPKGTEMPEXT);
+ varbufaddc(&fnametmpvb,0);
+
+ fnamenewvb.used= fnameidlu;
+ varbufaddstr(&fnamenewvb,filename);
+ varbufaddstr(&fnamenewvb,DPKGNEWEXT);
+ varbufaddc(&fnamenewvb,0);
+
+ debug(dbg_eachfiledetail, "setupvnamevbs main=`%s' tmp=`%s' new=`%s'",
+ fnamevb.buf, fnametmpvb.buf, fnamenewvb.buf);
+}
+
+int unlinkorrmdir(const char *filename) {
+ /* Returns 0 on success or -1 on failure, just like unlink & rmdir */
+ int r, e;
+
+ if (!rmdir(filename)) {
+ debug(dbg_eachfiledetail,"unlinkorrmdir `%s' rmdir OK",filename);
+ return 0;
+ }
+
+ if (errno != ENOTDIR) {
+ e= errno;
+ debug(dbg_eachfiledetail,"unlinkorrmdir `%s' rmdir %s",filename,strerror(e));
+ errno= e; return -1;
+ }
+
+ r= unlink(filename); e= errno;
+ debug(dbg_eachfiledetail,"unlinkorrmdir `%s' unlink %s",
+ filename, r ? strerror(e) : "OK");
+ errno= e; return r;
+}
+
+int tarobject(struct TarInfo *ti) {
+ static struct varbuf conffderefn, hardlinkfn, symlinkfn;
+ const char *usename;
+
+ struct tarcontext *tc= (struct tarcontext*)ti->UserData;
+ int statr, fd, i, existingdirectory;
+ size_t r;
+ struct stat stab, stabd;
+ char databuf[TARBLKSZ];
+ struct fileinlist *nifd;
+ struct pkginfo *divpkg, *otherpkg;
+ struct filepackages *packageslump;
+ mode_t am;
+
+ ensureobstackinit();
+
+ /* Append to list of files.
+ * The trailing / put on the end of names in tarfiles has already
+ * been stripped by TarExtractor (lib/tarfn.c).
+ */
+ nifd= obstack_alloc(&tar_obs, sizeof(struct fileinlist));
+ nifd->namenode= findnamenode(ti->Name, 0);
+ nifd->next= 0; *tc->newfilesp= nifd; tc->newfilesp= &nifd->next;
+ nifd->namenode->flags |= fnnf_new_inarchive;
+
+ debug(dbg_eachfile,
+ "tarobject ti->Name=`%s' Mode=%lo owner=%u.%u Type=%d(%c)"
+ " ti->LinkName=`%s' namenode=`%s' flags=%o instead=`%s'",
+ ti->Name, (long)ti->Mode, (unsigned)ti->UserID, (unsigned)ti->GroupID, ti->Type,
+ ti->Type == '\0' ? '_' :
+ ti->Type >= '0' && ti->Type <= '6' ? "-hlcbdp"[ti->Type - '0'] : '?',
+ ti->LinkName,
+ nifd->namenode->name, nifd->namenode->flags,
+ nifd->namenode->divert && nifd->namenode->divert->useinstead
+ ? nifd->namenode->divert->useinstead->name : "<none>");
+
+ if (nifd->namenode->divert && nifd->namenode->divert->camefrom) {
+ divpkg= nifd->namenode->divert->pkg;
+ forcibleerr(fc_overwritediverted,
+ _("trying to overwrite `%.250s', which is the "
+ "diverted version of `%.250s'%.10s%.100s%.10s"),
+ nifd->namenode->name,
+ nifd->namenode->divert->camefrom->name,
+ divpkg ? _(" (package: ") : "",
+ divpkg ? divpkg->name : "",
+ divpkg ? ")" : "");
+ }
+
+ usename= namenodetouse(nifd->namenode,tc->pkg)->name + 1; /* Skip the leading `/' */
+
+ if (nifd->namenode->flags & fnnf_new_conff) {
+ /* If it's a conffile we have to extract it next to the installed
+ * version (ie, we do the usual link-following).
+ */
+ if (conffderef(tc->pkg, &conffderefn, usename))
+ usename= conffderefn.buf;
+ debug(dbg_conff,"tarobject fnnf_new_conff deref=`%s'",usename);
+ }
+
+ setupfnamevbs(usename);
+
+ statr= lstat(fnamevb.buf,&stab);
+ if (statr) {
+ /* The lstat failed. */
+ if (errno != ENOENT && errno != ENOTDIR)
+ ohshite(_("unable to stat `%.255s' (which I was about to install)"),ti->Name);
+ /* OK, so it doesn't exist.
+ * However, it's possible that we were in the middle of some other
+ * backup/restore operation and were rudely interrupted.
+ * So, we see if we have .dpkg-tmp, and if so we restore it.
+ */
+ if (rename(fnametmpvb.buf,fnamevb.buf)) {
+ if (errno != ENOENT && errno != ENOTDIR)
+ ohshite(_("unable to clean up mess surrounding `%.255s' before "
+ "installing another version"),ti->Name);
+ debug(dbg_eachfiledetail,"tarobject nonexistent");
+ } else {
+ debug(dbg_eachfiledetail,"tarobject restored tmp to main");
+ statr= lstat(fnamevb.buf,&stab);
+ if (statr) ohshite(_("unable to stat restored `%.255s' before installing"
+ " another version"), ti->Name);
+ }
+ } else {
+ debug(dbg_eachfiledetail,"tarobject already exists");
+ }
+
+ /* Check to see if it's a directory or link to one and we don't need to
+ * do anything. This has to be done now so that we don't die due to
+ * a file overwriting conflict.
+ */
+ existingdirectory= 0;
+ switch (ti->Type) {
+ case SymbolicLink:
+ /* If it's already an existing directory, do nothing. */
+ if (!statr && S_ISDIR(stab.st_mode)) {
+ debug(dbg_eachfiledetail,"tarobject SymbolicLink exists as directory");
+ existingdirectory= 1;
+ }
+ break;
+ case Directory:
+ /* If it's already an existing directory, do nothing. */
+ if (!stat(fnamevb.buf,&stabd) && S_ISDIR(stabd.st_mode)) {
+ debug(dbg_eachfiledetail,"tarobject Directory exists");
+ existingdirectory= 1;
+ }
+ break;
+ case NormalFile0: case NormalFile1:
+ case CharacterDevice: case BlockDevice: case FIFO:
+ case HardLink:
+ break;
+ default:
+ ohshit(_("archive contained object `%.255s' of unknown type 0x%x"),ti->Name,ti->Type);
+ }
+
+ if (!existingdirectory) {
+ for (packageslump= nifd->namenode->packages;
+ packageslump;
+ packageslump= packageslump->more) {
+ for (i=0; i < PERFILEPACKAGESLUMP && packageslump->pkgs[i]; i++) {
+ otherpkg= packageslump->pkgs[i];
+ if (otherpkg == tc->pkg) continue;
+ debug(dbg_eachfile, "tarobject ... found in %s",otherpkg->name);
+ if (nifd->namenode->divert && nifd->namenode->divert->useinstead) {
+ /* Right, so we may be diverting this file. This makes the conflict
+ * OK iff one of us is the diverting package (we don't need to
+ * check for both being the diverting package, obviously).
+ */
+ divpkg= nifd->namenode->divert->pkg;
+ debug(dbg_eachfile, "tarobject ... diverted, divpkg=%s",
+ divpkg ? divpkg->name : "<none>");
+ if (otherpkg == divpkg || tc->pkg == divpkg) continue;
+ }
+ /* Nope ? Hmm, file conflict, perhaps. Check Replaces. */
+ if (otherpkg->clientdata->replacingfilesandsaid) continue;
+ /* Is the package with the conflicting file in the `config files
+ * only' state ? If so it must be a config file and we can
+ * silenty take it over.
+ */
+ if (otherpkg->status == stat_configfiles) continue;
+ /* Perhaps we're removing a conflicting package ? */
+ if (otherpkg->clientdata->istobe == itb_remove) continue;
+ if (does_replace(tc->pkg,&tc->pkg->available,otherpkg)) {
+ printf(_("Replacing files in old package %s ...\n"),otherpkg->name);
+ otherpkg->clientdata->replacingfilesandsaid= 1;
+ } else {
+ if (!statr && S_ISDIR(stab.st_mode)) {
+ forcibleerr(fc_overwritedir, _("trying to overwrite directory `%.250s' "
+ "in package %.250s with nondirectory"),
+ nifd->namenode->name,otherpkg->name);
+ } else {
+ /* WTA: At this point we are replacing something without a Replaces.
+ * if the new object is a directory and the previous object does not
+ * exist assume it's also a directory and don't complain
+ */
+ if (! (statr && ti->Type==Directory))
+ forcibleerr(fc_overwrite,
+ _("trying to overwrite `%.250s', which is also in package %.250s"),
+ nifd->namenode->name,otherpkg->name);
+ }
+ }
+ }
+ }
+ }
+
+ /* Now, at this stage we want to make sure neither of .dpkg-new and .dpkg-tmp
+ * are hanging around.
+ */
+ ensure_pathname_nonexisting(fnamenewvb.buf);
+ ensure_pathname_nonexisting(fnametmpvb.buf);
+
+ if (existingdirectory) return 0;
+
+ /* Now we start to do things that we need to be able to undo
+ * if something goes wrong.
+ */
+ push_cleanup(cu_installnew,~ehflag_normaltidy, 0,0, 1,(void*)nifd);
+
+ /* Extract whatever it is as .dpkg-new ... */
+ switch (ti->Type) {
+ case NormalFile0: case NormalFile1:
+ /* We create the file with mode 0 to make sure nobody can do anything with
+ * it until we apply the proper mode, which might be a statoverride.
+ */
+ fd= open(fnamenewvb.buf, (O_CREAT|O_EXCL|O_WRONLY), 0);
+ if (fd < 0) ohshite(_("unable to create `%.255s'"),ti->Name);
+ push_cleanup(cu_closefd,ehflag_bombout, 0,0, 1,&fd);
+ debug(dbg_eachfiledetail,"tarobject NormalFile[01] open size=%lu",
+ (unsigned long)ti->Size);
+ { char fnamebuf[256];
+ fd_fd_copy(tc->backendpipe, fd, ti->Size, _("backend dpkg-deb during `%.255s'"),quote_filename(fnamebuf,256,ti->Name));
+ }
+ r= ti->Size % TARBLKSZ;
+ if (r > 0) r= safe_read(tc->backendpipe,databuf,TARBLKSZ - r);
+ if (nifd->namenode->statoverride)
+ debug(dbg_eachfile, "tarobject ... stat override, uid=%d, gid=%d, mode=%04o",
+ nifd->namenode->statoverride->uid,
+ nifd->namenode->statoverride->gid,
+ nifd->namenode->statoverride->mode);
+ if (fchown(fd,
+ nifd->namenode->statoverride ? nifd->namenode->statoverride->uid : ti->UserID,
+ nifd->namenode->statoverride ? nifd->namenode->statoverride->gid : ti->GroupID))
+ ohshite(_("error setting ownership of `%.255s'"),ti->Name);
+ am=(nifd->namenode->statoverride ? nifd->namenode->statoverride->mode : ti->Mode) & ~S_IFMT;
+ if (fchmod(fd,am))
+ ohshite(_("error setting permissions of `%.255s'"),ti->Name);
+ pop_cleanup(ehflag_normaltidy); /* fd= open(fnamenewvb.buf) */
+ if (close(fd))
+ ohshite(_("error closing/writing `%.255s'"),ti->Name);
+ newtarobject_utime(fnamenewvb.buf,ti);
+ break;
+ case FIFO:
+ if (mkfifo(fnamenewvb.buf,0))
+ ohshite(_("error creating pipe `%.255s'"),ti->Name);
+ debug(dbg_eachfiledetail,"tarobject FIFO");
+ newtarobject_allmodes(fnamenewvb.buf,ti, nifd->namenode->statoverride);
+ break;
+ case CharacterDevice:
+ if (mknod(fnamenewvb.buf,S_IFCHR, ti->Device))
+ ohshite(_("error creating device `%.255s'"),ti->Name);
+ debug(dbg_eachfiledetail,"tarobject CharacterDevice");
+ newtarobject_allmodes(fnamenewvb.buf,ti, nifd->namenode->statoverride);
+ break;
+ case BlockDevice:
+ if (mknod(fnamenewvb.buf,S_IFBLK, ti->Device))
+ ohshite(_("error creating device `%.255s'"),ti->Name);
+ debug(dbg_eachfiledetail,"tarobject BlockDevice");
+ newtarobject_allmodes(fnamenewvb.buf,ti, nifd->namenode->statoverride);
+ break;
+ case HardLink:
+ varbufreset(&hardlinkfn);
+ varbufaddstr(&hardlinkfn,instdir); varbufaddc(&hardlinkfn,'/');
+ varbufaddstr(&hardlinkfn,ti->LinkName); varbufaddc(&hardlinkfn,0);
+ if (link(hardlinkfn.buf,fnamenewvb.buf))
+ ohshite(_("error creating hard link `%.255s'"),ti->Name);
+ debug(dbg_eachfiledetail,"tarobject HardLink");
+ newtarobject_allmodes(fnamenewvb.buf,ti, nifd->namenode->statoverride);
+ break;
+ case SymbolicLink:
+ /* We've already cheched for an existing directory. */
+ if (symlink(ti->LinkName,fnamenewvb.buf))
+ ohshite(_("error creating symbolic link `%.255s'"),ti->Name);
+ debug(dbg_eachfiledetail,"tarobject SymbolicLink creating");
+#ifdef HAVE_LCHOWN
+ if (lchown(fnamenewvb.buf,
+#else
+ if (chown(fnamenewvb.buf,
+#endif
+ nifd->namenode->statoverride ? nifd->namenode->statoverride->uid : ti->UserID,
+ nifd->namenode->statoverride ? nifd->namenode->statoverride->gid : ti->GroupID))
+ ohshite(_("error setting ownership of symlink `%.255s'"),ti->Name);
+ break;
+ case Directory:
+ /* We've already checked for an existing directory. */
+ if (mkdir(fnamenewvb.buf,0))
+ ohshite(_("error creating directory `%.255s'"),ti->Name);
+ debug(dbg_eachfiledetail,"tarobject Directory creating");
+ newtarobject_allmodes(fnamenewvb.buf,ti,nifd->namenode->statoverride);
+ break;
+ default:
+ internerr("bad tar type, but already checked");
+ }
+ /*
+ * Now we have extracted the new object in .dpkg-new (or, if the
+ * file already exists as a directory and we were trying to extract
+ * a directory or symlink, we returned earlier, so we don't need
+ * to worry about that here).
+ */
+
+ /* First, check to see if it's a conffile. If so we don't install
+ * it now - we leave it in .dpkg-new for --configure to take care of
+ */
+ if (nifd->namenode->flags & fnnf_new_conff) {
+ debug(dbg_conffdetail,"tarobject conffile extracted");
+ nifd->namenode->flags |= fnnf_elide_other_lists;
+ return 0;
+ }
+
+ /* Now we install it. If we can do an atomic overwrite we do so.
+ * If not we move aside the old file and then install the new.
+ * The backup file will be deleted later.
+ */
+ if (statr) { /* Don't try to back it up if it didn't exist. */
+ debug(dbg_eachfiledetail,"tarobject new - no backup");
+ } else {
+ if (ti->Type == Directory || S_ISDIR(stab.st_mode)) {
+ /* One of the two is a directory - can't do atomic install. */
+ debug(dbg_eachfiledetail,"tarobject directory, nonatomic");
+ nifd->namenode->flags |= fnnf_no_atomic_overwrite;
+ if (rename(fnamevb.buf,fnametmpvb.buf))
+ ohshite(_("unable to move aside `%.255s' to install new version"),ti->Name);
+ } else if (S_ISLNK(stab.st_mode)) {
+ /* We can't make a symlink with two hardlinks, so we'll have to copy it.
+ * (Pretend that making a copy of a symlink is the same as linking to it.)
+ */
+ varbufreset(&symlinkfn);
+ do {
+ varbufextend(&symlinkfn);
+ r= readlink(fnamevb.buf,symlinkfn.buf,symlinkfn.size);
+ if (r<0) ohshite(_("unable to read link `%.255s'"),ti->Name);
+ } while (r == symlinkfn.size);
+ symlinkfn.used= r; varbufaddc(&symlinkfn,0);
+ if (symlink(symlinkfn.buf,fnametmpvb.buf))
+ ohshite(_("unable to make backup symlink for `%.255s'"),ti->Name);
+#ifdef HAVE_LCHOWN
+ if (lchown(fnametmpvb.buf,stab.st_uid,stab.st_gid))
+#else
+ if (chown(fnametmpvb.buf,stab.st_uid,stab.st_gid))
+#endif
+ ohshite(_("unable to chown backup symlink for `%.255s'"),ti->Name);
+ } else {
+ debug(dbg_eachfiledetail,"tarobject nondirectory, `link' backup");
+ if (link(fnamevb.buf,fnametmpvb.buf))
+ ohshite(_("unable to make backup link of `%.255s' before installing new version"),
+ ti->Name);
+ }
+ }
+
+ if (rename(fnamenewvb.buf,fnamevb.buf))
+ ohshite(_("unable to install new version of `%.255s'"),ti->Name);
+
+ nifd->namenode->flags |= fnnf_elide_other_lists;
+
+ debug(dbg_eachfiledetail,"tarobject done and installed");
+ return 0;
+}
+
+static int try_remove_can(struct deppossi *pdep,
+ struct pkginfo *fixbyrm,
+ const char *why) {
+ struct packageinlist *newdeconf;
+
+ if (force_depends(pdep)) {
+ fprintf(stderr, _("dpkg: warning - "
+ "ignoring dependency problem with removal of %s:\n%s"),
+ fixbyrm->name, why);
+ return 1;
+ } else if (f_autodeconf) {
+ if (pdep->up->up->installed.essential) {
+ if (fc_removeessential) {
+ fprintf(stderr, _("dpkg: warning - considering deconfiguration of essential\n"
+ " package %s, to enable removal of %s.\n"),
+ pdep->up->up->name,fixbyrm->name);
+ } else {
+ fprintf(stderr, _("dpkg: no, %s is essential, will not deconfigure\n"
+ " it in order to enable removal of %s.\n"),
+ pdep->up->up->name,fixbyrm->name);
+ return 0;
+ }
+ }
+ pdep->up->up->clientdata->istobe= itb_deconfigure;
+ newdeconf= malloc(sizeof(struct packageinlist));
+ newdeconf->next= deconfigure;
+ newdeconf->pkg= pdep->up->up;
+ deconfigure= newdeconf;
+ return 1;
+ } else {
+ fprintf(stderr, _("dpkg: no, cannot remove %s (--auto-deconfigure will help):\n%s"),
+ fixbyrm->name, why);
+ return 0;
+ }
+}
+
+void check_conflict(struct dependency *dep, struct pkginfo *pkg,
+ const char *pfilename) {
+ struct pkginfo *fixbyrm;
+ struct deppossi *pdep, flagdeppossi;
+ struct varbuf conflictwhy, removalwhy;
+ struct dependency *providecheck;
+
+ varbufinit(&conflictwhy);
+ varbufinit(&removalwhy);
+
+ fixbyrm= 0;
+ if (depisok(dep, &conflictwhy, &fixbyrm, 0)) {
+ varbuffree(&conflictwhy);
+ varbuffree(&removalwhy);
+ return;
+ }
+ if (fixbyrm) {
+ ensure_package_clientdata(fixbyrm);
+ if (fixbyrm->clientdata->istobe == itb_installnew) {
+ fixbyrm= dep->up;
+ ensure_package_clientdata(fixbyrm);
+ }
+ if (((pkg->available.essential && fixbyrm->installed.essential) ||
+ (((fixbyrm->want != want_install && fixbyrm->want != want_hold) ||
+ does_replace(pkg,&pkg->available,fixbyrm)) &&
+ (!fixbyrm->installed.essential || fc_removeessential)))) {
+ assert(fixbyrm->clientdata->istobe == itb_normal || fixbyrm->clientdata->istobe == itb_deconfigure);
+ fixbyrm->clientdata->istobe= itb_remove;
+ fprintf(stderr, _("dpkg: considering removing %s in favour of %s ...\n"),
+ fixbyrm->name, pkg->name);
+ if (fixbyrm->status != stat_installed) {
+ fprintf(stderr,
+ _("%s is not properly installed - ignoring any dependencies on it.\n"),
+ fixbyrm->name);
+ pdep= 0;
+ } else {
+ for (pdep= fixbyrm->installed.depended;
+ pdep;
+ pdep= pdep->nextrev) {
+ if (pdep->up->type != dep_depends && pdep->up->type != dep_predepends)
+ continue;
+ if (depisok(pdep->up, &removalwhy, 0,0)) continue;
+ varbufaddc(&removalwhy,0);
+ if (!try_remove_can(pdep,fixbyrm,removalwhy.buf))
+ break;
+ }
+ if (!pdep) {
+ /* If we haven't found a reason not to yet, let's look some more. */
+ for (providecheck= fixbyrm->installed.depends;
+ providecheck;
+ providecheck= providecheck->next) {
+ if (providecheck->type != dep_provides) continue;
+ for (pdep= providecheck->list->ed->installed.depended;
+ pdep;
+ pdep= pdep->nextrev) {
+ if (pdep->up->type != dep_depends && pdep->up->type != dep_predepends)
+ continue;
+ if (depisok(pdep->up, &removalwhy, 0,0)) continue;
+ varbufaddc(&removalwhy,0);
+ fprintf(stderr, _("dpkg"
+ ": may have trouble removing %s, as it provides %s ...\n"),
+ fixbyrm->name, providecheck->list->ed->name);
+ if (!try_remove_can(pdep,fixbyrm,removalwhy.buf))
+ goto break_from_both_loops_at_once;
+ }
+ }
+ break_from_both_loops_at_once:;
+ }
+ }
+ if (!pdep && skip_due_to_hold(fixbyrm)) {
+ pdep= &flagdeppossi;
+ }
+ if (!pdep && (fixbyrm->eflag & eflagf_reinstreq)) {
+ if (fc_removereinstreq) {
+ fprintf(stderr, _("dpkg: package %s requires reinstallation, but will"
+ " remove anyway as you request.\n"), fixbyrm->name);
+ } else {
+ fprintf(stderr, _("dpkg: package %s requires reinstallation, "
+ "will not remove.\n"), fixbyrm->name);
+ pdep= &flagdeppossi;
+ }
+ }
+ if (!pdep) {
+ /* if this gets triggered, it means a package has > 10 conflicts/replaces
+ * pairs, which is the package's fault
+ */
+ assert(cflict_index < (int)sizeof(conflictor));
+ /* This conflict is OK - we'll remove the conflictor. */
+ conflictor[cflict_index++]= fixbyrm;
+ varbuffree(&conflictwhy); varbuffree(&removalwhy);
+ fprintf(stderr, _("dpkg: yes, will remove %s in favour of %s.\n"),
+ fixbyrm->name, pkg->name);
+ return;
+ }
+ fixbyrm->clientdata->istobe= itb_normal; /* put it back */
+ }
+ }
+ varbufaddc(&conflictwhy,0);
+ fprintf(stderr, _("dpkg: regarding %s containing %s:\n%s"),
+ pfilename, pkg->name, conflictwhy.buf);
+ if (!force_conflicts(dep->list))
+ ohshit(_("conflicting packages - not installing %.250s"),pkg->name);
+ fprintf(stderr, _("dpkg: warning - ignoring conflict, may proceed anyway !\n"));
+ varbuffree(&conflictwhy);
+
+ return;
+}
+
+void cu_cidir(int argc, void **argv) {
+ char *cidir= (char*)argv[0];
+ char *cidirrest= (char*)argv[1];
+ cidirrest[-1]= 0;
+ ensure_pathname_nonexisting(cidir);
+}
+
+void cu_fileslist(int argc, void **argv) {
+ destroyobstack();
+}
+
+void archivefiles(const char *const *argv) {
+ const char *volatile thisarg;
+ const char *const *volatile argp;
+ jmp_buf ejbuf;
+ int pi[2], fc, nfiles, c, i, r;
+ FILE *pf;
+ static struct varbuf findoutput;
+ const char **arglist;
+ char *p;
+
+ modstatdb_init(admindir,
+ f_noact ? msdbrw_readonly
+ : cipaction->arg == act_avail ? msdbrw_write
+ : fc_nonroot ? msdbrw_write
+ : msdbrw_needsuperuser);
+
+ checkpath();
+
+ if (f_recursive) {
+
+ if (!*argv)
+ badusage(_("--%s --recursive needs at least one path argument"),cipaction->olong);
+
+ m_pipe(pi);
+ if (!(fc= m_fork())) {
+ const char *const *ap;
+ char **narglist;
+ int i;
+ m_dup2(pi[1],1); close(pi[0]); close(pi[1]);
+ for (i=0, ap=argv; *ap; ap++, i++);
+ narglist= m_malloc(sizeof(char*)*(i+15));
+ narglist[0]= strdup(FIND);
+ for (i=1, ap=argv; *ap; ap++, i++) {
+ if (strchr(FIND_EXPRSTARTCHARS,(*ap)[0])) {
+ char *a;
+ a= m_malloc(strlen(*ap)+10);
+ strcpy(a,"./");
+ strcat(a,*ap);
+ narglist[i]= a;
+ } else {
+ narglist[i]= strdup(*ap);
+ }
+ }
+ narglist[i++]= strdup("-follow"); /* When editing these, make sure that */
+ narglist[i++]= strdup("-name"); /* arglist is mallocd big enough, above. */
+ narglist[i++]= strdup(ARCHIVE_FILENAME_PATTERN);
+ narglist[i++]= strdup("-type");
+ narglist[i++]= strdup("f");
+ narglist[i++]= strdup("-print0");
+ narglist[i++]= 0;
+ execvp(FIND, narglist);
+ ohshite(_("failed to exec find for --recursive"));
+ }
+ close(pi[1]);
+
+ nfiles= 0;
+ pf= fdopen(pi[0],"r"); if (!pf) ohshite(_("failed to fdopen find's pipe"));
+ varbufreset(&findoutput);
+ while ((c= fgetc(pf)) != EOF) {
+ varbufaddc(&findoutput,c);
+ if (!c) nfiles++;
+ }
+ if (ferror(pf)) ohshite(_("error reading find's pipe"));
+ if (fclose(pf)) ohshite(_("error closing find's pipe"));
+ r= waitsubproc(fc,"find",PROCNOERR);
+ if(!(r==0 || r==1))
+ ohshit(_("find for --recursive returned unhandled error %i"),r);
+
+ if (!nfiles)
+ ohshit(_("searched, but found no packages (files matching *.deb)"));
+
+ varbufaddc(&findoutput,0);
+ varbufaddc(&findoutput,0);
+
+ arglist= m_malloc(sizeof(char*)*(nfiles+1));
+ p= findoutput.buf; i=0;
+ while (*p) {
+ arglist[i++]= p;
+ while ((c= *p++) != 0);
+ }
+ arglist[i]= 0;
+ argp= arglist;
+
+ } else {
+
+ if (!*argv) badusage(_("--%s needs at least one package archive file argument"),
+ cipaction->olong);
+ argp= argv;
+
+ }
+
+ currenttime= time(0);
+
+ varbufinit(&fnamevb);
+ varbufinit(&fnametmpvb);
+ varbufinit(&fnamenewvb);
+
+ varbufaddstr(&fnamevb,instdir); varbufaddc(&fnamevb,'/');
+ varbufaddstr(&fnametmpvb,instdir); varbufaddc(&fnametmpvb,'/');
+ varbufaddstr(&fnamenewvb,instdir); varbufaddc(&fnamenewvb,'/');
+ fnameidlu= fnamevb.used;
+
+ ensure_diversions();
+ ensure_statoverrides();
+
+ while ((thisarg= *argp++) != 0) {
+ if (setjmp(ejbuf)) {
+ error_unwind(ehflag_bombout);
+ if (onerr_abort > 0) break;
+ continue;
+ }
+ push_error_handler(&ejbuf,print_error_perpackage,thisarg);
+ process_archive(thisarg);
+ onerr_abort++;
+ if (ferror(stdout)) werr("stdout");
+ if (ferror(stderr)) werr("stderr");
+ onerr_abort--;
+ set_error_display(0,0);
+ error_unwind(ehflag_normaltidy);
+ }
+
+ switch (cipaction->arg) {
+ case act_install:
+ case act_configure:
+ case act_remove:
+ case act_purge:
+ process_queue();
+ case act_unpack:
+ case act_avail:
+ break;
+ default:
+ internerr("unknown action");
+ }
+
+ modstatdb_shutdown();
+}
+
+int wanttoinstall(struct pkginfo *pkg, const struct versionrevision *ver, int saywhy) {
+ /* Decide whether we want to install a new version of the package.
+ * ver is the version we might want to install. If saywhy is 1 then
+ * if we skip the package we say what we are doing (and, if we are
+ * selecting a previously deselected package, say so and actually do
+ * the select). want_install returns 0 if the package should be
+ * skipped and 1 if it should be installed.
+ *
+ * ver may be 0, in which case saywhy must be 0 and want_install may
+ * also return -1 to mean it doesn't know because it would depend on
+ * the version number.
+ */
+ enum versiondisplayepochwhen needepochs;
+ int r;
+
+ if (pkg->want != want_install && pkg->want != want_hold) {
+ if (f_alsoselect) {
+ if (saywhy) {
+ printf(_("Selecting previously deselected package %s.\n"),pkg->name);
+ pkg->want= want_install;
+ }
+ return 1;
+ } else {
+ if (saywhy) printf(_("Skipping deselected package %s.\n"),pkg->name);
+ return 0;
+ }
+ }
+
+ if (pkg->status != stat_installed) return 1;
+ if (!ver) return -1;
+
+ r= versioncompare(ver,&pkg->installed.version);
+ if (r > 0) {
+ return 1;
+ } else if (r == 0) {
+ if (f_skipsame && /* same version fully installed ? */
+ pkg->status == stat_installed && !(pkg->eflag &= eflagf_reinstreq)) {
+ if (saywhy) fprintf(stderr, _("Version %.250s of %.250s already installed, "
+ "skipping.\n"),
+ versiondescribe(&pkg->installed.version,vdew_never),
+ pkg->name);
+ return 0;
+ } else {
+ return 1;
+ }
+ } else {
+ needepochs= epochsdiffer(&pkg->available.version,&pkg->installed.version) ?
+ vdew_always : vdew_never;
+ if (fc_downgrade) {
+ if (saywhy) fprintf(stderr, _("%s - warning: downgrading %.250s "
+ "from %.250s to %.250s.\n"), DPKG, pkg->name,
+ versiondescribe(&pkg->installed.version,needepochs),
+ versiondescribe(&pkg->available.version,needepochs));
+ return 1;
+ } else {
+ if (saywhy) fprintf(stderr, _("Will not downgrade %.250s from version %.250s "
+ "to %.250s, skipping.\n"), pkg->name,
+ versiondescribe(&pkg->installed.version,needepochs),
+ versiondescribe(&pkg->available.version,needepochs));
+ return 0;
+ }
+ }
+}
+
+/* vi: ts=8 sw=2
+ */
diff --git a/src/archives.h b/src/archives.h
new file mode 100644
index 000000000..6141b6933
--- /dev/null
+++ b/src/archives.h
@@ -0,0 +1,71 @@
+/*
+ * dpkg - main program for package management
+ * archives.h - functions common to archives.c and processarc.c
+ *
+ * Copyright (C) 1995 Ian Jackson <ian@chiark.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 dpkg; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef ARCHIVES_H
+#define ARCHIVES_H
+
+struct tarcontext {
+ int backendpipe;
+ struct pkginfo *pkg;
+ struct fileinlist **newfilesp;
+};
+
+extern int fnameidlu;
+extern struct varbuf fnamevb;
+extern struct varbuf fnametmpvb;
+extern struct varbuf fnamenewvb;
+extern struct packageinlist *deconfigure;
+
+extern struct pkginfo *conflictor[];
+extern int cflict_index;
+
+void cu_pathname(int argc, void **argv);
+void cu_cidir(int argc, void **argv);
+void cu_fileslist(int argc, void **argv);
+void cu_backendpipe(int argc, void **argv);
+
+void cu_installnew(int argc, void **argv);
+
+void cu_prermupgrade(int argc, void **argv);
+void cu_prerminfavour(int argc, void **argv);
+void cu_preinstverynew(int argc, void **argv);
+void cu_preinstnew(int argc, void **argv);
+void cu_preinstupgrade(int argc, void **argv);
+void cu_postrmupgrade(int argc, void **argv);
+
+void cu_prermdeconfigure(int argc, void **argv);
+void ok_prermdeconfigure(int argc, void **argv);
+
+void setupfnamevbs(const char *filename);
+int unlinkorrmdir(const char *filename);
+
+int tarobject(struct TarInfo *ti);
+int tarfileread(void *ud, char *buf, int len);
+
+int filesavespackage(struct fileinlist*, struct pkginfo*,
+ struct pkginfo *pkgbeinginstalled);
+
+void check_conflict(struct dependency *dep, struct pkginfo *pkg,
+ const char *pfilename);
+
+extern int cleanup_pkg_failed, cleanup_conflictor_failed;
+
+#endif /* ARCHIVES_H */
diff --git a/src/cleanup.c b/src/cleanup.c
new file mode 100644
index 000000000..3d93e8e42
--- /dev/null
+++ b/src/cleanup.c
@@ -0,0 +1,227 @@
+/*
+ * dpkg - main program for package management
+ * cleanup.c - cleanup functions, used when we need to unwind
+ *
+ * Copyright (C) 1995 Ian Jackson <ian@chiark.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 dpkg; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <config.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <utime.h>
+#include <assert.h>
+#include <time.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <dpkg.h>
+#include <dpkg-db.h>
+#include <tarfn.h>
+#include <myopt.h>
+
+#include "filesdb.h"
+#include "main.h"
+#include "archives.h"
+
+int cleanup_pkg_failed=0, cleanup_conflictor_failed=0;
+
+void cu_installnew(int argc, void **argv) {
+ /* Something went wrong and we're undoing.
+ * We have the following possible situations for non-conffiles:
+ * <foo>.dpkg-tmp exists - in this case we want to remove
+ * <foo> if it exists and replace it with <foo>.dpkg-tmp.
+ * This undoes the backup operation. We also make sure
+ * we delete <foo>.dpkg-new in case that's still hanging around.
+ * <foo>.dpkg-tmp does not exist - in this case we haven't
+ * got as far as creating it (or there wasn't an old version).
+ * In this case we just delete <foo>.dpkg-new if it exists,
+ * as it may be a half-extracted thing.
+ * For conffiles, we simply delete <foo>.dpkg-new. For these,
+ * <foo>.dpkg-tmp shouldn't exist, as we don't make a backup
+ * at this stage. Just to be on the safe side, though, we don't
+ * look for it.
+ */
+ struct fileinlist *nifd= (struct fileinlist*)argv[0];
+ struct filenamenode *namenode;
+ struct stat stab;
+
+ cleanup_pkg_failed++; cleanup_conflictor_failed++;
+
+ namenode= nifd->namenode;
+ debug(dbg_eachfile,"cu_installnew `%s' flags=%o",namenode->name,namenode->flags);
+
+ setupfnamevbs(namenode->name);
+
+ if (!(namenode->flags & fnnf_new_conff) && !lstat(fnametmpvb.buf,&stab)) {
+ /* OK, <foo>.dpkg-tmp exists. Remove <foo> and
+ * restore <foo>.dpkg-tmp ...
+ */
+ if (namenode->flags & fnnf_no_atomic_overwrite) {
+ /* If we can't do an atomic overwrite we have to delete first any
+ * link to the new version we may have created.
+ */
+ debug(dbg_eachfiledetail,"cu_installnew restoring nonatomic");
+ if (unlinkorrmdir(fnamevb.buf) && errno != ENOENT && errno != ENOTDIR)
+ ohshite(_("unable to remove newly-installed version of `%.250s' to allow"
+ " reinstallation of backup copy"),namenode->name);
+ } else {
+ debug(dbg_eachfiledetail,"cu_installnew restoring atomic");
+ }
+ /* Either we can do an atomic restore, or we've made room: */
+ if (rename(fnametmpvb.buf,fnamevb.buf))
+ ohshite(_("unable to restore backup version of `%.250s'"),namenode->name);
+ } else {
+ debug(dbg_eachfiledetail,"cu_installnew not restoring");
+ }
+ /* Whatever, we delete <foo>.dpkg-new now, if it still exists. */
+ if (unlinkorrmdir(fnamenewvb.buf) && errno != ENOENT && errno != ENOTDIR)
+ ohshite(_("unable to remove newly-extracted version of `%.250s'"),namenode->name);
+
+ cleanup_pkg_failed--; cleanup_conflictor_failed--;
+}
+
+void cu_prermupgrade(int argc, void **argv) {
+ struct pkginfo *pkg= (struct pkginfo*)argv[0];
+
+ if (cleanup_pkg_failed++) return;
+ maintainer_script_installed(pkg,POSTINSTFILE,"post-installation",
+ "abort-upgrade",
+ versiondescribe(&pkg->available.version,
+ vdew_nonambig),
+ (char*)0);
+ pkg->status= stat_installed;
+ pkg->eflag &= ~eflagf_reinstreq;
+ modstatdb_note(pkg);
+ cleanup_pkg_failed--;
+}
+
+void ok_prermdeconfigure(int argc, void **argv) {
+ struct pkginfo *deconf= (struct pkginfo*)argv[0];
+ /* also has conflictor in argv[1] and infavour in argv[2] */
+
+ if (cipaction->arg == act_install)
+ add_to_queue(deconf);
+}
+
+void cu_prermdeconfigure(int argc, void **argv) {
+ struct pkginfo *deconf= (struct pkginfo*)argv[0];
+ struct pkginfo *conflictor= (struct pkginfo*)argv[1];
+ struct pkginfo *infavour= (struct pkginfo*)argv[2];
+
+ maintainer_script_installed(deconf,POSTINSTFILE,"post-installation",
+ "abort-deconfigure", "in-favour", infavour->name,
+ versiondescribe(&infavour->available.version,
+ vdew_nonambig),
+ "removing", conflictor->name,
+ versiondescribe(&conflictor->installed.version,
+ vdew_nonambig),
+ (char*)0);
+ deconf->status= stat_installed;
+ modstatdb_note(deconf);
+}
+
+void cu_prerminfavour(int argc, void **argv) {
+ struct pkginfo *conflictor= (struct pkginfo*)argv[0];
+ struct pkginfo *infavour= (struct pkginfo*)argv[1];
+
+ if (cleanup_conflictor_failed++) return;
+ maintainer_script_installed(conflictor,POSTINSTFILE,"post-installation",
+ "abort-remove", "in-favour", infavour->name,
+ versiondescribe(&infavour->available.version,
+ vdew_nonambig),
+ (char*)0);
+ conflictor->status= stat_installed;
+ conflictor->eflag &= ~eflagf_reinstreq;
+ modstatdb_note(conflictor);
+ cleanup_conflictor_failed--;
+}
+
+void cu_preinstverynew(int argc, void **argv) {
+ struct pkginfo *pkg= (struct pkginfo*)argv[0];
+ char *cidir= (char*)argv[1];
+ char *cidirrest= (char*)argv[2];
+
+ if (cleanup_pkg_failed++) return;
+ maintainer_script_new(pkg->name, POSTRMFILE,"post-removal",cidir,cidirrest,
+ "abort-install",(char*)0);
+ pkg->status= stat_notinstalled;
+ pkg->eflag &= ~eflagf_reinstreq;
+ modstatdb_note(pkg);
+ cleanup_pkg_failed--;
+}
+
+void cu_preinstnew(int argc, void **argv) {
+ struct pkginfo *pkg= (struct pkginfo*)argv[0];
+ char *cidir= (char*)argv[1];
+ char *cidirrest= (char*)argv[2];
+
+ if (cleanup_pkg_failed++) return;
+ maintainer_script_new(pkg->name, POSTRMFILE,"post-removal",cidir,cidirrest,
+ "abort-install", versiondescribe(&pkg->installed.version,
+ vdew_nonambig),
+ (char*)0);
+ pkg->status= stat_configfiles;
+ pkg->eflag &= ~eflagf_reinstreq;
+ modstatdb_note(pkg);
+ cleanup_pkg_failed--;
+}
+
+void cu_preinstupgrade(int argc, void **argv) {
+ struct pkginfo *pkg= (struct pkginfo*)argv[0];
+ char *cidir= (char*)argv[1];
+ char *cidirrest= (char*)argv[2];
+ enum pkgstatus *oldstatusp= (enum pkgstatus*)argv[3];
+
+ if (cleanup_pkg_failed++) return;
+ maintainer_script_new(pkg->name, POSTRMFILE,"post-removal",cidir,cidirrest,
+ "abort-upgrade",
+ versiondescribe(&pkg->installed.version,
+ vdew_nonambig),
+ (char*)0);
+ pkg->status= *oldstatusp;
+ pkg->eflag &= ~eflagf_reinstreq;
+ modstatdb_note(pkg);
+ cleanup_pkg_failed--;
+}
+
+void cu_postrmupgrade(int argc, void **argv) {
+ struct pkginfo *pkg= (struct pkginfo*)argv[0];
+
+ if (cleanup_pkg_failed++) return;
+ maintainer_script_installed(pkg,PREINSTFILE,"pre-installation",
+ "abort-upgrade", versiondescribe(&pkg->available.version,
+ vdew_nonambig),
+ (char*)0);
+ cleanup_pkg_failed--;
+}
+
+void cu_prermremove(int argc, void **argv) {
+ struct pkginfo *pkg= (struct pkginfo*)argv[0];
+
+ if (cleanup_pkg_failed++) return;
+ maintainer_script_installed(pkg,POSTINSTFILE,"post-installation",
+ "abort-remove", (char*)0);
+ pkg->status= stat_installed;
+ pkg->eflag &= ~eflagf_reinstreq;
+ modstatdb_note(pkg);
+ cleanup_pkg_failed--;
+}
diff --git a/src/configure.c b/src/configure.c
new file mode 100644
index 000000000..efa7d2002
--- /dev/null
+++ b/src/configure.c
@@ -0,0 +1,663 @@
+/*
+ * dpkg - main program for package management
+ * configure.c - configure packages
+ *
+ * Copyright 1995 Ian Jackson <ian@chiark.greenend.org.uk>
+ * Copyright 1999,2002 Wichert Akkerman <wichert@deephackmode.org>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 dpkg; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <config.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/wait.h>
+
+#include <dpkg.h>
+#include <dpkg-db.h>
+
+#include "filesdb.h"
+#include "main.h"
+
+int conffoptcells[2][2]= { CONFFOPTCELLS };
+
+static void md5hash(struct pkginfo *pkg, char **hashbuf, const char *fn);
+static void copyfileperm(const char* source, const char* target);
+static void showdiff(const char* old, const char* new);
+static void suspend(void);
+static enum conffopt promptconfaction(const char* cfgfile, const char* realold,
+ const char* realnew, int useredited, int distedited,
+ enum conffopt what);
+
+extern struct pipef *status_pipes;
+
+
+void deferred_configure(struct pkginfo *pkg) {
+ /* The algorithm for deciding what to configure first is as follows:
+ * Loop through all packages doing a `try 1' until we've been round
+ * and nothing has been done, then do `try 2' and `try 3' likewise.
+ * The incrementing of `dependtry' is done by process_queue().
+ * Try 1:
+ * Are all dependencies of this package done ? If so, do it.
+ * Are any of the dependencies missing or the wrong version ?
+ * If so, abort (unless --force-depends, in which case defer)
+ * Will we need to configure a package we weren't given as an
+ * argument ? If so, abort - except if --force-configure-any,
+ * in which case we add the package to the argument list.
+ * If none of the above, defer the package.
+ * Try 2:
+ * Find a cycle and break it (see above).
+ * Do as for try 1.
+ * Try 3 (only if --force-depends-version).
+ * Same as for try 2, but don't mind version number in dependencies.
+ * Try 4 (only if --force-depends).
+ * Do anyway.
+ */
+ struct varbuf aemsgs, cdr, cdr2;
+ char *cdr2rest;
+ int ok, r, useredited, distedited;
+ struct conffile *conff;
+ char *currenthash= 0, *newdisthash= 0;
+ struct stat stab;
+ enum conffopt what;
+ static const char *EMPTY_HASH = "-";
+
+ if (pkg->status == stat_notinstalled)
+ ohshit(_("no package named `%s' is installed, cannot configure"),pkg->name);
+ if (pkg->status == stat_installed)
+ ohshit(_("package %.250s is already installed and configured"), pkg->name);
+ if (pkg->status != stat_unpacked && pkg->status != stat_halfconfigured)
+ ohshit(_("package %.250s is not ready for configuration\n"
+ " cannot configure (current status `%.250s')"),
+ pkg->name, statusinfos[pkg->status].name);
+
+ if (dependtry > 1)
+ if (findbreakcycle(pkg))
+ sincenothing= 0;
+
+ varbufinit(&aemsgs);
+ ok= dependencies_ok(pkg,0,&aemsgs);
+ if (ok == 1) {
+ varbuffree(&aemsgs);
+ pkg->clientdata->istobe= itb_installnew;
+ add_to_queue(pkg);
+ return;
+ } else if (ok == 0) {
+ sincenothing= 0;
+ varbufaddc(&aemsgs,0);
+ fprintf(stderr,
+ _("dpkg: dependency problems prevent configuration of %s:\n%s"),
+ pkg->name, aemsgs.buf);
+ varbuffree(&aemsgs);
+ ohshit(_("dependency problems - leaving unconfigured"));
+ } else if (aemsgs.used) {
+ varbufaddc(&aemsgs,0);
+ fprintf(stderr,
+ _("dpkg: %s: dependency problems, but configuring anyway as you request:\n%s"),
+ pkg->name, aemsgs.buf);
+ }
+ varbuffree(&aemsgs);
+ sincenothing= 0;
+
+ if (pkg->eflag & eflagf_reinstreq)
+ forcibleerr(fc_removereinstreq,
+ _("Package is in a very bad inconsistent state - you should\n"
+ " reinstall it before attempting configuration."));
+
+ printf(_("Setting up %s (%s) ...\n"),pkg->name,
+ versiondescribe(&pkg->installed.version,vdew_never));
+
+ if (f_noact) {
+ pkg->status= stat_installed;
+ pkg->clientdata->istobe= itb_normal;
+ return;
+ }
+
+ if (pkg->status == stat_unpacked) {
+ debug(dbg_general,"deferred_configure updating conffiles");
+ /* This will not do at all the right thing with overridden conffiles
+ * or conffiles that are the `target' of an override; all the references
+ * here would be to the `contested' filename, and in any case there'd
+ * only be one hash for both `versions' of the conffile.
+ *
+ * Overriding conffiles is a silly thing to do anyway :-).
+ */
+
+ modstatdb_note(pkg);
+
+ /* On entry, the `new' version of each conffile has been
+ * unpacked as *.dpkg-new, and the `installed' version is
+ * as-yet untouched in `*'. The hash of the `old distributed'
+ * version is in the conffiles data for the package.
+ * If `*.dpkg-new' no longer exists we assume that we've already
+ * processed this one.
+ */
+ varbufinit(&cdr);
+ varbufinit(&cdr2);
+ for (conff= pkg->installed.conffiles; conff; conff= conff->next) {
+ r= conffderef(pkg, &cdr, conff->name);
+ if (r == -1) {
+ conff->hash= EMPTY_HASH;
+ continue;
+ }
+ md5hash(pkg,&currenthash,cdr.buf);
+
+ varbufreset(&cdr2);
+ varbufaddstr(&cdr2,cdr.buf);
+ cdr2.used+=50; varbufaddc(&cdr2,0); cdr2rest= cdr2.buf+strlen(cdr.buf);
+ /* From now on we can just strcpy(cdr2rest,extension); */
+
+ strcpy(cdr2rest,DPKGNEWEXT);
+ /* If the .dpkg-new file is no longer there, ignore this one. */
+ if (lstat(cdr2.buf,&stab)) {
+ if (errno == ENOENT) continue;
+ ohshite(_("unable to stat new dist conffile `%.250s'"),cdr2.buf);
+ }
+ md5hash(pkg,&newdisthash,cdr2.buf);
+
+ /* Copy the permissions from the installed version to the new
+ * distributed version.
+ */
+ if (!stat(cdr.buf,&stab))
+ copyfileperm(cdr.buf, cdr2.buf);
+ else if (errno != ENOENT)
+ ohshite(_("unable to stat current installed conffile `%.250s'"),cdr.buf);
+
+ /* Select what the do */
+ if (!strcmp(currenthash,newdisthash)) {
+ /* They're both the same so there's no point asking silly questions. */
+ useredited= -1;
+ distedited= -1;
+ what= cfo_identical;
+ } else if (!strcmp(currenthash,NONEXISTENTFLAG) && fc_conff_miss) {
+ fprintf(stderr, _("\nConfiguration file `%s', does not exist on system.\n"
+ "Installing new config file as you request.\n"), conff->name);
+ what= cfo_newconff;
+ useredited= -1;
+ distedited= -1;
+ } else if (!strcmp(conff->hash,NEWCONFFILEFLAG)) {
+ if (!strcmp(currenthash,NONEXISTENTFLAG)) {
+ what= cfo_newconff;
+ useredited= -1;
+ distedited= -1;
+ } else {
+ useredited= 1;
+ distedited= 1;
+ what= conffoptcells[useredited][distedited] | cfof_isnew;
+ }
+ } else {
+ useredited= strcmp(conff->hash,currenthash) != 0;
+ distedited= strcmp(conff->hash,newdisthash) != 0;
+ what= conffoptcells[useredited][distedited];
+ }
+
+ debug(dbg_conff,
+ "deferred_configure `%s' (= `%s') useredited=%d distedited=%d what=%o",
+ conff->name, cdr.buf, useredited, distedited, what);
+
+ what=promptconfaction(conff->name, cdr.buf, cdr2.buf, useredited, distedited, what);
+
+ switch (what & ~cfof_isnew) {
+ case cfo_keep | cfof_backup:
+ strcpy(cdr2rest,DPKGOLDEXT);
+ if (unlink(cdr2.buf) && errno != ENOENT)
+ fprintf(stderr,
+ _("dpkg: %s: warning - failed to remove old backup `%.250s': %s\n"),
+ pkg->name, cdr2.buf, strerror(errno));
+ cdr.used--;
+ varbufaddstr(&cdr,DPKGDISTEXT);
+ varbufaddc(&cdr,0);
+ strcpy(cdr2rest,DPKGNEWEXT);
+ if (rename(cdr2.buf,cdr.buf))
+ fprintf(stderr,
+ _("dpkg: %s: warning - failed to rename `%.250s' to `%.250s': %s\n"),
+ pkg->name, cdr2.buf, cdr.buf, strerror(errno));
+ break;
+
+ case cfo_keep:
+ strcpy(cdr2rest,DPKGNEWEXT);
+ if (unlink(cdr2.buf))
+ fprintf(stderr,
+ _("dpkg: %s: warning - failed to remove `%.250s': %s\n"),
+ pkg->name, cdr2.buf, strerror(errno));
+ break;
+
+ case cfo_install | cfof_backup:
+ strcpy(cdr2rest,DPKGDISTEXT);
+ if (unlink(cdr2.buf) && errno != ENOENT)
+ fprintf(stderr,
+ _("dpkg: %s: warning - failed to remove old distrib version `%.250s': %s\n"),
+ pkg->name, cdr2.buf, strerror(errno));
+ strcpy(cdr2rest,DPKGOLDEXT);
+ if (unlink(cdr2.buf) && errno != ENOENT)
+ fprintf(stderr,
+ _("dpkg: %s: warning - failed to remove `%.250s' (before overwrite): %s\n"),
+ pkg->name, cdr2.buf, strerror(errno));
+ if (link(cdr.buf,cdr2.buf))
+ fprintf(stderr,
+ _("dpkg: %s: warning - failed to link `%.250s' to `%.250s': %s\n"),
+ pkg->name, cdr.buf, cdr2.buf, strerror(errno));
+ /* fall through */
+ case cfo_install:
+ printf(_("Installing new version of config file %s ...\n"),conff->name);
+ case cfo_newconff:
+ strcpy(cdr2rest,DPKGNEWEXT);
+ if (rename(cdr2.buf,cdr.buf))
+ ohshite(_("unable to install `%.250s' as `%.250s'"),cdr2.buf,cdr.buf);
+ break;
+
+ default:
+ internerr("unknown what");
+ }
+
+ conff->hash= nfstrsave(newdisthash);
+ modstatdb_note(pkg);
+ free(newdisthash);
+ free(currenthash);
+
+ } /* for (conff= ... */
+ varbuffree(&cdr);
+ varbuffree(&cdr2);
+
+ pkg->status= stat_halfconfigured;
+ }
+
+ assert(pkg->status == stat_halfconfigured);
+
+ modstatdb_note(pkg);
+
+ if (maintainer_script_installed(pkg, POSTINSTFILE, "post-installation",
+ "configure",
+ informativeversion(&pkg->configversion)
+ ? versiondescribe(&pkg->configversion,
+ vdew_nonambig)
+ : "",
+ (char*)0))
+ putchar('\n');
+
+ pkg->status= stat_installed;
+ pkg->eflag= eflagv_ok;
+ modstatdb_note(pkg);
+}
+
+
+
+/* Dereference a file by following all possibly used symlinks.
+ * Returns 0 if everything went ok, -1 otherwise.
+ */
+int conffderef(struct pkginfo *pkg, struct varbuf *result, const char *in) {
+ static char* linkreadbuf = 0;
+ static int linkreadbufsize = 0;
+ struct stat stab;
+ int r, need;
+ int loopprotect;
+
+ varbufreset(result);
+ varbufaddstr(result,instdir);
+ if (*in != '/') varbufaddc(result,'/');
+ varbufaddstr(result,in);
+ varbufaddc(result,0);
+
+ loopprotect= 0;
+
+ for (;;) {
+ debug(dbg_conffdetail,"conffderef in=`%s' current working=`%s'", in, result->buf);
+ if (lstat(result->buf,&stab)) {
+ if (errno != ENOENT)
+ fprintf(stderr, _("dpkg: %s: warning - unable to stat config file `%s'\n"
+ " (= `%s'): %s\n"),
+ pkg->name, in, result->buf, strerror(errno));
+ debug(dbg_conffdetail,"conffderef nonexistent");
+ return 0;
+ } else if (S_ISREG(stab.st_mode)) {
+ debug(dbg_conff,"conffderef in=`%s' result=`%s'", in, result->buf);
+ return 0;
+ } else if (S_ISLNK(stab.st_mode)) {
+ debug(dbg_conffdetail,"conffderef symlink loopprotect=%d",loopprotect);
+ if (loopprotect++ >= 25) {
+ fprintf(stderr, _("dpkg: %s: warning - config file `%s' is a circular link\n"
+ " (= `%s')\n"), pkg->name, in, result->buf);
+ return -1;
+ }
+ need= 255;
+ for (;;) {
+ if (need > linkreadbufsize) {
+ linkreadbuf= m_realloc(linkreadbuf,need);
+ linkreadbufsize= need;
+ debug(dbg_conffdetail,"conffderef readlink realloc(%d)=%p",need,linkreadbuf);
+ }
+ r= readlink(result->buf,linkreadbuf,linkreadbufsize-1);
+ if (r < 0) {
+ fprintf(stderr, _("dpkg: %s: warning - unable to readlink conffile `%s'\n"
+ " (= `%s'): %s\n"),
+ pkg->name, in, result->buf, strerror(errno));
+ return -1;
+ }
+ debug(dbg_conffdetail,"conffderef readlink gave %d, `%.*s'",
+ r, r>0 ? r : 0, linkreadbuf);
+ if (r < linkreadbufsize-1) break;
+ need= r<<2;
+ }
+ linkreadbuf[r]= 0;
+ if (linkreadbuf[0] == '/') {
+ varbufreset(result);
+ varbufaddstr(result,instdir);
+ debug(dbg_conffdetail,"conffderef readlink absolute");
+ } else {
+ for (r=result->used-2; r>0 && result->buf[r] != '/'; r--)
+ ;
+ if (r < 0) {
+ fprintf(stderr,
+ _("dpkg: %s: warning - conffile `%.250s' resolves to degenerate filename\n"
+ " (`%s' is a symlink to `%s')\n"),
+ pkg->name, in, result->buf, linkreadbuf);
+ return -1;
+ }
+ if (result->buf[r] == '/') r++;
+ result->used= r;
+ debug(dbg_conffdetail,"conffderef readlink relative to `%.*s'",
+ (int)result->used, result->buf);
+ }
+ varbufaddstr(result,linkreadbuf);
+ varbufaddc(result,0);
+ } else {
+ fprintf(stderr, _("dpkg: %s: warning - conffile `%.250s' is not a plain"
+ " file or symlink (= `%s')\n"),
+ pkg->name, in, result->buf);
+ return -1;
+ }
+ }
+}
+
+/* Generate a MD5 hash for fn and store it in *hashbuf. Memory is allocated
+ * by this function and should be freed manually.
+ */
+static void md5hash(struct pkginfo *pkg, char **hashbuf, const char *fn) {
+ static int fd;
+
+ fd=open(fn,O_RDONLY);
+
+ if (fd>=0) {
+ push_cleanup(cu_closefd,ehflag_bombout, 0,0, 1,&fd);
+ fd_md5(fd, hashbuf, -1, _("md5hash"));
+ pop_cleanup(ehflag_normaltidy); /* fd= open(cdr.buf) */
+ close(fd);
+ } else if (errno==ENOENT) {
+ *hashbuf= strdup(NONEXISTENTFLAG);
+ } else {
+ fprintf(stderr, _("dpkg: %s: warning - unable to open conffile %s for hash: %s\n"),
+ pkg->name, fn, strerror(errno));
+ *hashbuf= strdup("-");
+ }
+}
+
+/* Copy file ownership and permissions from one file to another
+ */
+static void copyfileperm(const char* source, const char* target) {
+ struct stat stab;
+
+ if (stat(source, &stab)==-1) {
+ if (errno==ENOENT)
+ return;
+ ohshite(_("unable to stat current installed conffile `%.250s'"), source);
+ }
+
+ if (chown(target, stab.st_uid, stab.st_gid)==-1)
+ ohshite(_("unable to change ownership of new dist conffile `%.250s'"), target);
+
+ if (chmod(target, (stab.st_mode & 07777))==-1)
+ ohshite(_("unable to set mode of new dist conffile `%.250s'"), target);
+}
+
+
+
+
+/* Show a diff between two files
+ */
+static void showdiff(const char* old, const char* new) {
+ int pid;
+ int r;
+ int status;
+
+ if (!(pid=m_fork())) {
+ /* Child process */
+ const char* p; /* pager */
+ const char* s; /* shell */
+ char cmdbuf[1024]; /* command to run */
+
+ p=getenv(PAGERENV);
+ if (!p || !*p)
+ p=DEFAULTPAGER;
+
+ sprintf(cmdbuf, DIFF " -Nu %.250s %.250s | %.250s", old, new, p);
+
+ s=getenv(SHELLENV);
+ if (!s || !*s)
+ s=DEFAULTSHELL;
+
+ execlp(s,s,"-c", cmdbuf, NULL);
+ ohshite(_("failed to run %s (%.250s)"), DIFF, cmdbuf);
+ }
+
+ /* Parent process */
+ while (((r=waitpid(pid,&status,0))==-1) && (errno==EINTR))
+ ;
+
+ if (r!=pid) {
+ onerr_abort++;
+ ohshite(_("wait for shell failed"));
+ }
+}
+
+
+/* Suspend dpkg temporarily
+ */
+static void suspend(void) {
+ const char* s;
+ int pid;
+
+ s= getenv(NOJOBCTRLSTOPENV);
+ if (s && *s) {
+ /* Do not job control to suspend but fork and start a new shell
+ * instead.
+ */
+
+ int status; /* waitpid status */
+ int r; /* waitpid result */
+
+ fputs(_("Type `exit' when you're done.\n"), stderr);
+
+ if (!(pid= m_fork())) {
+ /* Child process */
+ s= getenv(SHELLENV);
+ if (!s || !*s)
+ s=DEFAULTSHELL;
+
+ execlp(s,s,"-i",(char*)0);
+ ohshite(_("failed to exec shell (%.250s)"),s);
+ }
+
+ /* Parent process */
+ while (((r=waitpid(pid,&status,0))==-1) && (errno==EINTR))
+ ;
+
+ if (r!=pid) {
+ onerr_abort++;
+ ohshite(_("wait for shell failed"));
+ }
+ } else {
+ fputs(_("Don't forget to foreground (`fg') this "
+ "process when you're done !\n"), stderr);
+ kill(-getpgid(0),SIGTSTP);
+ }
+}
+
+
+/* Select what to do with a configuration file.
+ */
+static enum conffopt promptconfaction(const char* cfgfile, const char* realold,
+ const char* realnew, int useredited, int distedited,
+ enum conffopt what) {
+ const char *s;
+ int c, cc;
+
+ if (!(what&cfof_prompt))
+ return what;
+
+ /* if there is a status pipe, send conffile-prompt there */
+ if (status_pipes) {
+ static struct varbuf *status= NULL;
+ struct pipef *pipef= status_pipes;
+ int r;
+ if (status == NULL) {
+ status = nfmalloc(sizeof(struct varbuf));
+ varbufinit(status);
+ } else
+ varbufreset(status);
+
+ r= varbufprintf(status, "status: %s : %s : '%s' '%s' %i %i \n",
+ cfgfile, "conffile-prompt",
+ realold, realnew, useredited, distedited);
+ while (pipef) {
+ write(pipef->fd, status->buf, r);
+ pipef= pipef->next;
+ }
+ }
+
+
+ do {
+ fprintf(stderr, _("\nConfiguration file `%s'"), cfgfile);
+ if (strcmp(cfgfile, realold))
+ fprintf(stderr,_(" (actually `%s')"), realold);
+
+ if (what & cfof_isnew) {
+ fprintf(stderr,
+ _("\n"
+ " ==> File on system created by you or by a script.\n"
+ " ==> File also in package provided by package maintainer.\n"));
+ } else {
+ fprintf(stderr, useredited ?
+ _("\n ==> Modified (by you or by a script) since installation.\n") :
+ _("\n Not modified since installation.\n"));
+
+ fprintf(stderr, distedited ?
+ _(" ==> Package distributor has shipped an updated version.\n") :
+ _(" Version in package is the same as at last installation.\n"));
+ }
+
+ /* No --force-confdef but a forcible situtation */
+ /* TODO: check if this condition can not be simplified to just !fc_conff_def */
+ if (!(fc_conff_def && (what&(cfof_install|cfof_keep)))) {
+ if (fc_conff_new) {
+ fprintf(stderr, _(" ==> Using new file as you requested.\n"));
+ cc = 'y';
+ break;
+ } else if (fc_conff_old) {
+ fprintf(stderr, _(" ==> Using current old file as you requested.\n"));
+ cc = 'n';
+ break;
+ }
+ }
+
+
+ /* Force the default action (if there is one */
+ if (fc_conff_def) {
+ if (what&cfof_keep) {
+ fprintf(stderr, _(" ==> Keeping old config file as default.\n"));
+ cc = 'n';
+ break;
+ } else if (what&cfof_install) {
+ fprintf(stderr, _(" ==> Using new config file as default.\n"));
+ cc = 'y';
+ break;
+ }
+ }
+
+
+ fprintf(stderr,
+ _(" What would you like to do about it ? Your options are:\n"
+ " Y or I : install the package maintainer's version\n"
+ " N or O : keep your currently-installed version\n"
+ " D : show the differences between the versions\n"
+ " Z : background this process to examine the situation\n"));
+
+ if (what & cfof_keep)
+ fprintf(stderr, _(" The default action is to keep your current version.\n"));
+ else if (what & cfof_install)
+ fprintf(stderr, _(" The default action is to install the new version.\n"));
+
+ s= strrchr(cfgfile,'/');
+ if (!s || !*++s) s= cfgfile;
+ fprintf(stderr, "*** %s (Y/I/N/O/D/Z) %s ? ",
+ s,
+ (what & cfof_keep) ? _("[default=N]") :
+ (what & cfof_install) ? _("[default=Y]") : _("[no default]"));
+
+ if (ferror(stderr))
+ ohshite(_("error writing to stderr, discovered before conffile prompt"));
+
+ cc= 0;
+ while ((c= getchar()) != EOF && c != '\n')
+ if (!isspace(c) && !cc) cc= tolower(c);
+
+ if (c == EOF) {
+ if (ferror(stdin)) ohshite(_("read error on stdin at conffile prompt"));
+ ohshit(_("EOF on stdin at conffile prompt"));
+ }
+
+ if (!cc) {
+ if (what & cfof_keep) { cc= 'n'; break; }
+ else if (what & cfof_install) { cc= 'y'; break; }
+ }
+
+ /* fixme: say something if silently not install */
+ if (cc == 'd')
+ showdiff(realold, realnew);
+
+ if (cc == 'z')
+ suspend();
+
+ } while (!strchr("yino",cc));
+
+
+ switch (cc) {
+ case 'i':
+ case 'y':
+ what=cfof_install|cfof_backup;
+ break;
+
+ case 'n':
+ case 'o':
+ what=cfof_keep|cfof_backup;
+ break;
+
+ default:
+ internerr("unknown response");
+ }
+
+ return what;
+}
diff --git a/src/depcon.c b/src/depcon.c
new file mode 100644
index 000000000..0a725d104
--- /dev/null
+++ b/src/depcon.c
@@ -0,0 +1,464 @@
+/*
+ * dpkg - main program for package management
+ * depcon.c - dependency and conflict checking
+ *
+ * Copyright (C) 1994,1995 Ian Jackson <ian@chiark.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 dpkg; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <config.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <assert.h>
+
+#include <dpkg.h>
+#include <dpkg-db.h>
+
+#include "main.h"
+
+struct cyclesofarlink {
+ struct cyclesofarlink *back;
+ struct pkginfo *pkg;
+ struct deppossi *possi;
+};
+
+static int findbreakcyclerecursive(struct pkginfo *pkg, struct cyclesofarlink *sofar);
+
+static int foundcyclebroken(struct cyclesofarlink *thislink,
+ struct cyclesofarlink *sofar,
+ struct pkginfo *dependedon,
+ struct deppossi *possi) {
+ struct cyclesofarlink *sol;
+ const char *postinstfilename;
+ struct stat stab;
+
+ if(!possi) return 0;
+ /* We're investigating the dependency `possi' to see if it
+ * is part of a loop. To this end we look to see whether the
+ * depended-on package is already one of the packages whose
+ * dependencies we're searching.
+ */
+ for (sol=sofar; sol && sol->pkg != dependedon; sol=sol->back);
+
+ /* If not, we do a recursive search on it to see what we find. */
+ if (!sol) return findbreakcyclerecursive(possi->ed,thislink);
+
+ debug(dbg_depcon,"found cycle");
+ /* Right, we now break one of the links. We prefer to break
+ * a dependency of a package without a postinst script, as
+ * this is a null operation. If this is not possible we break
+ * the other link in the recursive calling tree which mentions
+ * this package (this being the first package involved in the
+ * cycle). It doesn't particularly matter which we pick, but if
+ * we break the earliest dependency we came across we may be
+ * able to do something straight away when findbreakcycle returns.
+ */
+ sofar= thislink;
+ for (sol= sofar; !(sol != sofar && sol->pkg == dependedon); sol=sol->back) {
+ postinstfilename= pkgadminfile(sol->pkg,POSTINSTFILE);
+ if (lstat(postinstfilename,&stab)) {
+ if (errno == ENOENT) break;
+ ohshite(_("unable to check for existence of `%.250s'"),postinstfilename);
+ }
+ }
+ /* Now we have either a package with no postinst, or the other
+ * occurrence of the current package in the list.
+ */
+ sol->possi->cyclebreak= 1;
+ debug(dbg_depcon,"cycle broken at %s -> %s\n",
+ sol->possi->up->up->name, sol->possi->ed->name);
+ return 1;
+}
+
+static int findbreakcyclerecursive(struct pkginfo *pkg, struct cyclesofarlink *sofar) {
+ /* Cycle breaking works recursively down the package dependency
+ * tree. `sofar' is the list of packages we've descended down
+ * already - if we encounter any of its packages again in a
+ * dependency we have found a cycle.
+ */
+ struct cyclesofarlink thislink, *sol;
+ struct dependency *dep;
+ struct deppossi *possi, *providelink;
+ struct pkginfo *provider;
+
+ if (pkg->color == black)
+ return 0;
+ pkg->color = gray;
+
+ if (f_debug & dbg_depcondetail) {
+ fprintf(stderr,"D0%05o: findbreakcyclerecursive %s ",dbg_depcondetail,pkg->name);
+ for (sol=sofar; sol; sol=sol->back) fprintf(stderr," <- %s",sol->pkg->name);
+ fprintf(stderr,"\n");
+ }
+ thislink.pkg= pkg;
+ thislink.back= sofar;
+ thislink.possi= 0;
+ for (dep= pkg->installed.depends; dep; dep= dep->next) {
+ if (dep->type != dep_depends && dep->type != dep_predepends) continue;
+ for (possi= dep->list; possi; possi= possi->next) {
+ /* Don't find the same cycles again. */
+ if (possi->cyclebreak) continue;
+ thislink.possi= possi;
+ if (foundcyclebroken(&thislink,sofar,possi->ed,possi)) return 1;
+ /* Right, now we try all the providers ... */
+ for (providelink= possi->ed->installed.depended;
+ providelink;
+ providelink= providelink->nextrev) {
+ if (providelink->up->type != dep_provides) continue;
+ provider= providelink->up->up;
+ if (provider->clientdata->istobe == itb_normal) continue;
+ /* We don't break things at `provides' links, so `possi' is
+ * still the one we use.
+ */
+ if (foundcyclebroken(&thislink,sofar,provider,possi)) return 1;
+ if (foundcyclebroken(&thislink,sofar,possi->ed,provider->installed.depended)) return 1;
+ }
+ }
+ }
+ /* Nope, we didn't find a cycle to break. */
+ pkg->color = black;
+ return 0;
+}
+
+int findbreakcycle(struct pkginfo *pkg) {
+ struct pkgiterator *iter;
+ struct pkginfo *tpkg;
+
+ /* Clear the visited flag of all packages before we traverse them. */
+ for (iter = iterpkgstart(); (tpkg=iterpkgnext(iter)); ) {
+ tpkg->color = white;
+ }
+
+ return findbreakcyclerecursive(pkg, 0);
+}
+
+void describedepcon(struct varbuf *addto, struct dependency *dep) {
+ varbufaddstr(addto,dep->up->name);
+ switch (dep->type) {
+ case dep_depends: varbufaddstr(addto, _(" depends on ")); break;
+ case dep_predepends: varbufaddstr(addto, _(" pre-depends on ")); break;
+ case dep_recommends: varbufaddstr(addto, _(" recommends ")); break;
+ case dep_conflicts: varbufaddstr(addto, _(" conflicts with ")); break;
+ case dep_suggests: varbufaddstr(addto, _(" suggests ")); break;
+ case dep_enhances: varbufaddstr(addto, _(" enhances ")); break;
+ default: internerr("unknown deptype");
+ }
+ varbufdependency(addto, dep);
+}
+
+int depisok(struct dependency *dep, struct varbuf *whynot,
+ struct pkginfo **canfixbyremove, int allowunconfigd) {
+ /* *whynot must already have been initialised; it need not be
+ * empty though - it will be reset before use.
+ * If depisok returns 0 for `not OK' it will contain a description,
+ * newline-terminated BUT NOT NULL-TERMINATED, of the reason.
+ * If depisok returns 1 it will contain garbage.
+ * allowunconfigd should be non-zero during the `Pre-Depends' checking
+ * before a package is unpacked, when it is sufficient for the package
+ * to be unpacked provided that both the unpacked and previously-configured
+ * versions are acceptable.
+ */
+ struct deppossi *possi;
+ struct deppossi *provider;
+ int nconflicts;
+
+ /* Use this buffer so that when internationalisation comes along we
+ * don't have to rewrite the code completely, only redo the sprintf strings
+ * (assuming we have the fancy argument-number-specifiers).
+ * Allow 250x3 for package names, versions, &c, + 250 for ourselves.
+ */
+ char linebuf[1024];
+
+ assert(dep->type == dep_depends || dep->type == dep_predepends ||
+ dep->type == dep_conflicts || dep->type == dep_recommends ||
+ dep->type == dep_suggests || dep->type == dep_enhances );
+
+ /* The dependency is always OK if we're trying to remove the depend*ing*
+ * package.
+ */
+ switch (dep->up->clientdata->istobe) {
+ case itb_remove: case itb_deconfigure:
+ return 1;
+ case itb_normal:
+ /* Only `installed' packages can be make dependency problems */
+ switch (dep->up->status) {
+ case stat_installed:
+ break;
+ case stat_notinstalled: case stat_configfiles: case stat_halfinstalled:
+ case stat_halfconfigured: case stat_unpacked:
+ return 1;
+ default:
+ internerr("unknown status depending");
+ }
+ break;
+ case itb_installnew: case itb_preinstall:
+ break;
+ default:
+ internerr("unknown istobe depending");
+ }
+
+ /* Describe the dependency, in case we have to moan about it. */
+ varbufreset(whynot);
+ varbufaddc(whynot, ' ');
+ describedepcon(whynot, dep);
+ varbufaddc(whynot,'\n');
+
+ /* TODO: check dep_enhances as well (WTA) */
+ if (dep->type == dep_depends || dep->type == dep_predepends ||
+ dep->type == dep_recommends || dep->type == dep_suggests ) {
+
+ /* Go through the alternatives. As soon as we find one that
+ * we like, we return `1' straight away. Otherwise, when we get to
+ * the end we'll have accumulated all the reasons in whynot and
+ * can return `0'.
+ */
+
+ for (possi= dep->list; possi; possi= possi->next) {
+ switch (possi->ed->clientdata->istobe) {
+ case itb_remove:
+ sprintf(linebuf,_(" %.250s is to be removed.\n"),possi->ed->name);
+ break;
+ case itb_deconfigure:
+ sprintf(linebuf,_(" %.250s is to be deconfigured.\n"),possi->ed->name);
+ break;
+ case itb_installnew:
+ if (versionsatisfied(&possi->ed->available,possi)) return 1;
+ sprintf(linebuf,_(" %.250s is to be installed, but is version %.250s.\n"),
+ possi->ed->name,
+ versiondescribe(&possi->ed->available.version,vdew_nonambig));
+ break;
+ case itb_normal: case itb_preinstall:
+ switch (possi->ed->status) {
+ case stat_installed:
+ if (versionsatisfied(&possi->ed->installed,possi)) return 1;
+ sprintf(linebuf,_(" %.250s is installed, but is version %.250s.\n"),
+ possi->ed->name,
+ versiondescribe(&possi->ed->installed.version,vdew_nonambig));
+ break;
+ case stat_notinstalled:
+ /* Don't say anything about this yet - it might be a virtual package.
+ * Later on, if nothing has put anything in linebuf, we know that it
+ * isn't and issue a diagnostic then.
+ */
+ *linebuf= 0;
+ break;
+ case stat_unpacked:
+ case stat_halfconfigured:
+ if (allowunconfigd) {
+ if (!informativeversion(&possi->ed->configversion)) {
+ sprintf(linebuf, _(" %.250s is unpacked, but has never been configured.\n"),
+ possi->ed->name);
+ break;
+ } else if (!versionsatisfied(&possi->ed->installed, possi)) {
+ sprintf(linebuf, _(" %.250s is unpacked, but is version %.250s.\n"),
+ possi->ed->name,
+ versiondescribe(&possi->ed->available.version,vdew_nonambig));
+ break;
+ } else if (!versionsatisfied3(&possi->ed->configversion,
+ &possi->version,possi->verrel)) {
+ sprintf(linebuf, _(" %.250s latest configured version is %.250s.\n"),
+ possi->ed->name,
+ versiondescribe(&possi->ed->configversion,vdew_nonambig));
+ break;
+ } else {
+ return 1;
+ }
+ }
+ default:
+ sprintf(linebuf, _(" %.250s is %s.\n"),
+ possi->ed->name, gettext(statusstrings[possi->ed->status]));
+ break;
+ }
+ break;
+ default:
+ internerr("unknown istobe depended");
+ }
+ varbufaddstr(whynot, linebuf);
+
+ /* If there was no version specified we try looking for Providers. */
+ if (possi->verrel == dvr_none) {
+
+ /* See if the package we're about to install Provides it. */
+ for (provider= possi->ed->available.depended;
+ provider;
+ provider= provider->nextrev) {
+ if (provider->up->type != dep_provides) continue;
+ if (provider->up->up->clientdata->istobe == itb_installnew) return 1;
+ }
+
+ /* Now look at the packages already on the system. */
+ for (provider= possi->ed->installed.depended;
+ provider;
+ provider= provider->nextrev) {
+ if (provider->up->type != dep_provides) continue;
+
+ switch (provider->up->up->clientdata->istobe) {
+ case itb_installnew:
+ /* Don't pay any attention to the Provides field of the
+ * currently-installed version of the package we're trying
+ * to install. We dealt with that by using the available
+ * information above.
+ */
+ continue;
+ case itb_remove:
+ sprintf(linebuf, _(" %.250s provides %.250s but is to be removed.\n"),
+ provider->up->up->name, possi->ed->name);
+ break;
+ case itb_deconfigure:
+ sprintf(linebuf, _(" %.250s provides %.250s but is to be deconfigured.\n"),
+ provider->up->up->name, possi->ed->name);
+ break;
+ case itb_normal: case itb_preinstall:
+ if (provider->up->up->status == stat_installed) return 1;
+ sprintf(linebuf, _(" %.250s provides %.250s but is %s.\n"),
+ provider->up->up->name, possi->ed->name,
+ gettext(statusstrings[provider->up->up->status]));
+ break;
+ default:
+ internerr("unknown istobe provider");
+ }
+ varbufaddstr(whynot, linebuf);
+ }
+
+ if (!*linebuf) {
+ /* If the package wasn't installed at all, and we haven't said
+ * yet why this isn't satisfied, we should say so now.
+ */
+ sprintf(linebuf, _(" %.250s is not installed.\n"), possi->ed->name);
+ varbufaddstr(whynot, linebuf);
+ }
+ }
+ }
+
+ if (canfixbyremove) *canfixbyremove= 0;
+ return 0;
+
+ } else {
+
+ /* It's a conflict. There's only one main alternative,
+ * but we also have to consider Providers. We return `0' as soon
+ * as we find something that matches the conflict, and only describe
+ * it then. If we get to the end without finding anything we return `1'.
+ */
+
+ possi= dep->list;
+ nconflicts= 0;
+
+ if (possi->ed != possi->up->up) {
+ /* If the package conflicts with itself it must mean that it conflicts
+ * with other packages which provide the same virtual name. We therefore
+ * don't look at the real package and go on to the virtual ones.
+ */
+
+ switch (possi->ed->clientdata->istobe) {
+ case itb_remove:
+ break;
+ case itb_installnew:
+ if (!versionsatisfied(&possi->ed->available, possi)) break;
+ sprintf(linebuf, _(" %.250s (version %.250s) is to be installed.\n"),
+ possi->ed->name,
+ versiondescribe(&possi->ed->available.version,vdew_nonambig));
+ varbufaddstr(whynot, linebuf);
+ if (!canfixbyremove) return 0;
+ nconflicts++;
+ *canfixbyremove= possi->ed;
+ break;
+ case itb_normal: case itb_deconfigure: case itb_preinstall:
+ switch (possi->ed->status) {
+ case stat_notinstalled: case stat_configfiles:
+ break;
+ default:
+ if (!versionsatisfied(&possi->ed->installed, possi)) break;
+ sprintf(linebuf, _(" %.250s (version %.250s) is %s.\n"),
+ possi->ed->name,
+ versiondescribe(&possi->ed->installed.version,vdew_nonambig),
+ gettext(statusstrings[possi->ed->status]));
+ varbufaddstr(whynot, linebuf);
+ if (!canfixbyremove) return 0;
+ nconflicts++;
+ *canfixbyremove= possi->ed;
+ }
+ break;
+ default:
+ internerr("unknown istobe conflict");
+ }
+ }
+
+ /* If there was no version specified we try looking for Providers. */
+ if (possi->verrel == dvr_none) {
+
+ /* See if the package we're about to install Provides it. */
+ for (provider= possi->ed->available.depended;
+ provider;
+ provider= provider->nextrev) {
+ if (provider->up->type != dep_provides) continue;
+ if (provider->up->up->clientdata->istobe != itb_installnew) continue;
+ if (provider->up->up == dep->up) continue; /* conflicts and provides the same */
+ sprintf(linebuf, _(" %.250s provides %.250s and is to be installed.\n"),
+ provider->up->up->name, possi->ed->name);
+ varbufaddstr(whynot, linebuf);
+ /* We can't remove the one we're about to install: */
+ if (canfixbyremove) *canfixbyremove= 0;
+ return 0;
+ }
+
+ /* Now look at the packages already on the system. */
+ for (provider= possi->ed->installed.depended;
+ provider;
+ provider= provider->nextrev) {
+ if (provider->up->type != dep_provides) continue;
+
+ if (provider->up->up == dep->up) continue; /* conflicts and provides the same */
+
+ switch (provider->up->up->clientdata->istobe) {
+ case itb_installnew:
+ /* Don't pay any attention to the Provides field of the
+ * currently-installed version of the package we're trying
+ * to install. We dealt with that package by using the
+ * available information above.
+ */
+ continue;
+ case itb_remove:
+ continue;
+ case itb_normal: case itb_deconfigure: case itb_preinstall:
+ switch (provider->up->up->status) {
+ case stat_notinstalled: case stat_configfiles:
+ continue;
+ default:
+ sprintf(linebuf, _(" %.250s provides %.250s and is %s.\n"),
+ provider->up->up->name, possi->ed->name,
+ gettext(statusstrings[provider->up->up->status]));
+ varbufaddstr(whynot, linebuf);
+ if (!canfixbyremove) return 0;
+ nconflicts++;
+ *canfixbyremove= provider->up->up;
+ break;
+ }
+ break;
+ default:
+ internerr("unknown istobe conflict provider");
+ }
+ }
+ }
+
+ if (!nconflicts) return 1;
+ if (nconflicts>1) *canfixbyremove= 0;
+ return 0;
+
+ } /* if (dependency) {...} else {...} */
+}
diff --git a/src/enquiry.c b/src/enquiry.c
new file mode 100644
index 000000000..247394858
--- /dev/null
+++ b/src/enquiry.c
@@ -0,0 +1,459 @@
+/*
+ * dpkg - main program for package management
+ * enquiry.c - status enquiry and listing options
+ *
+ * Copyright (C) 1995,1996 Ian Jackson <ian@chiark.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 dpkg; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* fixme: per-package audit */
+#include <config.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fnmatch.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/termios.h>
+#include <fcntl.h>
+
+#include <dpkg.h>
+#include <dpkg-db.h>
+#include <myopt.h>
+
+#include "filesdb.h"
+#include "main.h"
+
+int pkglistqsortcmp(const void *a, const void *b) {
+ const struct pkginfo *pa= *(const struct pkginfo**)a;
+ const struct pkginfo *pb= *(const struct pkginfo**)b;
+ return strcmp(pa->name,pb->name);
+}
+
+static void limiteddescription(struct pkginfo *pkg, int maxl,
+ const char **pdesc_r, int *l_r) {
+ const char *pdesc, *p;
+ int l;
+
+ pdesc= pkg->installed.valid ? pkg->installed.description : 0;
+ if (!pdesc) pdesc= _("(no description available)");
+ p= strchr(pdesc,'\n');
+ if (!p) p= pdesc+strlen(pdesc);
+ l= (p - pdesc > maxl) ? maxl : (int)(p - pdesc);
+ *pdesc_r=pdesc; *l_r=l;
+}
+
+struct badstatinfo {
+ int (*yesno)(struct pkginfo*, const struct badstatinfo *bsi);
+ int val;
+ const char *explanation;
+};
+
+static int bsyn_reinstreq(struct pkginfo *pkg, const struct badstatinfo *bsi) {
+ return pkg->eflag &= eflagf_reinstreq;
+}
+
+static int bsyn_status(struct pkginfo *pkg, const struct badstatinfo *bsi) {
+ if (pkg->eflag &= eflagf_reinstreq) return 0;
+ return (int)pkg->status == bsi->val;
+}
+
+static const struct badstatinfo badstatinfos[]= {
+ {
+ bsyn_reinstreq, 0,
+ N_("The following packages are in a mess due to serious problems during\n"
+ "installation. They must be reinstalled for them (and any packages\n"
+ "that depend on them) to function properly:\n")
+ }, {
+ bsyn_status, stat_unpacked,
+ N_("The following packages have been unpacked but not yet configured.\n"
+ "They must be configured using dpkg --configure or the configure\n"
+ "menu option in dselect for them to work:\n")
+ }, {
+ bsyn_status, stat_halfconfigured,
+ N_("The following packages are only half configured, probably due to problems\n"
+ "configuring them the first time. The configuration should be retried using\n"
+ "dpkg --configure <package> or the configure menu option in dselect:\n")
+ }, {
+ bsyn_status, stat_halfinstalled,
+ N_("The following packages are only half installed, due to problems during\n"
+ "installation. The installation can probably be completed by retrying it;\n"
+ "the packages can be removed using dselect or dpkg --remove:\n")
+ }, {
+ 0
+ }
+};
+
+static void describebriefly(struct pkginfo *pkg) {
+ int maxl, l;
+ const char *pdesc;
+
+ maxl= 57;
+ l= strlen(pkg->name);
+ if (l>20) maxl -= (l-20);
+ limiteddescription(pkg,maxl,&pdesc,&l);
+ printf(" %-20s %.*s\n",pkg->name,l,pdesc);
+}
+
+void audit(const char *const *argv) {
+ struct pkgiterator *it;
+ struct pkginfo *pkg;
+ const struct badstatinfo *bsi;
+ int head;
+
+ if (*argv) badusage(_("--audit does not take any arguments"));
+
+ modstatdb_init(admindir,msdbrw_readonly);
+
+ for (bsi= badstatinfos; bsi->yesno; bsi++) {
+ head= 0;
+ it= iterpkgstart();
+ while ((pkg= iterpkgnext(it))) {
+ if (!bsi->yesno(pkg,bsi)) continue;
+ if (!head) {
+ fputs(gettext(bsi->explanation),stdout);
+ head= 1;
+ }
+ describebriefly(pkg);
+ }
+ iterpkgend(it);
+ if (head) putchar('\n');
+ }
+ if (ferror(stderr)) werr("stderr");
+}
+
+struct sectionentry {
+ struct sectionentry *next;
+ const char *name;
+ int count;
+};
+
+static int yettobeunpacked(struct pkginfo *pkg, const char **thissect) {
+ if (pkg->want != want_install) return 0;
+
+ switch (pkg->status) {
+ case stat_unpacked: case stat_installed: case stat_halfconfigured:
+ return 0;
+ case stat_notinstalled: case stat_halfinstalled: case stat_configfiles:
+ if (thissect)
+ *thissect= pkg->section && *pkg->section ? pkg->section : _("<unknown>");
+ return 1;
+ default:
+ internerr("unknown status checking for unpackedness");
+ }
+ return 0;
+}
+
+void unpackchk(const char *const *argv) {
+ int totalcount, sects;
+ struct sectionentry *sectionentries, *se, **sep;
+ struct pkgiterator *it;
+ struct pkginfo *pkg;
+ const char *thissect;
+ char buf[20];
+ int width;
+
+ if (*argv) badusage(_("--yet-to-unpack does not take any arguments"));
+
+ modstatdb_init(admindir,msdbrw_readonly|msdbrw_noavail);
+
+ totalcount= 0;
+ sectionentries= 0;
+ sects= 0;
+ it= iterpkgstart();
+ while ((pkg= iterpkgnext(it))) {
+ if (!yettobeunpacked(pkg, &thissect)) continue;
+ for (se= sectionentries; se && strcasecmp(thissect,se->name); se= se->next);
+ if (!se) {
+ se= nfmalloc(sizeof(struct sectionentry));
+ for (sep= &sectionentries;
+ *sep && strcasecmp(thissect,(*sep)->name) > 0;
+ sep= &(*sep)->next);
+ se->name= thissect;
+ se->count= 0;
+ se->next= *sep;
+ *sep= se;
+ sects++;
+ }
+ se->count++; totalcount++;
+ }
+ iterpkgend(it);
+
+ if (totalcount == 0) exit(0);
+
+ if (totalcount <= 12) {
+ it= iterpkgstart();
+ while ((pkg= iterpkgnext(it))) {
+ if (!yettobeunpacked(pkg,0)) continue;
+ describebriefly(pkg);
+ }
+ iterpkgend(it);
+ } else if (sects <= 12) {
+ for (se= sectionentries; se; se= se->next) {
+ sprintf(buf,"%d",se->count);
+ printf(_(" %d in %s: "),se->count,se->name);
+ width= 70-strlen(se->name)-strlen(buf);
+ while (width > 59) { putchar(' '); width--; }
+ it= iterpkgstart();
+ while ((pkg= iterpkgnext(it))) {
+ if (!yettobeunpacked(pkg,&thissect)) continue;
+ if (strcasecmp(thissect,se->name)) continue;
+ width -= strlen(pkg->name); width--;
+ if (width < 4) { printf(" ..."); break; }
+ printf(" %s",pkg->name);
+ }
+ iterpkgend(it);
+ putchar('\n');
+ }
+ } else {
+ printf(_(" %d packages, from the following sections:"),totalcount);
+ width= 0;
+ for (se= sectionentries; se; se= se->next) {
+ sprintf(buf,"%d",se->count);
+ width -= (6 + strlen(se->name) + strlen(buf));
+ if (width < 0) { putchar('\n'); width= 73 - strlen(se->name) - strlen(buf); }
+ printf(" %s (%d)",se->name,se->count);
+ }
+ putchar('\n');
+ }
+ fflush(stdout);
+ if (ferror(stdout)) werr("stdout");
+}
+
+static void assertversion(const char *const *argv,
+ struct versionrevision *verrev_buf,
+ const char *reqversion) {
+ struct pkginfo *pkg;
+
+ if (*argv) badusage(_("--assert-* does not take any arguments"));
+
+ modstatdb_init(admindir,msdbrw_readonly|msdbrw_noavail);
+ if (verrev_buf->epoch == ~0UL) {
+ verrev_buf->epoch= 0;
+ verrev_buf->version= nfstrsave(reqversion);
+ verrev_buf->revision= 0;
+ }
+ pkg= findpackage("dpkg");
+ switch (pkg->status) {
+ case stat_installed:
+ break;
+ case stat_unpacked: case stat_halfconfigured: case stat_halfinstalled:
+ if (versionsatisfied3(&pkg->configversion,verrev_buf,dvr_laterequal))
+ break;
+ printf(_("Version of dpkg with working epoch support not yet configured.\n"
+ " Please use `dpkg --configure dpkg', and then try again.\n"));
+ exit(1);
+ default:
+ printf(_("dpkg not recorded as installed, cannot check for epoch support !\n"));
+ exit(1);
+ }
+}
+
+void assertpredep(const char *const *argv) {
+ static struct versionrevision predepversion = {~0UL,0,0};
+ assertversion(argv,&predepversion,"1.1.0");
+}
+
+void assertepoch(const char *const *argv) {
+ static struct versionrevision epochversion = {~0UL,0,0};
+ assertversion(argv,&epochversion,"1.4.0.7");
+}
+
+void assertlongfilenames(const char *const *argv) {
+ static struct versionrevision epochversion = {~0UL,0,0};
+ assertversion(argv,&epochversion,"1.4.1.17");
+}
+
+void assertmulticonrep(const char *const *argv) {
+ static struct versionrevision epochversion = {~0UL,0,0};
+ assertversion(argv,&epochversion,"1.4.1.19");
+}
+
+void predeppackage(const char *const *argv) {
+ /* Print a single package which:
+ * (a) is the target of one or more relevant predependencies.
+ * (b) has itself no unsatisfied pre-dependencies.
+ * If such a package is present output is the Packages file entry,
+ * which can be massaged as appropriate.
+ * Exit status:
+ * 0 = a package printed, OK
+ * 1 = no suitable package available
+ * 2 = error
+ */
+ static struct varbuf vb;
+
+ struct pkgiterator *it;
+ struct pkginfo *pkg= 0, *startpkg, *trypkg;
+ struct dependency *dep;
+ struct deppossi *possi, *provider;
+
+ if (*argv) badusage(_("--predep-package does not take any argument"));
+
+ modstatdb_init(admindir,msdbrw_readonly);
+ clear_istobes(); /* We use clientdata->istobe to detect loops */
+
+ for (it=iterpkgstart(), dep=0;
+ !dep && (pkg=iterpkgnext(it));
+ ) {
+ if (pkg->want != want_install) continue; /* Ignore packages user doesn't want */
+ if (!pkg->files) continue; /* Ignore packages not available */
+ pkg->clientdata->istobe= itb_preinstall;
+ for (dep= pkg->available.depends; dep; dep= dep->next) {
+ if (dep->type != dep_predepends) continue;
+ if (depisok(dep,&vb,0,1)) continue;
+ break; /* This will leave dep non-NULL, and so exit the loop. */
+ }
+ pkg->clientdata->istobe= itb_normal;
+ /* If dep is NULL we go and get the next package. */
+ }
+ if (!dep) exit(1); /* Not found */
+ assert(pkg);
+ startpkg= pkg;
+ pkg->clientdata->istobe= itb_preinstall;
+
+ /* OK, we have found an unsatisfied predependency.
+ * Now go and find the first thing we need to install, as a first step
+ * towards satisfying it.
+ */
+ do {
+ /* We search for a package which would satisfy dep, and put it in pkg */
+ for (possi=dep->list, pkg=0;
+ !pkg && possi;
+ possi=possi->next) {
+ trypkg= possi->ed;
+ if (!trypkg->available.valid) continue;
+ if (trypkg->files && versionsatisfied(&trypkg->available,possi)) {
+ if (trypkg->clientdata->istobe == itb_normal) { pkg= trypkg; break; }
+ }
+ if (possi->verrel != dvr_none) continue;
+ for (provider=possi->ed->available.depended;
+ !pkg && provider;
+ provider=provider->next) {
+ if (provider->up->type != dep_provides) continue;
+ trypkg= provider->up->up;
+ if (!trypkg->available.valid || !trypkg->files) continue;
+ if (trypkg->clientdata->istobe == itb_normal) { pkg= trypkg; break; }
+ }
+ }
+ if (!pkg) {
+ varbufreset(&vb);
+ describedepcon(&vb,dep);
+ varbufaddc(&vb,0);
+ fprintf(stderr, _("dpkg: cannot see how to satisfy pre-dependency:\n %s\n"),vb.buf);
+ ohshit(_("cannot satisfy pre-dependencies for %.250s (wanted due to %.250s)"),
+ dep->up->name,startpkg->name);
+ }
+ pkg->clientdata->istobe= itb_preinstall;
+ for (dep= pkg->available.depends; dep; dep= dep->next) {
+ if (dep->type != dep_predepends) continue;
+ if (depisok(dep,&vb,0,1)) continue;
+ break; /* This will leave dep non-NULL, and so exit the loop. */
+ }
+ } while (dep);
+
+ /* OK, we've found it - pkg has no unsatisfied pre-dependencies ! */
+ writerecord(stdout,"<stdout>",pkg,&pkg->available);
+ if (fflush(stdout)) werr("stdout");
+}
+
+void printarch(const char *const *argv) {
+ if (*argv) badusage(_("--print-architecture does not take any argument"));
+
+ if (printf("%s\n",architecture) == EOF) werr("stdout");
+ if (fflush(stdout)) werr("stdout");
+}
+
+void cmpversions(const char *const *argv) {
+ struct relationinfo {
+ const char *string;
+ /* These values are exit status codes, so 0=true, 1=false */
+ int if_lesser, if_equal, if_greater;
+ int if_none_a, if_none_both, if_none_b;
+ };
+
+ static const struct relationinfo relationinfos[]= {
+ /* < = > !a!2!b */
+ { "le", 0,0,1, 0,0,1 },
+ { "lt", 0,1,1, 0,1,1 },
+ { "eq", 1,0,1, 1,0,1 },
+ { "ne", 0,1,0, 0,1,0 },
+ { "ge", 1,0,0, 1,0,0 },
+ { "gt", 1,1,0, 1,1,0 },
+ { "le-nl", 0,0,1, 1,0,0 }, /* Here none */
+ { "lt-nl", 0,1,1, 1,1,0 }, /* is counted */
+ { "ge-nl", 1,0,0, 0,0,1 }, /* than any version.*/
+ { "gt-nl", 1,1,0, 0,1,1 }, /* */
+ { "<", 0,0,1, 0,0,1 }, /* For compatibility*/
+ { "<=", 0,0,1, 0,0,1 }, /* with dpkg */
+ { "<<", 0,1,1, 0,1,1 }, /* control file */
+ { "=", 1,0,1, 1,0,1 }, /* syntax */
+ { ">", 1,0,0, 1,0,0 }, /* */
+ { ">=", 1,0,0, 1,0,0 },
+ { ">>", 1,1,0, 1,1,0 },
+ { 0 }
+ };
+
+ const struct relationinfo *rip;
+ const char *emsg;
+ struct versionrevision a, b;
+ int r;
+
+ if (!argv[0] || !argv[1] || !argv[2] || argv[3])
+ badusage(_("--compare-versions takes three arguments:"
+ " <version> <relation> <version>"));
+
+ for (rip=relationinfos; rip->string && strcmp(rip->string,argv[1]); rip++);
+
+ if (!rip->string) badusage(_("--compare-versions bad relation"));
+
+ if (*argv[0] && strcmp(argv[0],"<unknown>")) {
+ emsg= parseversion(&a,argv[0]);
+ if (emsg) {
+ if (printf(_("version a has bad syntax: %s\n"),emsg) == EOF) werr("stdout");
+ if (fflush(stdout)) werr("stdout");
+ exit(1);
+ }
+ } else {
+ blankversion(&a);
+ }
+ if (*argv[2] && strcmp(argv[2],"<unknown>")) {
+ emsg= parseversion(&b,argv[2]);
+ if (emsg) {
+ if (printf(_("version b has bad syntax: %s\n"),emsg) == EOF) werr("stdout");
+ if (fflush(stdout)) werr("stdout");
+ exit(1);
+ }
+ } else {
+ blankversion(&b);
+ }
+ if (!informativeversion(&a)) {
+ exit(informativeversion(&b) ? rip->if_none_a : rip->if_none_both);
+ } else if (!informativeversion(&b)) {
+ exit(rip->if_none_b);
+ }
+ r= versioncompare(&a,&b);
+ debug(dbg_general,"cmpversions a=`%s' b=`%s' r=%d",
+ versiondescribe(&a,vdew_always),
+ versiondescribe(&b,vdew_always),
+ r);
+ if (r>0) exit(rip->if_greater);
+ else if (r<0) exit(rip->if_lesser);
+ else exit(rip->if_equal);
+}
diff --git a/src/errors.c b/src/errors.c
new file mode 100644
index 000000000..a830c2d1f
--- /dev/null
+++ b/src/errors.c
@@ -0,0 +1,132 @@
+/*
+ * dpkg - main program for package management
+ * main.c - main program
+ *
+ * Copyright (C) 1994,1995 Ian Jackson <ian@chiark.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 dpkg; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <config.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <limits.h>
+#include <ctype.h>
+
+#include <dpkg.h>
+#include <dpkg-db.h>
+#include <myopt.h>
+
+#include "main.h"
+
+int nerrs= 0;
+
+struct error_report {
+ struct error_report *next;
+ const char *what;
+};
+
+static struct error_report *reports=0;
+static struct error_report **lastreport= &reports;
+static struct error_report emergency;
+
+extern struct pipef *status_pipes;
+
+void print_error_perpackage(const char *emsg, const char *arg) {
+ struct error_report *nr;
+
+ fprintf(stderr, _("%s: error processing %s (--%s):\n %s\n"),
+ DPKG, arg, cipaction->olong, emsg);
+
+ if (status_pipes) {
+ static struct varbuf *status= NULL;
+ struct pipef *pipef= status_pipes;
+ int r;
+ if (status == NULL) {
+ status = nfmalloc(sizeof(struct varbuf));
+ varbufinit(status);
+ } else
+ varbufreset(status);
+
+ r= varbufprintf(status, "status: %s : %s : %s\n", arg, "error",emsg);
+ while (pipef) {
+ write(pipef->fd, status->buf, r);
+ pipef= pipef->next;
+ }
+ }
+
+
+ nr= malloc(sizeof(struct error_report));
+ if (!nr) {
+ perror(_("dpkg: failed to allocate memory for new entry in list of failed packages."));
+ onerr_abort++;
+ nr= &emergency;
+ }
+ nr->what= arg;
+ nr->next= 0;
+ *lastreport= nr;
+ lastreport= &nr->next;
+
+ if (nerrs++ < errabort) return;
+ fprintf(stderr, _("dpkg: too many errors, stopping\n"));
+ onerr_abort++;
+}
+
+int reportbroken_retexitstatus(void) {
+ if (reports) {
+ fputs(_("Errors were encountered while processing:\n"),stderr);
+ while (reports) {
+ fprintf(stderr," %s\n",reports->what);
+ reports= reports->next;
+ }
+ }
+ if (onerr_abort) {
+ fputs(_("Processing was halted because there were too many errors.\n"),stderr);
+ }
+ return nerrs || onerr_abort ? 1 : 0;
+}
+
+int skip_due_to_hold(struct pkginfo *pkg) {
+ if (pkg->want != want_hold) return 0;
+ if (fc_hold) {
+ fprintf(stderr, _("Package %s was on hold, processing it anyway as you request\n"),
+ pkg->name);
+ return 0;
+ }
+ printf(_("Package %s is on hold, not touching it. Use --force-hold to override.\n"),
+ pkg->name);
+ return 1;
+}
+
+void forcibleerr(int forceflag, const char *fmt, ...) {
+ va_list al;
+ va_start(al,fmt);
+ if (forceflag) {
+ fputs(_("dpkg - warning, overriding problem because --force enabled:\n "),stderr);
+ vfprintf(stderr,fmt,al);
+ fputc('\n',stderr);
+ } else {
+ ohshitv(fmt,al);
+ }
+ va_end(al);
+}
diff --git a/src/filesdb.c b/src/filesdb.c
new file mode 100644
index 000000000..35f1d2ed5
--- /dev/null
+++ b/src/filesdb.c
@@ -0,0 +1,610 @@
+/*
+ * dpkg - main program for package management
+ * filesdb.c - management of database of files installed on system
+ *
+ * Copyright (C) 1995 Ian Jackson <ian@chiark.greenend.org.uk>
+ * Copyright (C) 2000,2001 Wichert Akkerman <wakkerma@debian.org>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 dpkg; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <config.h>
+
+#include <assert.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <pwd.h>
+#include <grp.h>
+#include <sys/types.h>
+
+#include <dpkg.h>
+#include <dpkg-db.h>
+
+#include "filesdb.h"
+#include "main.h"
+
+/*** Generic data structures and routines ***/
+
+static int allpackagesdone= 0;
+static int nfiles= 0;
+static struct diversion *diversions= 0;
+static FILE *diversionsfile= 0;
+static FILE *statoverridefile= 0;
+
+void note_must_reread_files_inpackage(struct pkginfo *pkg) {
+ allpackagesdone= 0;
+ ensure_package_clientdata(pkg);
+ pkg->clientdata->fileslistvalid= 0;
+}
+
+static int saidread=0;
+
+ /* load the list of files in this package into memory, or update the
+ * list if it is there but stale
+ */
+void ensure_packagefiles_available(struct pkginfo *pkg) {
+ int fd;
+ const char *filelistfile;
+ struct fileinlist **lendp, *newent, *current;
+ struct filepackages *packageslump;
+ int search, findlast, putat;
+ struct stat stat_buf;
+ char *loaded_list, *loaded_list_end, *thisline, *nextline, *ptr;
+
+ if (pkg->clientdata && pkg->clientdata->fileslistvalid) return;
+ ensure_package_clientdata(pkg);
+
+ /* Throw away any the stale data, if there was any. */
+ for (current= pkg->clientdata->files;
+ current;
+ current= current->next) {
+ /* For each file that used to be in the package,
+ * go through looking for this package's entry in the list
+ * of packages containing this file, and blank it out.
+ */
+ for (packageslump= current->namenode->packages;
+ packageslump;
+ packageslump= packageslump->more)
+ for (search= 0;
+ search < PERFILEPACKAGESLUMP && packageslump->pkgs[search];
+ search++)
+ if (packageslump->pkgs[search] == pkg) {
+ /* Hah! Found it. */
+ for (findlast= search+1;
+ findlast < PERFILEPACKAGESLUMP && packageslump->pkgs[findlast];
+ findlast++);
+ findlast--;
+ /* findlast is now the last occupied entry, which may be the same as
+ * search. We blank out the entry for this package. We also
+ * have to copy the last entry into the empty slot, because
+ * the list is null-pointer-terminated.
+ */
+ packageslump->pkgs[search]= packageslump->pkgs[findlast];
+ packageslump->pkgs[findlast]= 0;
+ /* This may result in an empty link in the list. This is OK. */
+ goto xit_search_to_delete_from_perfilenodelist;
+ }
+ xit_search_to_delete_from_perfilenodelist:
+ ;
+ /* The actual filelist links were allocated using nfmalloc, so
+ * we shouldn't free them.
+ */
+ }
+ pkg->clientdata->files= 0;
+
+ /* Packages which aren't installed don't have a files list. */
+ if (pkg->status == stat_notinstalled) {
+ pkg->clientdata->fileslistvalid= 1; return;
+ }
+
+ filelistfile= pkgadminfile(pkg,LISTFILE);
+
+ onerr_abort++;
+
+ fd= open(filelistfile,O_RDONLY);
+
+ if (fd==-1) {
+ if (errno != ENOENT)
+ ohshite(_("unable to open files list file for package `%.250s'"),pkg->name);
+ onerr_abort--;
+ if (pkg->status != stat_configfiles) {
+ if (saidread == 1) putc('\n',stderr);
+ fprintf(stderr,
+ _("dpkg: serious warning: files list file for package `%.250s' missing,"
+ " assuming package has no files currently installed.\n"), pkg->name);
+ }
+ pkg->clientdata->files= 0;
+ pkg->clientdata->fileslistvalid= 1;
+ return;
+ }
+
+ push_cleanup(cu_closefd,ehflag_bombout, 0,0, 1,&fd);
+
+ if(fstat(fd, &stat_buf))
+ ohshite("unable to stat files list file for package `%.250s'",pkg->name);
+
+ if (stat_buf.st_size) {
+ loaded_list = nfmalloc(stat_buf.st_size);
+ loaded_list_end = loaded_list + stat_buf.st_size;
+
+ fd_buf_copy(fd, loaded_list, stat_buf.st_size, _("files list for package `%.250s'"), pkg->name);
+
+ lendp= &pkg->clientdata->files;
+ thisline = loaded_list;
+ while (thisline < loaded_list_end) {
+ if (!(ptr = memchr(thisline, '\n', loaded_list_end - thisline)))
+ ohshit("files list file for package `%.250s' is missing final newline",pkg->name);
+ /* where to start next time around */
+ nextline = ptr + 1;
+ /* strip trailing "/" */
+ if (ptr > thisline && ptr[-1] == '/') ptr--;
+ /* add the file to the list */
+ if (ptr == thisline)
+ ohshit(_("files list file for package `%.250s' contains empty filename"),pkg->name);
+ *ptr = 0;
+ newent= nfmalloc(sizeof(struct fileinlist));
+ newent->namenode= findnamenode(thisline, fnn_nocopy);
+ newent->next= 0;
+ *lendp= newent;
+ lendp= &newent->next;
+ thisline = nextline;
+ }
+ }
+ pop_cleanup(ehflag_normaltidy); /* fd= open() */
+ if (close(fd))
+ ohshite(_("error closing files list file for package `%.250s'"),pkg->name);
+
+ onerr_abort--;
+
+ for (newent= pkg->clientdata->files; newent; newent= newent->next) {
+ packageslump= newent->namenode->packages;
+ putat= 0;
+ if (packageslump) {
+ for (; putat < PERFILEPACKAGESLUMP && packageslump->pkgs[putat];
+ putat++);
+ if (putat >= PERFILEPACKAGESLUMP) packageslump= 0;
+ }
+ if (!packageslump) {
+ packageslump= nfmalloc(sizeof(struct filepackages));
+ packageslump->more= newent->namenode->packages;
+ newent->namenode->packages= packageslump;
+ putat= 0;
+ }
+ packageslump->pkgs[putat]= pkg;
+ if (++putat < PERFILEPACKAGESLUMP) packageslump->pkgs[putat]= 0;
+ }
+ pkg->clientdata->fileslistvalid= 1;
+}
+
+void ensure_allinstfiles_available(void) {
+ struct pkgiterator *it;
+ struct pkginfo *pkg;
+
+ if (allpackagesdone) return;
+ if (saidread<2) {
+ saidread=1;
+ printf(_("(Reading database ... "));
+ }
+ it= iterpkgstart();
+ while ((pkg= iterpkgnext(it)) != 0) ensure_packagefiles_available(pkg);
+ iterpkgend(it);
+ allpackagesdone= 1;
+
+ if (saidread==1) {
+ printf(_("%d files and directories currently installed.)\n"),nfiles);
+ saidread=2;
+ }
+}
+
+void ensure_allinstfiles_available_quiet(void) {
+ saidread=2;
+ ensure_allinstfiles_available();
+}
+
+void write_filelist_except(struct pkginfo *pkg, struct fileinlist *list, int leaveout) {
+ /* If leaveout is nonzero, will not write any file whose filenamenode
+ * has the fnnf_elide_other_lists flag set.
+ */
+ static struct varbuf vb, newvb;
+ FILE *file;
+
+ varbufreset(&vb);
+ varbufaddstr(&vb,admindir);
+ varbufaddstr(&vb,"/" INFODIR);
+ varbufaddstr(&vb,pkg->name);
+ varbufaddstr(&vb,"." LISTFILE);
+ varbufaddc(&vb,0);
+
+ varbufreset(&newvb);
+ varbufaddstr(&newvb,vb.buf);
+ varbufaddstr(&newvb,NEWDBEXT);
+ varbufaddc(&newvb,0);
+
+ file= fopen(newvb.buf,"w+");
+ if (!file)
+ ohshite(_("unable to create updated files list file for package %s"),pkg->name);
+ push_cleanup(cu_closefile,ehflag_bombout, 0,0, 1,(void*)file);
+ while (list) {
+ if (!(leaveout && (list->namenode->flags & fnnf_elide_other_lists))) {
+ fputs(list->namenode->name,file);
+ putc('\n',file);
+ }
+ list= list->next;
+ }
+ if (ferror(file))
+ ohshite(_("failed to write to updated files list file for package %s"),pkg->name);
+ if (fflush(file))
+ ohshite(_("failed to flush updated files list file for package %s"),pkg->name);
+ if (fsync(fileno(file)))
+ ohshite(_("failed to sync updated files list file for package %s"),pkg->name);
+ pop_cleanup(ehflag_normaltidy); /* file= fopen() */
+ if (fclose(file))
+ ohshite(_("failed to close updated files list file for package %s"),pkg->name);
+ if (rename(newvb.buf,vb.buf))
+ ohshite(_("failed to install updated files list file for package %s"),pkg->name);
+
+ note_must_reread_files_inpackage(pkg);
+}
+
+void reversefilelist_init(struct reversefilelistiter *iterptr,
+ struct fileinlist *files) {
+ /* Initialises an iterator that appears to go through the file
+ * list `files' in reverse order, returning the namenode from
+ * each. What actually happens is that we walk the list here,
+ * building up a reverse list, and then peel it apart one
+ * entry at a time.
+ */
+ struct fileinlist *newent;
+
+ iterptr->todo= 0;
+ while (files) {
+ newent= m_malloc(sizeof(struct fileinlist));
+ newent->namenode= files->namenode;
+ newent->next= iterptr->todo;
+ iterptr->todo= newent;
+ files= files->next;
+ }
+}
+
+struct filenamenode *reversefilelist_next(struct reversefilelistiter *iterptr) {
+ struct filenamenode *ret;
+ struct fileinlist *todo;
+
+ todo= iterptr->todo;
+ if (!todo) return 0;
+ ret= todo->namenode;
+ iterptr->todo= todo->next;
+ free(todo);
+ return ret;
+}
+
+void reversefilelist_abort(struct reversefilelistiter *iterptr) {
+ /* Clients must call this function to clean up the reversefilelistiter
+ * if they wish to break out of the iteration before it is all done.
+ * Calling this function is not necessary if reversefilelist_next has
+ * been called until it returned 0.
+ */
+ while (reversefilelist_next(iterptr));
+}
+
+void ensure_statoverrides(void) {
+ static struct varbuf vb;
+
+ struct stat stab1, stab2;
+ FILE *file;
+ char *loaded_list, *loaded_list_end, *thisline, *nextline, *ptr;
+ struct filestatoverride *fso;
+ struct filenamenode *fnn;
+
+ varbufreset(&vb);
+ varbufaddstr(&vb,admindir);
+ varbufaddstr(&vb,"/" STATOVERRIDEFILE);
+ varbufaddc(&vb,0);
+
+ onerr_abort++;
+
+ file= fopen(vb.buf,"r");
+ if (!file) {
+ if (errno != ENOENT) ohshite(_("failed to open statoverride file"));
+ if (!statoverridefile) { onerr_abort--; return; }
+ } else {
+ if (fstat(fileno(file),&stab2))
+ ohshite(_("failed to fstat statoverride file"));
+ if (statoverridefile) {
+ if (fstat(fileno(statoverridefile),&stab1))
+ ohshite(_("failed to fstat previous statoverride file"));
+ if (stab1.st_dev == stab2.st_dev && stab1.st_ino == stab2.st_ino) {
+ fclose(file); onerr_abort--; return;
+ }
+ }
+ }
+ if (statoverridefile) fclose(statoverridefile);
+ statoverridefile= file;
+ setcloexec(fileno(statoverridefile), vb.buf);
+
+ /* If the statoverride list is empty we don't need to bother reading it. */
+ if (!stab2.st_size) {
+ onerr_abort--;
+ return;
+ }
+
+ loaded_list = nfmalloc(stab2.st_size);
+ loaded_list_end = loaded_list + stab2.st_size;
+
+ fd_buf_copy(fileno(file), loaded_list, stab2.st_size, _("statoverride file `%.250s'"), vb.buf);
+
+ thisline = loaded_list;
+ while (thisline<loaded_list_end) {
+ char* endptr;
+
+ fso= nfmalloc(sizeof(struct filestatoverride));
+
+ if (!(ptr = memchr(thisline, '\n', loaded_list_end - thisline)))
+ ohshit("statoverride file is missing final newline");
+ /* where to start next time around */
+ nextline = ptr + 1;
+ if (ptr == thisline)
+ ohshit(_("statoverride file contains empty line"));
+ *ptr = 0;
+
+ /* Extract the uid */
+ if (!(ptr=memchr(thisline, ' ', nextline-thisline)))
+ ohshit("syntax error in statusoverride file ");
+ *ptr=0;
+ if (thisline[0]=='#') {
+ fso->uid=strtol(thisline + 1, &endptr, 10);
+ if (*endptr!=0)
+ ohshit("syntax error: invalid uid in statusoverride file ");
+ } else {
+ struct passwd* pw = getpwnam(thisline);
+ if (pw==NULL)
+ ohshit("syntax error: unknown user `%s' in statusoverride file ", thisline);
+ fso->uid=pw->pw_uid;
+ }
+
+ /* Move to the next bit */
+ thisline=ptr+1;
+ if (thisline>=loaded_list_end)
+ ohshit("unexpected end of line in statusoverride file");
+
+ /* Extract the gid */
+ if (!(ptr=memchr(thisline, ' ', nextline-thisline)))
+ ohshit("syntax error in statusoverride file ");
+ *ptr=0;
+ if (thisline[0]=='#') {
+ fso->gid=strtol(thisline + 1, &endptr, 10);
+ if (*endptr!=0)
+ ohshit("syntax error: invalid gid in statusoverride file ");
+ } else {
+ struct group* gr = getgrnam(thisline);
+ if (gr==NULL)
+ ohshit("syntax error: unknown group `%s' in statusoverride file ", thisline);
+ fso->gid=gr->gr_gid;
+ }
+
+ /* Move to the next bit */
+ thisline=ptr+1;
+ if (thisline>=loaded_list_end)
+ ohshit("unexecpted end of line in statusoverride file");
+
+ /* Extract the mode */
+ if (!(ptr=memchr(thisline, ' ', nextline-thisline)))
+ ohshit("syntax error in statusoverride file ");
+ *ptr=0;
+ fso->mode=strtol(thisline, &endptr, 8);
+ if (*endptr!=0)
+ ohshit("syntax error: invalid mode in statusoverride file ");
+
+ /* Move to the next bit */
+ thisline=ptr+1;
+ if (thisline>=loaded_list_end)
+ ohshit("unexecpted end of line in statusoverride file");
+
+ fnn= findnamenode(thisline, 0);
+ if (fnn->statoverride)
+ ohshit("multiple statusoverides present for file `%.250s'", thisline);
+ fnn->statoverride=fso;
+ /* Moving on.. */
+ thisline=nextline;
+ }
+
+ onerr_abort--;
+}
+
+void ensure_diversions(void) {
+ static struct varbuf vb;
+
+ struct stat stab1, stab2;
+ char linebuf[MAXDIVERTFILENAME];
+ FILE *file;
+ struct diversion *ov, *oicontest, *oialtname;
+ int l;
+
+ varbufreset(&vb);
+ varbufaddstr(&vb,admindir);
+ varbufaddstr(&vb,"/" DIVERSIONSFILE);
+ varbufaddc(&vb,0);
+
+ onerr_abort++;
+
+ file= fopen(vb.buf,"r");
+ if (!file) {
+ if (errno != ENOENT) ohshite(_("failed to open diversions file"));
+ if (!diversionsfile) { onerr_abort--; return; }
+ } else if (diversionsfile) {
+ if (fstat(fileno(diversionsfile),&stab1))
+ ohshite(_("failed to fstat previous diversions file"));
+ if (fstat(fileno(file),&stab2))
+ ohshite(_("failed to fstat diversions file"));
+ if (stab1.st_dev == stab2.st_dev && stab1.st_ino == stab2.st_ino) {
+ fclose(file); onerr_abort--; return;
+ }
+ }
+ if (diversionsfile) fclose(diversionsfile);
+ diversionsfile= file;
+ setcloexec(fileno(diversionsfile), vb.buf);
+
+ for (ov= diversions; ov; ov= ov->next) {
+ ov->useinstead->divert->camefrom->divert= 0;
+ ov->useinstead->divert= 0;
+ }
+ diversions= 0;
+ if (!file) { onerr_abort--; return; }
+
+ while (fgets(linebuf,sizeof(linebuf),file)) {
+ oicontest= nfmalloc(sizeof(struct diversion));
+ oialtname= nfmalloc(sizeof(struct diversion));
+
+ l= strlen(linebuf);
+ if (l == 0) ohshit(_("fgets gave an empty string from diversions [i]"));
+ if (linebuf[--l] != '\n') ohshit(_("diversions file has too-long line or EOF [i]"));
+ linebuf[l]= 0;
+ oialtname->camefrom= findnamenode(linebuf, 0);
+ oialtname->useinstead= 0;
+
+ if (!fgets(linebuf,sizeof(linebuf),file)) {
+ if (ferror(file)) ohshite(_("read error in diversions [ii]"));
+ else ohshit(_("unexpected EOF in diversions [ii]"));
+ }
+ l= strlen(linebuf);
+ if (l == 0) ohshit(_("fgets gave an empty string from diversions [ii]"));
+ if (linebuf[--l] != '\n') ohshit(_("diversions file has too-long line or EOF [ii]"));
+ linebuf[l]= 0;
+ oicontest->useinstead= findnamenode(linebuf, 0);
+ oicontest->camefrom= 0;
+
+ if (!fgets(linebuf,sizeof(linebuf),file)) {
+ if (ferror(file)) ohshite(_("read error in diversions [iii]"));
+ else ohshit(_("unexpected EOF in diversions [iii]"));
+ }
+ l= strlen(linebuf);
+ if (l == 0) ohshit(_("fgets gave an empty string from diversions [iii]"));
+ if (linebuf[--l] != '\n') ohshit(_("diversions file has too-long line or EOF [ii]"));
+ linebuf[l]= 0;
+
+ oicontest->pkg= oialtname->pkg=
+ strcmp(linebuf,":") ? findpackage(linebuf) : 0;
+
+ if (oialtname->camefrom->divert || oicontest->useinstead->divert)
+ ohshit(_("conflicting diversions involving `%.250s' or `%.250s'"),
+ oialtname->camefrom->name, oicontest->useinstead->name);
+
+ oialtname->camefrom->divert= oicontest;
+ oicontest->useinstead->divert= oialtname;
+
+ oicontest->next= diversions;
+ diversions= oicontest;
+ }
+ if (ferror(file)) ohshite(_("read error in diversions [i]"));
+
+ onerr_abort--;
+}
+
+struct fileiterator {
+ struct filenamenode *namenode;
+ int nbinn;
+};
+
+#define BINS (1 << 17)
+ /* This must always be a power of two. If you change it
+ * consider changing the per-character hashing factor (currently
+ * 1785 = 137*13) too.
+ */
+
+static struct filenamenode *bins[BINS];
+
+struct fileiterator *iterfilestart(void) {
+ struct fileiterator *i;
+ i= m_malloc(sizeof(struct fileiterator));
+ i->namenode= 0;
+ i->nbinn= 0;
+ return i;
+}
+
+struct filenamenode *iterfilenext(struct fileiterator *i) {
+ struct filenamenode *r= NULL;
+
+ while (!i->namenode) {
+ if (i->nbinn >= BINS) return 0;
+ i->namenode= bins[i->nbinn++];
+ }
+ r= i->namenode;
+ i->namenode= r->next;
+ return r;
+}
+
+void iterfileend(struct fileiterator *i) {
+ free(i);
+}
+
+void filesdbinit(void) {
+ struct filenamenode *fnn;
+ int i;
+
+ for (i=0; i<BINS; i++)
+ for (fnn= bins[i]; fnn; fnn= fnn->next) {
+ fnn->flags= 0;
+ fnn->oldhash= 0;
+ fnn->filestat= 0;
+ }
+}
+
+static int hash(const char *name) {
+ int v= 0;
+ while (*name) { v *= 1787; v += *name; name++; }
+ return v;
+}
+
+struct filenamenode *findnamenode(const char *name, enum fnnflags flags) {
+ struct filenamenode **pointerp, *newnode;
+ const char *orig_name = name;
+
+ /* We skip initial slashes and ./ pairs, and add our own single leading slash. */
+ name= skip_slash_dotslash(name);
+
+ pointerp= bins + (hash(name) & (BINS-1));
+ while (*pointerp) {
+/* Why is this assert nescessary? It is checking already added entries. */
+ assert((*pointerp)->name[0] == '/');
+ if (!strcmp((*pointerp)->name+1,name)) break;
+ pointerp= &(*pointerp)->next;
+ }
+ if (*pointerp) return *pointerp;
+
+ newnode= nfmalloc(sizeof(struct filenamenode));
+ newnode->packages= 0;
+ if((flags & fnn_nocopy) && name > orig_name && name[-1] == '/')
+ newnode->name = name - 1;
+ else {
+ char *newname= nfmalloc(strlen(name)+2);
+ newname[0]= '/'; strcpy(newname+1,name);
+ newnode->name= newname;
+ }
+ newnode->flags= 0;
+ newnode->next= 0;
+ newnode->divert= 0;
+ newnode->statoverride= 0;
+ newnode->filestat= 0;
+ *pointerp= newnode;
+ nfiles++;
+
+ return newnode;
+}
+
+/* vi: ts=8 sw=2
+ */
diff --git a/src/filesdb.h b/src/filesdb.h
new file mode 100644
index 000000000..cedaafeaf
--- /dev/null
+++ b/src/filesdb.h
@@ -0,0 +1,148 @@
+/*
+ * dpkg - main program for package management
+ * filesdb.h - management of database of files installed on system
+ *
+ * Copyright (C) 1995 Ian Jackson <ian@chiark.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 dpkg; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef FILESDB_H
+#define FILESDB_H
+
+/*
+ * Data structure here is as follows:
+ *
+ * For each package we have a `struct fileinlist *', the head of
+ * a list of files in that package. They are in `forwards' order.
+ * Each entry has a pointer to the `struct filenamenode'.
+ *
+ * The struct filenamenodes are in a hash table, indexed by name.
+ * (This hash table is not visible to callers.)
+ *
+ * Each filenamenode has a (possibly empty) list of `struct
+ * filepackage', giving a list of the packages listing that
+ * filename.
+ *
+ * When we read files contained info about a particular package
+ * we set the `files' member of the clientdata struct to the
+ * appropriate thing. When not yet set the files pointer is
+ * made to point to `fileslist_uninited' (this is available only
+ * internally, withing filesdb.c - the published interface is
+ * ensure_*_available).
+ */
+
+struct pkginfo;
+
+/* flags to findnamenode() */
+
+enum fnnflags {
+ fnn_nocopy= 000001, /* do not need to copy filename */
+};
+
+struct filenamenode {
+ struct filenamenode *next;
+ const char *name;
+ struct filepackages *packages;
+ struct diversion *divert;
+ struct filestatoverride *statoverride;
+ /* Fields from here on are used by archives.c &c, and cleared by
+ * filesdbinit.
+ */
+ enum {
+ fnnf_new_conff= 000001, /* in the newconffiles list */
+ fnnf_new_inarchive= 000002, /* in the new filesystem archive */
+ fnnf_old_conff= 000004, /* in the old package's conffiles list */
+ fnnf_elide_other_lists= 000010, /* must remove from other packages' lists */
+ fnnf_no_atomic_overwrite= 000020, /* >=1 instance is a dir, cannot rename over */
+ } flags; /* Set to zero when a new node is created. */
+ const char *oldhash; /* valid iff this namenode is in the newconffiles list */
+ struct stat *filestat;
+};
+
+struct fileinlist {
+ struct fileinlist *next;
+ struct filenamenode *namenode;
+};
+
+struct filestatoverride {
+ /* We allow the administrator to override the owner, group and mode of
+ * a file. If such an override is present we use that instead of the
+ * stat information stored in the archive.
+ *
+ * This functionality used to be in the suidmanager package.
+ */
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+};
+
+struct diversion {
+ /* When we deal with an `overridden' file, every package except
+ * the overriding one is considered to contain the other file
+ * instead. Both files have entries in the filesdb database, and
+ * they refer to each other via these diversion structures.
+ *
+ * The contested filename's filenamenode has an diversion entry
+ * with useinstead set to point to the redirected filename's
+ * filenamenode; the redirected filenamenode has camefrom set to the
+ * contested filenamenode. Both sides' diversion entries will
+ * have pkg set to the package (if any) which is allowed to use the
+ * contended filename.
+ *
+ * Packages that contain either version of the file will all
+ * refer to the contested filenamenode in their per-file package lists
+ * (both in core and on disk). References are redirected to the other
+ * filenamenode's filename where appropriate.
+ */
+ struct filenamenode *useinstead;
+ struct filenamenode *camefrom;
+ struct pkginfo *pkg;
+ struct diversion *next;
+ /* The `contested' halves are in this list for easy cleanup. */
+};
+
+#define PERFILEPACKAGESLUMP 10
+
+struct filepackages {
+ struct filepackages *more;
+ struct pkginfo *pkgs[PERFILEPACKAGESLUMP];
+ /* pkgs is a null-pointer-terminated list; anything after the first null
+ * is garbage
+ */
+};
+
+struct fileiterator;
+struct fileiterator *iterfilestart(void);
+struct filenamenode *iterfilenext(struct fileiterator *i);
+void iterfileend(struct fileiterator *i);
+
+void ensure_diversions(void);
+void ensure_statoverrides(void);
+
+void ensure_packagefiles_available(struct pkginfo *pkg);
+void ensure_allinstfiles_available(void);
+void ensure_allinstfiles_available_quiet(void);
+void note_must_reread_files_inpackage(struct pkginfo *pkg);
+struct filenamenode *findnamenode(const char *filename, enum fnnflags flags);
+void write_filelist_except(struct pkginfo *pkg, struct fileinlist *list, int leaveout);
+
+struct reversefilelistiter { struct fileinlist *todo; };
+
+void reversefilelist_init(struct reversefilelistiter *iterptr, struct fileinlist *files);
+struct filenamenode *reversefilelist_next(struct reversefilelistiter *iterptr);
+void reversefilelist_abort(struct reversefilelistiter *iterptr);
+
+#endif /* FILESDB_H */
diff --git a/src/help.c b/src/help.c
new file mode 100644
index 000000000..8a4746208
--- /dev/null
+++ b/src/help.c
@@ -0,0 +1,480 @@
+/*
+ * dpkg - main program for package management
+ * help.c - various helper routines
+ *
+ * Copyright (C) 1995 Ian Jackson <ian@chiark.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 dpkg; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <config.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <assert.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#include <dpkg.h>
+#include <dpkg-db.h>
+
+#include "filesdb.h"
+#include "main.h"
+
+const char *const statusstrings[]= {
+ N_("not installed"),
+ N_("unpacked but not configured"),
+ N_("broken due to postinst failure"),
+ N_("installed"),
+ N_("broken due to failed removal"),
+ N_("not installed but configs remain")
+};
+
+struct filenamenode *namenodetouse(struct filenamenode *namenode, struct pkginfo *pkg) {
+ struct filenamenode *r;
+
+ if (!namenode->divert) return namenode;
+
+ debug(dbg_eachfile,"namenodetouse namenode=`%s' pkg=%s",
+ namenode->name,pkg->name);
+
+ r=
+ (namenode->divert->useinstead && namenode->divert->pkg != pkg)
+ ? namenode->divert->useinstead : namenode;
+
+ debug(dbg_eachfile,
+ "namenodetouse ... useinstead=%s camefrom=%s pkg=%s return %s",
+ namenode->divert->useinstead ? namenode->divert->useinstead->name : "<none>",
+ namenode->divert->camefrom ? namenode->divert->camefrom->name : "<none>",
+ namenode->divert->pkg ? namenode->divert->pkg->name : "<none>",
+ r->name);
+ return r;
+}
+
+void checkpath(void) {
+/* Verify that some programs can be found in the PATH. */
+ static const char *const checklist[]= { "ldconfig",
+#ifdef WITH_START_STOP_DAEMON
+ "start-stop-daemon",
+#endif
+ "install-info", "update-rc.d", 0
+ };
+
+ struct stat stab;
+ const char *const *clp;
+ const char *path, *s, *p;
+ char* buf;
+ int warned= 0;
+ long l;
+
+ path= getenv("PATH");
+ if (!path) ohshit(_("dpkg - error: PATH is not set.\n"));
+ buf=(char*)m_malloc(strlen(path)+2+strlen("start-stop-daemon"));
+
+ for (clp=checklist; *clp; clp++) {
+ s= path;
+ while (s) {
+ p= strchr(s,':');
+ l= p ? p-s : (long)strlen(s);
+ memcpy(buf,s,l);
+ if (l) buf[l++]= '/';
+ strcpy(buf+l,*clp);
+ if (stat(buf,&stab) == 0 && (stab.st_mode & 0111)) break;
+ s= p; if (s) s++;
+ }
+ if (!s) {
+ fprintf(stderr,_("dpkg: `%s' not found on PATH.\n"),*clp);
+ warned++;
+ }
+ }
+
+ free(buf);
+ if (warned)
+ forcibleerr(fc_badpath,_("%d expected program(s) not found on PATH.\nNB: root's "
+ "PATH should usually contain /usr/local/sbin, /usr/sbin and /sbin."),
+ warned);
+}
+
+void ensure_package_clientdata(struct pkginfo *pkg) {
+ if (pkg->clientdata) return;
+ pkg->clientdata= nfmalloc(sizeof(struct perpackagestate));
+ pkg->clientdata->istobe= itb_normal;
+ pkg->clientdata->fileslistvalid= 0;
+ pkg->clientdata->files= 0;
+}
+
+void cu_closepipe(int argc, void **argv) {
+ int *p1= (int*)argv[0];
+ close(p1[0]); close(p1[1]);
+}
+
+void cu_closefile(int argc, void **argv) {
+ FILE *f= (FILE*)(argv[0]);
+ fclose(f);
+}
+
+void cu_closedir(int argc, void **argv) {
+ DIR *d= (DIR*)(argv[0]);
+ closedir(d);
+}
+
+void cu_closefd(int argc, void **argv) {
+ int ip= *(int*)argv[0];
+ close(ip);
+}
+
+int ignore_depends(struct pkginfo *pkg) {
+ struct packageinlist *id;
+ for (id= ignoredependss; id; id= id->next)
+ if (id->pkg == pkg) return 1;
+ return 0;
+}
+
+int force_depends(struct deppossi *possi) {
+ return fc_depends ||
+ ignore_depends(possi->ed) ||
+ ignore_depends(possi->up->up);
+}
+
+int force_conflicts(struct deppossi *possi) {
+ return fc_conflicts;
+}
+
+const char *pkgadminfile(struct pkginfo *pkg, const char *whichfile) {
+ static struct varbuf vb;
+ varbufreset(&vb);
+ varbufaddstr(&vb,admindir);
+ varbufaddstr(&vb,"/" INFODIR);
+ varbufaddstr(&vb,pkg->name);
+ varbufaddc(&vb,'.');
+ varbufaddstr(&vb,whichfile);
+ varbufaddc(&vb,0);
+ return vb.buf;
+}
+
+static const char* preexecscript(const char *path, char *const *argv) {
+ /* returns the path to the script inside the chroot
+ * none of the stuff here will work if admindir isn't inside instdir
+ * as expected. - fixme
+ */
+ size_t instdirl;
+
+ if (*instdir) {
+ if (chroot(instdir)) ohshite(_("failed to chroot to `%.250s'"),instdir);
+ }
+ if (f_debug & dbg_scripts) {
+ fprintf(stderr,"D0%05o: fork/exec %s (",dbg_scripts,path);
+ while (*argv) fprintf(stderr," %s",*argv++);
+ fputs(" )\n",stderr);
+ }
+ instdirl= strlen(instdir);
+ if (!instdirl) return path;
+ assert (strlen(path)>=instdirl);
+ return path+instdirl;
+}
+
+static char *const *vbuildarglist(const char *scriptname, va_list ap) {
+ static char *bufs[PKGSCRIPTMAXARGS+1];
+ char *nextarg;
+ int i;
+
+ i=0;
+ if(bufs[0]) free(bufs[0]);
+ bufs[i++]= strdup(scriptname); /* yes, cast away const because exec wants it that way */
+ for (;;) {
+ assert(i < PKGSCRIPTMAXARGS);
+ nextarg= va_arg(ap,char*);
+ if (!nextarg) break;
+ bufs[i++]= nextarg;
+ }
+ bufs[i]= 0;
+ return bufs;
+}
+
+static char *const *buildarglist(const char *scriptname, ...) {
+ char *const *arglist;
+ va_list ap;
+ va_start(ap,scriptname);
+ arglist= vbuildarglist(scriptname,ap);
+ va_end(ap);
+ return arglist;
+}
+
+#define NSCRIPTCATCHSIGNALS (int)(sizeof(script_catchsignallist)/sizeof(int)-1)
+static int script_catchsignallist[]= { SIGQUIT, SIGINT, 0 };
+static struct sigaction script_uncatchsignal[NSCRIPTCATCHSIGNALS];
+
+static void cu_restorescriptsignals(int argc, void **argv) {
+ int i;
+ for (i=0; i<NSCRIPTCATCHSIGNALS; i++) {
+ if (sigaction(script_catchsignallist[i],&script_uncatchsignal[i],0)) {
+ fprintf(stderr,_("error un-catching signal %s: %s\n"),
+ strsignal(script_catchsignallist[i]),strerror(errno));
+ onerr_abort++;
+ }
+ }
+}
+
+static void script_catchsignals(void) {
+ int i;
+ struct sigaction catchsig;
+
+ onerr_abort++;
+ memset(&catchsig,0,sizeof(catchsig));
+ catchsig.sa_handler= SIG_IGN;
+ sigemptyset(&catchsig.sa_mask);
+ catchsig.sa_flags= 0;
+ for (i=0; i<NSCRIPTCATCHSIGNALS; i++)
+ if (sigaction(script_catchsignallist[i],&catchsig,&script_uncatchsignal[i]))
+ ohshite(_("unable to ignore signal %s before running script"),
+ strsignal(script_catchsignallist[i]));
+ push_cleanup(cu_restorescriptsignals,~0, 0,0, 0);
+ onerr_abort--;
+}
+
+static void setexecute(const char *path, struct stat *stab) {
+ if ((stab->st_mode & 0555) == 0555) return;
+ if (!chmod(path,0755)) return;
+ ohshite(_("unable to set execute permissions on `%.250s'"),path);
+}
+static int do_script(const char *pkg, const char *scriptname, const char *scriptpath, struct stat *stab, char *const arglist[], const char *desc, const char *name, int warn) {
+ const char *scriptexec;
+ int c1, r;
+ setexecute(scriptpath,stab);
+
+ c1= m_fork();
+ if (!c1) {
+ const char **narglist;
+ for (r=0; arglist[r]; r++) ;
+ narglist=nfmalloc((r+1)*sizeof(char*));
+ for (r=1; arglist[r-1]; r++)
+ narglist[r]= arglist[r];
+ scriptexec= preexecscript(scriptpath,(char * const *)narglist);
+ narglist[0]= scriptexec;
+ execv(scriptexec,(char * const *)narglist);
+ ohshite(desc,name);
+ }
+ script_catchsignals(); /* This does a push_cleanup() */
+ r= waitsubproc(c1,name,warn);
+ pop_cleanup(ehflag_normaltidy);
+ return r;
+}
+
+int maintainer_script_installed(struct pkginfo *pkg, const char *scriptname,
+ const char *description, ...) {
+ /* all ...'s are const char*'s */
+ const char *scriptpath;
+ char *const *arglist;
+ struct stat stab;
+ va_list ap;
+ char buf[100];
+
+ scriptpath= pkgadminfile(pkg,scriptname);
+ va_start(ap,description);
+ arglist= vbuildarglist(scriptname,ap);
+ va_end(ap);
+ sprintf(buf,"%s script",description);
+
+ if (stat(scriptpath,&stab)) {
+ if (errno == ENOENT) {
+ debug(dbg_scripts,"maintainer_script_installed nonexistent %s",scriptname);
+ return 0;
+ }
+ ohshite(_("unable to stat installed %s script `%.250s'"),description,scriptpath);
+ }
+ do_script(pkg->name, scriptname, scriptpath, &stab, arglist, _("unable to execute %s"), buf, 0);
+ ensure_diversions();
+ return 1;
+}
+
+int maintainer_script_new(const char *pkgname,
+ const char *scriptname, const char *description,
+ const char *cidir, char *cidirrest, ...) {
+ char *const *arglist;
+ struct stat stab;
+ va_list ap;
+ char buf[100];
+
+ va_start(ap,cidirrest);
+ arglist= vbuildarglist(scriptname,ap);
+ va_end(ap);
+ sprintf(buf,"%s script",description);
+
+ strcpy(cidirrest,scriptname);
+ if (stat(cidir,&stab)) {
+ if (errno == ENOENT) {
+ debug(dbg_scripts,"maintainer_script_new nonexistent %s `%s'",scriptname,cidir);
+ return 0;
+ }
+ ohshite(_("unable to stat new %s script `%.250s'"),description,cidir);
+ }
+ do_script(pkgname, scriptname, cidir, &stab, arglist, _("unable to execute new %s"), buf, 0);
+ ensure_diversions();
+ return 1;
+}
+
+int maintainer_script_alternative(struct pkginfo *pkg,
+ const char *scriptname, const char *description,
+ const char *cidir, char *cidirrest,
+ const char *ifok, const char *iffallback) {
+ const char *oldscriptpath;
+ char *const *arglist;
+ struct stat stab;
+ char buf[100];
+
+ oldscriptpath= pkgadminfile(pkg,scriptname);
+ arglist= buildarglist(scriptname,
+ ifok,versiondescribe(&pkg->available.version,
+ vdew_nonambig),
+ (char*)0);
+ sprintf(buf,_("old %s script"),description);
+ if (stat(oldscriptpath,&stab)) {
+ if (errno == ENOENT) {
+ debug(dbg_scripts,"maintainer_script_alternative nonexistent %s `%s'",
+ scriptname,oldscriptpath);
+ return 0;
+ }
+ fprintf(stderr,
+ _("dpkg: warning - unable to stat %s `%.250s': %s\n"),
+ buf,oldscriptpath,strerror(errno));
+ } else {
+ if (!do_script(pkg->name, scriptname, oldscriptpath, &stab, arglist, _("unable to execute %s"), buf, PROCWARN))
+ return 1;
+ ensure_diversions();
+ }
+ fprintf(stderr, _("dpkg - trying script from the new package instead ...\n"));
+
+ arglist= buildarglist(scriptname,
+ iffallback,versiondescribe(&pkg->installed.version,
+ vdew_nonambig),
+ (char*)0);
+ strcpy(cidirrest,scriptname);
+ sprintf(buf,_("new %s script"),description);
+
+ if (stat(cidir,&stab)) {
+ if (errno == ENOENT)
+ ohshit(_("there is no script in the new version of the package - giving up"));
+ else
+ ohshite(_("unable to stat %s `%.250s'"),buf,cidir);
+ }
+
+ do_script(pkg->name, scriptname, cidir, &stab, arglist, _("unable to execute %s"), buf, 0);
+ fprintf(stderr, _("dpkg: ... it looks like that went OK.\n"));
+
+ ensure_diversions();
+ return 1;
+}
+
+void clear_istobes(void) {
+ struct pkgiterator *it;
+ struct pkginfo *pkg;
+
+ it= iterpkgstart();
+ while ((pkg= iterpkgnext(it)) != 0) {
+ ensure_package_clientdata(pkg);
+ pkg->clientdata->istobe= itb_normal;
+ pkg->clientdata->replacingfilesandsaid= 0;
+ }
+}
+
+void debug(int which, const char *fmt, ...) {
+ va_list ap;
+ if (!(f_debug & which)) return;
+ fprintf(stderr,"D0%05o: ",which);
+ va_start(ap,fmt);
+ vfprintf(stderr,fmt,ap);
+ va_end(ap);
+ putc('\n',stderr);
+}
+
+int isdirectoryinuse(struct filenamenode *file, struct pkginfo *pkg) {
+ /* Returns 1 if the file is used by packages other than pkg, 0 otherwise. */
+ struct filepackages *packageslump;
+ int i;
+
+ debug(dbg_veryverbose, "isdirectoryinuse `%s' (except %s)", file->name,
+ pkg ? pkg->name : "<none>");
+ for (packageslump= file->packages; packageslump; packageslump= packageslump->more) {
+ debug(dbg_veryverbose, "isdirectoryinuse packageslump %s ...",
+ packageslump->pkgs[0] ? packageslump->pkgs[0]->name : "<none>");
+ for (i=0; i < PERFILEPACKAGESLUMP && packageslump->pkgs[i]; i++) {
+ debug(dbg_veryverbose, "isdirectoryinuse considering [%d] %s ...", i,
+ packageslump->pkgs[i]->name);
+ if (packageslump->pkgs[i] == pkg) continue;
+ return 1;
+ }
+ }
+ debug(dbg_veryverbose, "isdirectoryinuse no");
+ return 0;
+}
+
+void oldconffsetflags(const struct conffile *searchconff) {
+ struct filenamenode *namenode;
+
+ while (searchconff) {
+ namenode= findnamenode(searchconff->name, 0); /* XXX */
+ namenode->flags |= fnnf_old_conff;
+ debug(dbg_conffdetail, "oldconffsetflags `%s' namenode %p flags %o",
+ searchconff->name, namenode, namenode->flags);
+ searchconff= searchconff->next;
+ }
+}
+
+int chmodsafe_unlink(const char *pathname) {
+ struct stat stab;
+
+ if (lstat(pathname,&stab)) return -1;
+ if (S_ISREG(stab.st_mode) ? (stab.st_mode & 07000) :
+ !(S_ISLNK(stab.st_mode) || S_ISDIR(stab.st_mode) ||
+ S_ISFIFO(stab.st_mode) || S_ISSOCK(stab.st_mode))) {
+ /* We chmod it if it is 1. a sticky or set-id file, or 2. an unrecognised
+ * object (ie, not a file, link, directory, fifo or socket
+ */
+ if (chmod(pathname,0600)) return -1;
+ }
+ if (unlink(pathname)) return -1;
+ return 0;
+}
+
+void ensure_pathname_nonexisting(const char *pathname) {
+ int c1;
+ const char *u;
+
+ u= skip_slash_dotslash(pathname);
+ assert(*u);
+
+ debug(dbg_eachfile,"ensure_pathname_nonexisting `%s'",pathname);
+ if (!rmdir(pathname)) return; /* Deleted it OK, it was a directory. */
+ if (errno == ENOENT || errno == ELOOP) return;
+ if (errno == ENOTDIR) {
+ /* Either it's a file, or one of the path components is. If one
+ * of the path components is this will fail again ...
+ */
+ if (!chmodsafe_unlink(pathname)) return; /* OK, it was */
+ if (errno == ENOTDIR) return;
+ }
+ if (errno != ENOTEMPTY) /* Huh ? */
+ ohshite(_("failed to rmdir/unlink `%.255s'"),pathname);
+ c1= m_fork();
+ if (!c1) {
+ execlp(RM,"rm","-rf","--",pathname,(char*)0);
+ ohshite(_("failed to exec rm for cleanup"));
+ }
+ debug(dbg_eachfile,"ensure_pathname_nonexisting running rm -rf");
+ waitsubproc(c1,"rm cleanup",0);
+}
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 000000000..955fc4b89
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,563 @@
+/*
+ * dpkg - main program for package management
+ * main.c - main program
+ *
+ * Copyright (C) 1994,1995 Ian Jackson <ian@chiark.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 dpkg; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <config.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <limits.h>
+#include <ctype.h>
+
+#include <dpkg.h>
+#include <dpkg-db.h>
+#include <myopt.h>
+
+#include "main.h"
+
+static void printversion(void) {
+ if (fputs(_("Debian GNU/Linux `"), stdout) < 0) werr("stdout");
+ if (fputs(DPKG, stdout) < 0) werr("stdout");
+ if (fputs(_("' package management program version "), stdout) < 0) werr("stdout");
+ if (fputs( DPKG_VERSION_ARCH ".\n", stdout) < 0) werr("stdout");
+ if (fputs(_( "This is free software; see the GNU General Public Licence version 2 or\n"
+ "later for copying conditions. There is NO warranty.\n"
+ "See " DPKG " --licence for copyright and license details.\n"),
+ stdout) < 0) werr("stdout");
+}
+/*
+ options that need fixing:
+ dpkg --yet-to-unpack \n\
+ */
+static void usage(void) {
+ if (fprintf (stdout, _("\
+Usage: \n\
+ dpkg -i|--install <.deb file name> ... | -R|--recursive <dir> ...\n\
+ dpkg --unpack <.deb file name> ... | -R|--recursive <dir> ...\n\
+ dpkg -A|--record-avail <.deb file name> ... | -R|--recursive <dir> ...\n\
+ dpkg --configure <package name> ... | -a|--pending\n\
+ dpkg -r|--remove | -P|--purge <package name> ... | -a|--pending\n\
+ dpkg --get-selections [<pattern> ...] get list of selections to stdout\n\
+ dpkg --set-selections set package selections from stdin\n\
+ dpkg --update-avail <Packages-file> replace available packages info\n\
+ dpkg --merge-avail <Packages-file> merge with info from file\n\
+ dpkg --clear-avail erase existing available info\n\
+ dpkg --forget-old-unavail forget uninstalled unavailable pkgs\n\
+ dpkg -s|--status <package-name> ... display package status details\n\
+ dpkg -p|--print-avail <package-name> ... display available version details\n\
+ dpkg -L|--listfiles <package-name> ... list files `owned' by package(s)\n\
+ dpkg -l|--list [<pattern> ...] list packages concisely\n\
+ dpkg -S|--search <pattern> ... find package(s) owning file(s)\n\
+ dpkg -C|--audit check for broken package(s)\n\
+ dpkg --print-architecture print dpkg architecture\n\
+ dpkg --compare-versions <a> <rel> <b> compare version numbers - see below\n\
+ dpkg --help | --version show this help / version number\n\
+ dpkg --force-help | -Dh|--debug=help help on forcing resp. debugging\n\
+ dpkg --licence print copyright licensing terms\n\
+\n\
+Use dpkg -b|--build|-c|--contents|-e|--control|-I|--info|-f|--field|\n\
+ -x|--extract|-X|--vextract|--fsys-tarfile on archives (type %s --help.)\n\
+\n\
+For internal use: dpkg --assert-support-predepends | --predep-package |\n\
+ --assert-working-epoch | --assert-long-filenames | --assert-multi-conrep\n\
+\n\
+Options:\n\
+ --admindir=<directory> Use <directory> instead of %s\n\
+ --root=<directory> Install on alternative system rooted elsewhere\n\
+ --instdir=<directory> Change inst'n root without changing admin dir\n\
+ -O|--selected-only Skip packages not selected for install/upgrade\n\
+ -E|--skip-same-version Skip packages whose same version is installed\n\
+ -G|--refuse-downgrade Skip packages with earlier version than installed\n\
+ -B|--auto-deconfigure Install even if it would break some other package\n\
+ --no-debsig Do no try to verify package signatures\n\
+ --no-act|--dry-run|--simulate\n\
+ Just say what we would do - don't do it\n\
+ -D|--debug=<octal> Enable debugging - see -Dhelp or --debug=help\n\
+ --status-fd <n> Send status change updates to file descriptor <n>\n\
+ --ignore-depends=<package>,... Ignore dependencies involving <package>\n\
+ --force-... Override problems - see --force-help\n\
+ --no-force-...|--refuse-... Stop when problems encountered\n\
+ --abort-after <n> Abort after encountering <n> errors\n\
+\n\
+Comparison operators for --compare-versions are:\n\
+ lt le eq ne ge gt (treat empty version as earlier than any version);\n\
+ lt-nl le-nl ge-nl gt-nl (treat empty version as later than any version);\n\
+ < << <= = >= >> > (only for compatibility with control file syntax).\n\
+\n\
+Use `dselect' or 'aptitude' for user-friendly package management.\n"),
+ BACKEND, ADMINDIR) < 0) werr ("stdout");
+}
+
+const char thisname[]= "dpkg";
+const char architecture[]= ARCHITECTURE;
+const char printforhelp[]= N_("\
+Type dpkg --help for help about installing and deinstalling packages [*];\n\
+Use dselect for user-friendly package management;\n\
+Type dpkg -Dhelp for a list of dpkg debug flag values;\n\
+Type dpkg --force-help for a list of forcing options;\n\
+Type dpkg-deb --help for help about manipulating *.deb files;\n\
+Type dpkg --licence for copyright licence and lack of warranty (GNU GPL) [*].\n\
+\n\
+Options marked [*] produce a lot of output - pipe it through `less' or `more' !");
+
+const struct cmdinfo *cipaction= 0;
+int f_pending=0, f_recursive=0, f_alsoselect=1, f_skipsame=0, f_noact=0;
+int f_autodeconf=0, f_nodebsig=0;
+unsigned long f_debug=0;
+/* Change fc_overwrite to 1 to enable force-overwrite by default */
+int fc_downgrade=1, fc_configureany=0, fc_hold=0, fc_removereinstreq=0, fc_overwrite=0;
+int fc_removeessential=0, fc_conflicts=0, fc_depends=0, fc_dependsversion=0;
+int fc_autoselect=1, fc_badpath=0, fc_overwritediverted=0, fc_architecture=0;
+int fc_nonroot=0, fc_overwritedir=0, fc_conff_new=0, fc_conff_miss=0;
+int fc_conff_old=0, fc_conff_def=0;
+int fc_badverify = 0;
+
+int errabort = 50;
+const char *admindir= ADMINDIR;
+const char *instdir= "";
+struct packageinlist *ignoredependss=0;
+
+static const struct forceinfo {
+ const char *name;
+ int *opt;
+} forceinfos[]= {
+ { "downgrade", &fc_downgrade },
+ { "configure-any", &fc_configureany },
+ { "hold", &fc_hold },
+ { "remove-reinstreq", &fc_removereinstreq },
+ { "remove-essential", &fc_removeessential },
+ { "conflicts", &fc_conflicts },
+ { "confnew", &fc_conff_new },
+ { "confold", &fc_conff_old },
+ { "confdef", &fc_conff_def },
+ { "confmiss", &fc_conff_miss },
+ { "depends", &fc_depends },
+ { "depends-version", &fc_dependsversion },
+ { "auto-select", &fc_autoselect },
+ { "bad-path", &fc_badpath },
+ { "not-root", &fc_nonroot },
+ { "overwrite", &fc_overwrite },
+ { "overwrite-diverted", &fc_overwritediverted },
+ { "overwrite-dir", &fc_overwritedir },
+ { "architecture", &fc_architecture },
+ { "bad-verify", &fc_badverify },
+ { 0 }
+};
+
+static void helponly(const struct cmdinfo *cip, const char *value) NONRETURNING;
+static void helponly(const struct cmdinfo *cip, const char *value) {
+ usage(); exit(0);
+}
+static void versiononly(const struct cmdinfo *cip, const char *value) NONRETURNING;
+static void versiononly(const struct cmdinfo *cip, const char *value) {
+ printversion(); exit(0);
+}
+
+static void setaction(const struct cmdinfo *cip, const char *value) {
+ if (cipaction)
+ badusage(_("conflicting actions --%s and --%s"),cip->olong,cipaction->olong);
+ cipaction= cip;
+}
+
+static void setobsolete(const struct cmdinfo *cip, const char *value) {
+ fprintf(stderr, _("Warning: obsolete option `--%s'\n"),cip->olong);
+}
+
+static void setdebug(const struct cmdinfo *cpi, const char *value) {
+ char *endp;
+
+ if (*value == 'h') {
+ if (printf(
+_("%s debugging option, --debug=<octal> or -D<octal>:\n\n\
+ number ref. in source description\n\
+ 1 general Generally helpful progress information\n\
+ 2 scripts Invocation and status of maintainer scripts\n\
+ 10 eachfile Output for each file processed\n\
+ 100 eachfiledetail Lots of output for each file processed\n\
+ 20 conff Output for each configuration file\n\
+ 200 conffdetail Lots of output for each configuration file\n\
+ 40 depcon Dependencies and conflicts\n\
+ 400 depcondetail Lots of dependencies/conflicts output\n\
+ 1000 veryverbose Lots of drivel about eg the dpkg/info directory\n\
+ 2000 stupidlyverbose Insane amounts of drivel\n\n\
+Debugging options are be mixed using bitwise-or.\n\
+Note that the meanings and values are subject to change.\n"),
+ DPKG) < 0) werr("stdout");
+ exit(0);
+ }
+
+ f_debug= strtoul(value,&endp,8);
+ if (*endp) badusage(_("--debug requires an octal argument"));
+}
+
+static void setroot(const struct cmdinfo *cip, const char *value) {
+ char *p;
+ instdir= value;
+ p= m_malloc(strlen(value) + sizeof(ADMINDIR));
+ strcpy(p,value);
+ strcat(p,ADMINDIR);
+ admindir= p;
+}
+
+static void ignoredepends(const struct cmdinfo *cip, const char *value) {
+ char *copy, *p;
+ const char *pnerr;
+ struct packageinlist *ni;
+
+ copy= m_malloc(strlen(value)+2);
+ strcpy(copy,value);
+ copy[strlen(value)+1]= 0;
+ for (p=copy; *p; p++) {
+ if (*p != ',') continue;
+ *p++= 0;
+ if (!*p || *p==',' || p==copy+1)
+ badusage(_("null package name in --ignore-depends comma-separated list `%.250s'"),
+ value);
+ }
+ p= copy;
+ while (*p) {
+ pnerr= illegal_packagename(value,0);
+ if (pnerr) ohshite(_("--ignore-depends requires a legal package name. "
+ "`%.250s' is not; %s"), value, pnerr);
+ ni= m_malloc(sizeof(struct packageinlist));
+ ni->pkg= findpackage(value);
+ ni->next= ignoredependss;
+ ignoredependss= ni;
+ p+= strlen(p)+1;
+ }
+}
+
+static void setinteger(const struct cmdinfo *cip, const char *value) {
+ unsigned long v;
+ char *ep;
+
+ v= strtoul(value,&ep,0);
+ if (*ep || v > INT_MAX)
+ badusage(_("invalid integer for --%s: `%.250s'"),cip->olong,value);
+ *cip->iassignto= v;
+}
+
+static void setpipe(const struct cmdinfo *cip, const char *value) {
+ static struct pipef **lastpipe;
+ unsigned long v;
+ char *ep;
+
+ v= strtoul(value,&ep,0);
+ if (*ep || v > INT_MAX)
+ badusage(_("invalid integer for --%s: `%.250s'"),cip->olong,value);
+
+ lastpipe= cip->parg;
+ if (*lastpipe) {
+ (*lastpipe)->next= nfmalloc(sizeof(struct pipef));
+ *lastpipe= (*lastpipe)->next;
+ } else {
+ *lastpipe= nfmalloc(sizeof(struct pipef));
+ }
+ (*lastpipe)->fd= v;
+ (*lastpipe)->next= NULL;
+}
+
+static void setforce(const struct cmdinfo *cip, const char *value) {
+ const char *comma;
+ size_t l;
+ const struct forceinfo *fip;
+
+ if (!strcmp(value,"help")) {
+ if (printf(_("%s forcing options - control behaviour when problems found:\n\
+ warn but continue: --force-<thing>,<thing>,...\n\
+ stop with error: --refuse-<thing>,<thing>,... | --no-force-<thing>,...\n\
+ Forcing things:\n\
+ all Set all force options\n\
+ auto-select [*] (De)select packages to install (remove) them\n\
+ downgrade [*] Replace a package with a lower version\n\
+ configure-any Configure any package which may help this one\n\
+ hold Process incidental packages even when on hold\n\
+ bad-path PATH is missing important programs, problems likely\n\
+ not-root Try to (de)install things even when not root\n\
+ overwrite Overwrite a file from one package with another\n\
+ overwrite-diverted Overwrite a diverted file with an undiverted version\n\
+ bad-verify Install a package even if it fails authenticity check\n\
+ depends-version [!] Turn dependency version problems into warnings\n\
+ depends [!] Turn all dependency problems into warnings\n\
+ confnew [!] Always use the new config files, don't prompt\n\
+ confold [!] Always use the old config files, don't prompt\n\
+ confdef [!] Use the default option for new config files if one\n\
+ is available, don't prompt. If no default can be found,\n\
+ you will be prompted unless one of the confold or\n\
+ confnew options is also given\n\
+ confmiss [!] Always install missing config files\n\
+ conflicts [!] Allow installation of conflicting packages\n\
+ architecture [!] Process even packages with wrong architecture\n\
+ overwrite-dir [!] Overwrite one package's directory with another's file\n\
+ remove-reinstreq [!] Remove packages which require installation\n\
+ remove-essential [!] Remove an essential package\n\
+\n\
+WARNING - use of options marked [!] can seriously damage your installation.\n\
+Forcing options marked [*] are enabled by default.\n"),
+ DPKG) < 0) werr("stdout");
+ exit(0);
+ }
+
+ for (;;) {
+ comma= strchr(value,',');
+ l= comma ? (int)(comma-value) : strlen(value);
+ for (fip=forceinfos; fip->name; fip++)
+ if (!strncmp(fip->name,value,l) && strlen(fip->name)==l) break;
+ if (!fip->name)
+ if(!strncmp("all",value,l))
+ for (fip=forceinfos; fip->name; fip++)
+ *fip->opt= cip->arg;
+ else
+ badusage(_("unknown force/refuse option `%.*s'"), l<250 ? (int)l : 250, value);
+ else
+ *fip->opt= cip->arg;
+ if (!comma) break;
+ value= ++comma;
+ }
+}
+
+static const char okpassshortopts[]= "D";
+
+void execbackend(const char *const *argv) NONRETURNING;
+void commandfd(const char *const *argv);
+static const struct cmdinfo cmdinfos[]= {
+ /* This table has both the action entries in it and the normal options.
+ * The action entries are made with the ACTION macro, as they all
+ * have a very similar structure.
+ */
+#define ACTION(longopt,shortopt,code,function) \
+ { longopt, shortopt, 0,0,0, setaction, code, 0, (voidfnp)function }
+#define OBSOLETE(longopt,shortopt) \
+ { longopt, shortopt, 0,0,0, setobsolete, 0, 0, 0 }
+#define ACTIONBACKEND(longopt,shortop, backend) \
+ { longopt, shortop, 0,0,0, setaction, 0, (void*)backend, (voidfnp)execbackend }
+
+ ACTION( "install", 'i', act_install, archivefiles ),
+ ACTION( "unpack", 0, act_unpack, archivefiles ),
+ ACTION( "record-avail", 'A', act_avail, archivefiles ),
+ ACTION( "configure", 0, act_configure, packages ),
+ ACTION( "remove", 'r', act_remove, packages ),
+ ACTION( "purge", 'P', act_purge, packages ),
+ ACTIONBACKEND( "listfiles", 'L', DPKGQUERY),
+ ACTIONBACKEND( "status", 's', DPKGQUERY),
+ ACTION( "get-selections", 0, act_getselections, getselections ),
+ ACTION( "set-selections", 0, act_setselections, setselections ),
+ ACTIONBACKEND( "print-avail", 'p', DPKGQUERY),
+ ACTION( "update-avail", 0, act_avreplace, updateavailable ),
+ ACTION( "merge-avail", 0, act_avmerge, updateavailable ),
+ ACTION( "clear-avail", 0, act_avclear, updateavailable ),
+ ACTION( "forget-old-unavail", 0, act_forgetold, forgetold ),
+ ACTION( "audit", 'C', act_audit, audit ),
+ ACTION( "yet-to-unpack", 0, act_unpackchk, unpackchk ),
+ ACTIONBACKEND( "list", 'l', DPKGQUERY),
+ ACTIONBACKEND( "search", 'S', DPKGQUERY),
+ ACTION( "assert-support-predepends", 0, act_assertpredep, assertpredep ),
+ ACTION( "assert-working-epoch", 0, act_assertepoch, assertepoch ),
+ ACTION( "assert-long-filenames", 0, act_assertlongfilenames, assertlongfilenames ),
+ ACTION( "assert-multi-conrep", 0, act_assertmulticonrep, assertmulticonrep ),
+ ACTION( "print-architecture", 0, act_printarch, printarch ),
+ ACTION( "print-installation-architecture", 0, act_printinstarch, printarch ),
+ ACTION( "predep-package", 0, act_predeppackage, predeppackage ),
+ ACTION( "compare-versions", 0, act_cmpversions, cmpversions ),
+/*
+ ACTION( "command-fd", 'c', act_commandfd, commandfd ),
+*/
+
+ { "status-fd", 0, 1, 0, 0, setpipe, 0, &status_pipes },
+ { "pending", 'a', 0, &f_pending, 0, 0, 1 },
+ { "recursive", 'R', 0, &f_recursive, 0, 0, 1 },
+ { "no-act", 0, 0, &f_noact, 0, 0, 1 },
+ { "dry-run", 0, 0, &f_noact, 0, 0, 1 },
+ { "simulate", 0, 0, &f_noact, 0, 0, 1 },
+ { "no-debsig", 0, 0, &f_nodebsig, 0, 0, 1 },
+ { 0, 'G', 0, &fc_downgrade, 0, 0, /* alias for --refuse */ 0 },
+ { "selected-only", 'O', 0, &f_alsoselect, 0, 0, 0 },
+ { "no-also-select", 'N', 0, &f_alsoselect, 0,0,0 /* fixme: remove sometime */ },
+ { "skip-same-version", 'E', 0, &f_skipsame, 0, 0, 1 },
+ { "auto-deconfigure", 'B', 0, &f_autodeconf, 0, 0, 1 },
+ OBSOLETE( "largemem", 0 ),
+ OBSOLETE( "smallmem", 0 ),
+ { "root", 0, 1, 0, 0, setroot },
+ { "abort-after", 0, 1, &errabort, 0, setinteger, 0 },
+ { "admindir", 0, 1, 0, &admindir, 0 },
+ { "instdir", 0, 1, 0, &instdir, 0 },
+ { "ignore-depends", 0, 1, 0, 0, ignoredepends },
+ { "force", 0, 2, 0, 0, setforce, 1 },
+ { "refuse", 0, 2, 0, 0, setforce, 0 },
+ { "no-force", 0, 2, 0, 0, setforce, 0 },
+ { "debug", 'D', 1, 0, 0, setdebug },
+ { "help", 'h', 0, 0, 0, helponly },
+ { "version", 0, 0, 0, 0, versiononly },
+ { "licence",/* UK spelling */ 0,0,0,0, showcopyright },
+ { "license",/* US spelling */ 0,0,0,0, showcopyright },
+ ACTIONBACKEND( "build", 'b', BACKEND),
+ ACTIONBACKEND( "contents", 'c', BACKEND),
+ ACTIONBACKEND( "control", 'e', BACKEND),
+ ACTIONBACKEND( "info", 'I', BACKEND),
+ ACTIONBACKEND( "field", 'f', BACKEND),
+ ACTIONBACKEND( "extract", 'x', BACKEND),
+ ACTIONBACKEND( "new", 0, BACKEND),
+ ACTIONBACKEND( "old", 0, BACKEND),
+ ACTIONBACKEND( "vextract", 'X', BACKEND),
+ ACTIONBACKEND( "fsys-tarfile", 0, BACKEND),
+ { 0, 0 }
+};
+
+void execbackend(const char *const *argv) {
+ char **nargv;
+ int i, argc = 1;
+ const char *const *arg = argv;
+ while(*arg != 0) { arg++; argc++; }
+ nargv= malloc(sizeof(char *) * (argc + 2));
+
+ if (!nargv) ohshite(_("couldn't malloc in execbackend"));
+ nargv[0]= strdup(cipaction->parg);
+ if (!nargv[0]) ohshite(_("couldn't strdup in execbackend"));
+ nargv[1]= malloc(strlen(cipaction->olong) + 3);
+ if (!nargv[1]) ohshite(_("couldn't malloc in execbackend"));
+ strcpy(nargv[1], "--");
+ strcat(nargv[1], cipaction->olong);
+ for (i= 2; i <= argc; i++) {
+ nargv[i]= strdup(argv[i-2]);
+ if (!nargv[i]) ohshite(_("couldn't strdup in execbackend"));
+ }
+ nargv[i]= 0;
+ execvp(cipaction->parg, nargv);
+ ohshite(_("failed to exec %s"),(char *)cipaction->parg);
+}
+void commandfd(const char *const *argv) {
+ jmp_buf ejbuf;
+ struct varbuf linevb;
+ const char * pipein;
+ const char **newargs;
+ char *ptr, *endptr;
+ FILE *in;
+ int c, lno, infd, i, skipchar;
+ static void (*actionfunction)(const char *const *argv);
+
+ if ((pipein= *argv++) == NULL) badusage(_("--command-fd takes 1 argument, not 0"));
+ if (*argv) badusage(_("--command-fd only takes 1 argument"));
+ if ((infd= strtol(pipein, (char **)NULL, 10)) == -1)
+ ohshite(_("invalid number for --command-fd"));
+ if ((in= fdopen(infd, "r")) == NULL)
+ ohshite(_("couldn't open `%i' for stream"), infd);
+
+ if (setjmp(ejbuf)) { /* expect warning about possible clobbering of argv */
+ error_unwind(ehflag_bombout); exit(2);
+ }
+ varbufinit(&linevb);
+ for (;;lno= 0) {
+ const char **oldargs= NULL;
+ int argc= 1, mode= 0;
+ lno= 0;
+ push_error_handler(&ejbuf,print_error_fatal,0);
+
+ do { c= getc(in); if (c == '\n') lno++; } while (c != EOF && isspace(c));
+ if (c == EOF) break;
+ if (c == '#') {
+ do { c= getc(in); if (c == '\n') lno++; } while (c != EOF && c != '\n');
+ continue;
+ }
+ varbufreset(&linevb);
+ do {
+ varbufaddc(&linevb,c);
+ c= getc(in);
+ if (c == '\n') lno++;
+ if (isspace(c)) argc++; /* This isn't fully accurate, but overestimating can't hurt. */
+ } while (c != EOF && c != '\n');
+ if (c == EOF) ohshit(_("unexpected eof before end of line %d"),lno);
+ if (!argc) continue;
+ varbufaddc(&linevb,0);
+printf("line=`%*s'\n",(int)linevb.used,linevb.buf);
+ oldargs= newargs= realloc(oldargs,sizeof(const char *) * (argc + 1));
+ argc= 1;
+ ptr= linevb.buf;
+ endptr= ptr + linevb.used;
+ skipchar= 0;
+ while(ptr < endptr) {
+ if (skipchar) {
+ skipchar= 0;
+ } else if (*ptr == '\\') {
+ memmove(ptr, (ptr+1), (linevb.used-(linevb.buf - ptr)-1));
+ endptr--;
+ skipchar= 1;
+ continue;
+ } else if (isspace(*ptr)) {
+ if (mode == 1) {
+ *ptr= 0;
+ mode= 0;
+ }
+ } else {
+ if (mode == 0) {
+ newargs[argc]= ptr;
+ argc++;
+ mode= 1;
+ }
+ }
+ ptr++;
+ }
+ *ptr= 0;
+ newargs[argc++] = 0;
+
+/* We strdup each argument, but never free it, because the error messages
+ * contain references back to these strings. Freeing them, and reusing
+ * the memory, would make those error messages confusing, to say the
+ * least.
+ */
+ for(i=1;i<argc;i++)
+ if (newargs[i]) newargs[i]=strdup(newargs[i]);
+
+ cipaction= NULL;
+ myopt((const char *const**)&newargs,cmdinfos);
+ if (!cipaction) badusage(_("need an action option"));
+
+ actionfunction= (void (*)(const char* const*))cipaction->farg;
+ actionfunction(newargs);
+ set_error_display(0,0);
+ error_unwind(ehflag_normaltidy);
+ }
+}
+
+
+int main(int argc, const char *const *argv) {
+ jmp_buf ejbuf;
+ static void (*actionfunction)(const char *const *argv);
+
+ standard_startup(&ejbuf, argc, &argv, DPKG, 1, cmdinfos);
+ if (!cipaction) badusage(_("need an action option"));
+
+ setvbuf(stdout,0,_IONBF,0);
+ filesdbinit();
+
+ actionfunction= (void (*)(const char* const*))cipaction->farg;
+
+ actionfunction(argv);
+
+ standard_shutdown(0);
+
+ return reportbroken_retexitstatus();
+}
diff --git a/src/main.h b/src/main.h
new file mode 100644
index 000000000..7c6e33e06
--- /dev/null
+++ b/src/main.h
@@ -0,0 +1,223 @@
+/*
+ * dpkg - main program for package management
+ * main.h - external definitions for this program
+ *
+ * Copyright (C) 1995 Ian Jackson <ian@chiark.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 dpkg; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef MAIN_H
+#define MAIN_H
+
+struct fileinlist; /* these two are defined in filesdb.h */
+struct filenamenode;
+
+struct perpackagestate {
+ enum istobes {
+ itb_normal, itb_remove, itb_installnew, itb_deconfigure, itb_preinstall
+ } istobe;
+
+ /* filelistvalid files meaning
+ * 0 0 not read yet, must do so if want them
+ * 0 !=0 read, but rewritten and now out of
+ * date. If want info must throw away old
+ * and reread file.
+ * 1 !=0 read, all is OK
+ * 1 0 read OK, but, there were no files
+ */
+ int fileslistvalid;
+ struct fileinlist *files;
+ int replacingfilesandsaid;
+};
+
+struct packageinlist {
+ struct packageinlist *next;
+ struct pkginfo *pkg;
+};
+
+enum action { act_unset, act_install, act_unpack, act_avail, act_configure,
+ act_remove, act_purge, act_listpackages, act_avreplace, act_avmerge,
+ act_unpackchk, act_status, act_searchfiles, act_audit, act_listfiles,
+ act_assertpredep, act_printarch, act_predeppackage, act_cmpversions,
+ act_printinstarch, act_compareversions, act_printavail, act_avclear,
+ act_forgetold, act_getselections, act_setselections,
+ act_assertepoch, act_assertlongfilenames, act_assertmulticonrep,
+ act_commandfd };
+
+enum conffopt {
+ cfof_prompt = 001,
+ cfof_keep = 002,
+ cfof_install = 004,
+ cfof_backup = 0100,
+ cfof_newconff = 0200,
+ cfof_isnew = 0400,
+ cfom_main = 007,
+ cfo_keep = cfof_keep,
+ cfo_prompt_keep = cfof_keep | cfof_prompt,
+ cfo_prompt = cfof_prompt,
+ cfo_prompt_install = cfof_prompt | cfof_install,
+ cfo_install = cfof_install,
+ cfo_newconff = cfof_install | cfof_newconff,
+ cfo_identical = cfof_keep
+};
+
+extern int conffoptcells[2][2];
+extern const char *const statusstrings[];
+
+extern const struct cmdinfo *cipaction;
+extern int f_pending, f_recursive, f_alsoselect, f_skipsame, f_noact;
+extern int f_autodeconf, f_largemem, f_nodebsig;
+extern unsigned long f_debug;
+extern int fc_downgrade, fc_configureany, fc_hold, fc_removereinstreq, fc_overwrite;
+extern int fc_removeessential, fc_conflicts, fc_depends, fc_dependsversion;
+extern int fc_autoselect, fc_badpath, fc_overwritediverted, fc_architecture;
+extern int fc_nonroot, fc_overwritedir, fc_conff_new, fc_conff_miss;
+extern int fc_conff_old, fc_conff_def;
+extern int fc_badverify;
+
+extern int errabort;
+extern const char *admindir;
+extern const char *instdir;
+extern struct packageinlist *ignoredependss;
+extern const char architecture[];
+
+/* from filesdb.c */
+
+void filesdbinit(void);
+
+/* from archives.c */
+
+void archivefiles(const char *const *argv);
+void process_archive(const char *filename);
+int wanttoinstall(struct pkginfo *pkg, const struct versionrevision *ver, int saywhy);
+
+/* from update.c */
+
+void forgetold(const char *const *argv);
+void updateavailable(const char *const *argv);
+
+/* from enquiry.c */
+
+void listpackages(const char *const *argv);
+void audit(const char *const *argv);
+void unpackchk(const char *const *argv);
+void showpackages(const char *const *argv);
+void searchfiles(const char *const *argv);
+void enqperpackage(const char *const *argv);
+void assertepoch(const char *const *argv);
+void assertpredep(const char *const *argv);
+void assertlongfilenames(const char *const *argv);
+void assertmulticonrep(const char *const *argv);
+void predeppackage(const char *const *argv);
+void printarch(const char *const *argv);
+void printinstarch(const char *const *argv);
+void cmpversions(const char *const *argv) NONRETURNING;
+
+/* Intended for use as a comparison function for qsort when
+ * sorting an array of pointers to struct pkginfo:
+ */
+int pkglistqsortcmp(const void *a, const void *b);
+
+/* from select.c */
+
+void getselections(const char *const *argv);
+void setselections(const char *const *argv);
+
+/* from packages.c, remove.c and configure.c */
+
+void add_to_queue(struct pkginfo *pkg);
+void process_queue(void);
+void packages(const char *const *argv);
+void removal_bulk(struct pkginfo *pkg);
+int conffderef(struct pkginfo *pkg, struct varbuf *result, const char *in);
+int dependencies_ok(struct pkginfo *pkg, struct pkginfo *removing,
+ struct varbuf *aemsgs);
+
+void deferred_remove(struct pkginfo *pkg);
+void deferred_configure(struct pkginfo *pkg);
+
+extern int queuelen, sincenothing, dependtry;
+
+/* from cleanup.c (most of these are declared in archives.h) */
+
+void cu_prermremove(int argc, void **argv);
+
+/* from errors.c */
+
+extern int nerrs;
+void print_error_perpackage(const char *emsg, const char *arg);
+void forcibleerr(int forceflag, const char *format, ...) PRINTFFORMAT(2,3);
+int reportbroken_retexitstatus(void);
+int skip_due_to_hold(struct pkginfo *pkg);
+
+/* from help.c */
+
+void cu_closefile(int argc, void **argv);
+void cu_closepipe(int argc, void **argv);
+void cu_closedir(int argc, void **argv);
+void cu_closefd(int argc, void **argv);
+
+int ignore_depends(struct pkginfo *pkg);
+int force_depends(struct deppossi *possi);
+int force_conff_new(struct deppossi *possi);
+int force_conff_miss(struct deppossi *possi);
+int force_conflicts(struct deppossi *possi);
+void ensure_package_clientdata(struct pkginfo *pkg);
+const char *pkgadminfile(struct pkginfo *pkg, const char *whichfile);
+void oldconffsetflags(const struct conffile *searchconff);
+void ensure_pathname_nonexisting(const char *pathname);
+int chmodsafe_unlink(const char *pathname); /* chmod 600, then unlink */
+void checkpath(void);
+struct filenamenode *namenodetouse(struct filenamenode*, struct pkginfo*);
+
+/* all ...'s are const char*'s ... */
+int maintainer_script_installed(struct pkginfo *pkg, const char *scriptname,
+ const char *description, ...);
+int maintainer_script_new(const char *pkgname,
+ const char *scriptname, const char *description,
+ const char *cidir, char *cidirrest, ...);
+int maintainer_script_alternative(struct pkginfo *pkg,
+ const char *scriptname, const char *description,
+ const char *cidir, char *cidirrest,
+ const char *ifok, const char *iffallback);
+void clear_istobes(void);
+int isdirectoryinuse(struct filenamenode *namenode, struct pkginfo *pkg);
+
+enum debugflags {
+ dbg_general= 00001,
+ dbg_scripts= 00002,
+ dbg_eachfile= 00010,
+ dbg_eachfiledetail= 00100,
+ dbg_conff= 00020,
+ dbg_conffdetail= 00200,
+ dbg_depcon= 00040,
+ dbg_depcondetail= 00400,
+ dbg_veryverbose= 01000,
+ dbg_stupidlyverbose= 02000,
+};
+
+void debug(int which, const char *fmt, ...) PRINTFFORMAT(2,3);
+void check_libver(void);
+
+/* from depcon.c */
+
+int depisok(struct dependency *dep, struct varbuf *whynot,
+ struct pkginfo **fixbyrm, int allowunconfigd);
+struct cyclesofarlink;
+int findbreakcycle(struct pkginfo *pkg);
+void describedepcon(struct varbuf *addto, struct dependency *dep);
+
+#endif /* MAIN_H */
diff --git a/src/packages.c b/src/packages.c
new file mode 100644
index 000000000..1c81b1884
--- /dev/null
+++ b/src/packages.c
@@ -0,0 +1,417 @@
+/*
+ * dpkg - main program for package management
+ * packages.c - common to actions that process packages
+ *
+ * Copyright (C) 1994,1995 Ian Jackson <ian@chiark.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 dpkg; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <config.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+
+#include <dpkg.h>
+#include <dpkg-db.h>
+#include <myopt.h>
+
+#include "filesdb.h"
+#include "main.h"
+
+struct pkginqueue {
+ struct pkginqueue *next;
+ struct pkginfo *pkg;
+};
+
+static struct pkginqueue *queuehead= 0, **queuetail= &queuehead;
+
+int queuelen=0, sincenothing=0, dependtry=0;
+
+void add_to_queue(struct pkginfo *pkg) {
+ struct pkginqueue *newent;
+
+ newent= m_malloc(sizeof(struct pkginqueue));
+ newent->pkg= pkg;
+ newent->next= 0;
+ *queuetail= newent;
+ queuetail= &newent->next;
+
+ queuelen++;
+}
+
+void packages(const char *const *argv) {
+ struct pkgiterator *it;
+ struct pkginfo *pkg;
+ const char *thisarg;
+ size_t l;
+
+ modstatdb_init(admindir,
+ f_noact ? msdbrw_readonly
+ : fc_nonroot ? msdbrw_write
+ : msdbrw_needsuperuser);
+ checkpath();
+
+ if (f_pending) {
+
+ if (*argv)
+ badusage(_("--%s --pending does not take any non-option arguments"),cipaction->olong);
+
+ it= iterpkgstart();
+ while ((pkg= iterpkgnext(it)) != 0) {
+ switch (cipaction->arg) {
+ case act_configure:
+ if (pkg->status != stat_unpacked && pkg->status != stat_halfconfigured)
+ continue;
+ if (pkg->want != want_install)
+ continue;
+ break;
+ case act_remove:
+ case act_purge:
+ if (pkg->want != want_purge) {
+ if (pkg->want != want_deinstall) continue;
+ if (pkg->status == stat_configfiles) continue;
+ }
+ if (pkg->status == stat_notinstalled)
+ continue;
+ break;
+ default:
+ internerr("unknown action for pending");
+ }
+ add_to_queue(pkg);
+ }
+ iterpkgend(it);
+
+ } else {
+
+ if (!*argv)
+ badusage(_("--%s needs at least one package name argument"), cipaction->olong);
+
+ while ((thisarg= *argv++) != 0) {
+ pkg= findpackage(thisarg);
+ if (pkg->status == stat_notinstalled) {
+ l= strlen(pkg->name);
+ if (l >= sizeof(DEBEXT) && !strcmp(pkg->name+l-sizeof(DEBEXT)+1,DEBEXT))
+ badusage(_("you must specify packages by their own names,"
+ " not by quoting the names of the files they come in"));
+ }
+ add_to_queue(pkg);
+ }
+
+ }
+
+ ensure_diversions();
+
+ process_queue();
+
+ modstatdb_shutdown();
+}
+
+void process_queue(void) {
+ struct pkginqueue *removeent, *rundown;
+ struct pkginfo *volatile pkg;
+ jmp_buf ejbuf;
+ enum istobes istobe= itb_normal;
+
+ clear_istobes();
+
+ switch (cipaction->arg) {
+ case act_configure: case act_install: istobe= itb_installnew; break;
+ case act_remove: case act_purge: istobe= itb_remove; break;
+ default: internerr("unknown action for queue start");
+ }
+ for (rundown= queuehead; rundown; rundown= rundown->next) {
+ ensure_package_clientdata(rundown->pkg);
+ if (rundown->pkg->clientdata->istobe == istobe) {
+ /* Remove it from the queue - this is a second copy ! */
+ switch (cipaction->arg) {
+ case act_configure: case act_remove: case act_purge:
+ printf(_("Package %s listed more than once, only processing once.\n"),
+ rundown->pkg->name);
+ break;
+ case act_install:
+ printf(_("More than one copy of package %s has been unpacked\n"
+ " in this run ! Only configuring it once.\n"),
+ rundown->pkg->name);
+ break;
+ default:
+ internerr("unknown action in duplicate");
+ }
+ rundown->pkg= 0;
+ } else {
+ rundown->pkg->clientdata->istobe= istobe;
+ }
+ }
+
+ while (queuelen) {
+ removeent= queuehead;
+ assert(removeent);
+ queuehead= queuehead->next;
+ queuelen--;
+ if (queuetail == &removeent->next) queuetail= &queuehead;
+
+ pkg= removeent->pkg;
+ free(removeent);
+
+ if (!pkg) continue; /* duplicate, which we removed earlier */
+
+ assert(pkg->status <= stat_configfiles);
+
+ if (setjmp(ejbuf)) {
+ /* give up on it from the point of view of other packages, ie reset istobe */
+ pkg->clientdata->istobe= itb_normal;
+ error_unwind(ehflag_bombout);
+ if (onerr_abort > 0) break;
+ continue;
+ }
+ push_error_handler(&ejbuf,print_error_perpackage,pkg->name);
+ if (sincenothing++ > queuelen*2+2) {
+ dependtry++; sincenothing= 0;
+ assert(dependtry <= 4);
+ }
+ switch (cipaction->arg) {
+ case act_install:
+ /* Don't try to configure pkgs that we've just disappeared. */
+ if (pkg->status == stat_notinstalled)
+ break;
+ case act_configure:
+ deferred_configure(pkg);
+ break;
+ case act_remove: case act_purge:
+ deferred_remove(pkg);
+ break;
+ default:
+ internerr("unknown action in queue");
+ }
+ if (ferror(stdout)) werr("stdout");
+ if (ferror(stderr)) werr("stderr");
+ set_error_display(0,0);
+ error_unwind(ehflag_normaltidy);
+ }
+}
+
+/*** dependency processing - common to --configure and --remove ***/
+
+/*
+ * The algorithm for deciding what to configure or remove first is as
+ * follows:
+ *
+ * Loop through all packages doing a `try 1' until we've been round and
+ * nothing has been done, then do `try 2' and `try 3' likewise.
+ *
+ * When configuring, in each try we check to see whether all
+ * dependencies of this package are done. If so we do it. If some of
+ * the dependencies aren't done yet but will be later we defer the
+ * package, otherwise it is an error.
+ *
+ * When removing, in each try we check to see whether there are any
+ * packages that would have dependencies missing if we removed this
+ * one. If not we remove it now. If some of these packages are
+ * themselves scheduled for removal we defer the package until they
+ * have been done.
+ *
+ * The criteria for satisfying a dependency vary with the various
+ * tries. In try 1 we treat the dependencies as absolute. In try 2 we
+ * check break any cycles in the dependency graph involving the package
+ * we are trying to process before trying to process the package
+ * normally. In try 3 (which should only be reached if
+ * --force-depends-version is set) we ignore version number clauses in
+ * Depends lines. In try 4 (only reached if --force-depends is set) we
+ * say "ok" regardless.
+ *
+ * If we are configuring and one of the packages we depend on is
+ * awaiting configuration but wasn't specified in the argument list we
+ * will add it to the argument list if --configure-any is specified.
+ * In this case we note this as having "done something" so that we
+ * don't needlessly escalate to higher levels of dependency checking
+ * and breaking.
+ */
+
+static int deppossi_ok_found(struct pkginfo *possdependee,
+ struct pkginfo *requiredby,
+ struct pkginfo *removing,
+ struct pkginfo *providing,
+ int *matched,
+ struct deppossi *checkversion,
+ int *interestingwarnings,
+ struct varbuf *oemsgs) {
+ int thisf;
+
+ if (ignore_depends(possdependee)) {
+ debug(dbg_depcondetail," ignoring depended package so ok and found");
+ return 3;
+ }
+ thisf= 0;
+ if (possdependee == removing) {
+ varbufaddstr(oemsgs,_(" Package "));
+ varbufaddstr(oemsgs,possdependee->name);
+ if (providing) {
+ varbufaddstr(oemsgs,_(" which provides "));
+ varbufaddstr(oemsgs,providing->name);
+ }
+ varbufaddstr(oemsgs,_(" is to be removed.\n"));
+ *matched= 1;
+ if (fc_depends) thisf= (dependtry >= 4) ? 2 : 1;
+ debug(dbg_depcondetail," removing possdependee, returning %d",thisf);
+ return thisf;
+ }
+ switch (possdependee->status) {
+ case stat_installed:
+ case stat_unpacked:
+ case stat_halfconfigured:
+ assert(possdependee->installed.valid);
+ if (checkversion && !versionsatisfied(&possdependee->installed,checkversion)) {
+ varbufaddstr(oemsgs,_(" Version of "));
+ varbufaddstr(oemsgs,possdependee->name);
+ varbufaddstr(oemsgs,_(" on system is "));
+ varbufaddstr(oemsgs,versiondescribe(&possdependee->installed.version,
+ vdew_nonambig));
+ varbufaddstr(oemsgs,".\n");
+ assert(checkversion->verrel != dvr_none);
+ if (fc_depends) thisf= (dependtry >= 3) ? 2 : 1;
+ debug(dbg_depcondetail," bad version, returning %d",thisf);
+ (*interestingwarnings)++;
+ return thisf;
+ }
+ if (possdependee->status == stat_installed) {
+ debug(dbg_depcondetail," is installed, ok and found");
+ return 3;
+ }
+ if (possdependee->clientdata &&
+ possdependee->clientdata->istobe == itb_installnew) {
+ debug(dbg_depcondetail," unpacked/halfconfigured, defer");
+ return 1;
+ } else if (!removing && fc_configureany && !skip_due_to_hold(possdependee)) {
+ fprintf(stderr,
+ _("dpkg: also configuring `%s' (required by `%s')\n"),
+ possdependee->name, requiredby->name);
+ add_to_queue(possdependee); sincenothing=0; return 1;
+ } else {
+ varbufaddstr(oemsgs,_(" Package "));
+ varbufaddstr(oemsgs,possdependee->name);
+ if (providing) {
+ varbufaddstr(oemsgs,_(" which provides "));
+ varbufaddstr(oemsgs,providing->name);
+ }
+ varbufaddstr(oemsgs,_(" is not configured yet.\n"));
+ if (fc_depends) thisf= (dependtry >= 4) ? 2 : 1;
+ debug(dbg_depcondetail," not configured/able - returning %d",thisf);
+ (*interestingwarnings)++;
+ return thisf;
+ }
+ default:
+ varbufaddstr(oemsgs,_(" Package "));
+ varbufaddstr(oemsgs,possdependee->name);
+ if (providing) {
+ varbufaddstr(oemsgs,_(" which provides "));
+ varbufaddstr(oemsgs,providing->name);
+ }
+ varbufaddstr(oemsgs,_(" is not installed.\n"));
+ if (fc_depends) thisf= (dependtry >= 4) ? 2 : 1;
+ debug(dbg_depcondetail," not installed - returning %d",thisf);
+ (*interestingwarnings)++;
+ return thisf;
+ }
+}
+
+int dependencies_ok(struct pkginfo *pkg, struct pkginfo *removing,
+ struct varbuf *aemsgs) {
+ int ok, matched, found, thisf, interestingwarnings;
+ struct varbuf oemsgs;
+ struct dependency *dep;
+ struct deppossi *possi, *provider;
+
+ varbufinit(&oemsgs);
+ interestingwarnings= 0;
+ ok= 2; /* 2=ok, 1=defer, 0=halt */
+ debug(dbg_depcon,"checking dependencies of %s (- %s)",
+ pkg->name, removing ? removing->name : "<none>");
+ assert(pkg->installed.valid);
+ for (dep= pkg->installed.depends; dep; dep= dep->next) {
+ if (dep->type != dep_depends && dep->type != dep_predepends) continue;
+ debug(dbg_depcondetail," checking group ...");
+ matched= 0; varbufreset(&oemsgs);
+ found= 0; /* 0=none, 1=defer, 2=withwarning, 3=ok */
+ for (possi= dep->list; found != 3 && possi; possi= possi->next) {
+ debug(dbg_depcondetail," checking possibility -> %s",possi->ed->name);
+ if (possi->cyclebreak) {
+ debug(dbg_depcondetail," break cycle so ok and found");
+ found= 3; break;
+ }
+ thisf= deppossi_ok_found(possi->ed,pkg,removing,0,
+ &matched,possi,&interestingwarnings,&oemsgs);
+ if (thisf > found) found= thisf;
+ if (found != 3 && possi->verrel == dvr_none) {
+ if (possi->ed->installed.valid) {
+ for (provider= possi->ed->installed.depended;
+ found != 3 && provider;
+ provider= provider->nextrev) {
+ if (provider->up->type != dep_provides) continue;
+ debug(dbg_depcondetail," checking provider %s",provider->up->up->name);
+ thisf= deppossi_ok_found(provider->up->up,pkg,removing,possi->ed,
+ &matched,0,&interestingwarnings,&oemsgs);
+ if (thisf > found) found= thisf;
+ }
+ }
+ }
+ debug(dbg_depcondetail," found %d",found);
+ if (thisf > found) found= thisf;
+ }
+ debug(dbg_depcondetail," found %d matched %d",found,matched);
+ if (removing && !matched) continue;
+ switch (found) {
+ case 0:
+ ok= 0;
+ case 2:
+ varbufaddstr(aemsgs, " ");
+ varbufaddstr(aemsgs, pkg->name);
+ varbufaddstr(aemsgs, _(" depends on "));
+ varbufdependency(aemsgs, dep);
+ if (interestingwarnings) {
+ /* Don't print the line about the package to be removed if
+ * that's the only line.
+ */
+ varbufaddstr(aemsgs, _("; however:\n"));
+ varbufaddc(&oemsgs, 0);
+ varbufaddstr(aemsgs, oemsgs.buf);
+ } else {
+ varbufaddstr(aemsgs, ".\n");
+ }
+ break;
+ case 1:
+ if (ok>1) ok= 1;
+ break;
+ case 3:
+ break;
+ default:
+ internerr("unknown value for found");
+ }
+ }
+ if (ok == 0 && (pkg->clientdata && pkg->clientdata->istobe == itb_remove))
+ ok= 1;
+
+ varbuffree(&oemsgs);
+ debug(dbg_depcon,"ok %d msgs >>%.*s<<", ok, (int)aemsgs->used, aemsgs->buf);
+ return ok;
+}
diff --git a/src/processarc.c b/src/processarc.c
new file mode 100644
index 000000000..9de7e9dee
--- /dev/null
+++ b/src/processarc.c
@@ -0,0 +1,1055 @@
+/*
+ * dpkg - main program for package management
+ * processarc.c - the huge function process_archive
+ *
+ * Copyright (C) 1995 Ian Jackson <ian@chiark.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 dpkg; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <config.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <utime.h>
+#include <assert.h>
+#include <time.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <dpkg.h>
+#include <dpkg-db.h>
+#include <tarfn.h>
+#include <myopt.h>
+
+#include "filesdb.h"
+#include "main.h"
+#include "archives.h"
+
+void process_archive(const char *filename) {
+ static const struct TarFunctions tf = {
+ tarfileread,
+ tarobject, tarobject, tarobject, tarobject, tarobject
+ };
+
+ /* These need to be static so that we can pass their addresses to
+ * push_cleanup as arguments to the cu_xxx routines; if an error occurs
+ * we unwind the stack before processing the cleanup list, and these
+ * variables had better still exist ...
+ */
+ static int p1[2];
+ static char cidirtmpnambuf[L_tmpnam+100];
+ static char *cidirbuf=0, *reasmbuf=0;
+ static struct fileinlist *newconffiles, *newfileslist;
+ static enum pkgstatus oldversionstatus;
+ static struct varbuf infofnvb, fnvb, depprobwhy;
+ static struct tarcontext tc;
+
+ int c1, r, admindirlen, i, infodirlen, infodirbaseused, status;
+ struct pkgiterator *it;
+ struct pkginfo *pkg, *otherpkg, *divpkg;
+ char *cidir, *cidirrest, *p;
+ char *pfilenamebuf, conffilenamebuf[MAXCONFFILENAME];
+ const char *pfilename, *newinfofilename;
+ struct fileinlist *newconff, **newconffileslastp;
+ struct fileinlist *cfile;
+ struct reversefilelistiter rlistit;
+ struct conffile *searchconff, **iconffileslastp, *newiconff;
+ struct filepackages *packageslump;
+ struct dependency *dsearch, *newdeplist, **newdeplistlastp;
+ struct dependency *newdep, *dep, *providecheck;
+ struct deppossi *psearch, **newpossilastp, *possi, *newpossi, *pdep;
+ FILE *conff;
+ DIR *dsd;
+ struct filenamenode *namenode;
+ struct dirent *de;
+ struct stat stab;
+ struct packageinlist *deconpil, *deconpiltemp;
+
+ cleanup_pkg_failed= cleanup_conflictor_failed= 0;
+ admindirlen= strlen(admindir);
+
+ for (pfilename= filename ; pfilename && strlen(pfilename) > 30 &&
+ strchr(pfilename,'/') != NULL ; pfilename++ )
+ pfilename= strchr(pfilename,'/');
+ if (pfilename && pfilename != filename) {
+ pfilenamebuf= (char *)nfmalloc(strlen(pfilename)+5);
+ strcpy(pfilenamebuf,".../");
+ strcat(pfilenamebuf,pfilename);
+ pfilename= pfilenamebuf;
+ } else {
+ pfilename= filename;
+ }
+
+ if (stat(filename,&stab)) ohshite(_("cannot access archive"));
+
+ if (!f_noact) {
+ /* We can't `tentatively-reassemble' packages. */
+ if (!reasmbuf) {
+ reasmbuf= m_malloc(admindirlen+sizeof(REASSEMBLETMP)+5);
+ strcpy(reasmbuf,admindir);
+ strcat(reasmbuf,"/" REASSEMBLETMP);
+ }
+ if (unlink(reasmbuf) && errno != ENOENT)
+ ohshite(_("error ensuring `%.250s' doesn't exist"),reasmbuf);
+ push_cleanup(cu_pathname,~0, 0,0, 1,(void*)reasmbuf);
+ c1= m_fork();
+ if (!c1) {
+ execlp(SPLITTER, SPLITTER,"-Qao",reasmbuf,filename,(char*)0);
+ ohshite(_("failed to exec dpkg-split to see if it's part of a multiparter"));
+ }
+ while ((r= waitpid(c1,&status,0)) == -1 && errno == EINTR);
+ if (r != c1) { onerr_abort++; ohshite(_("wait for dpkg-split failed")); }
+ switch (WIFEXITED(status) ? WEXITSTATUS(status) : -1) {
+ case 0:
+ /* It was a part - is it complete ? */
+ if (!stat(reasmbuf,&stab)) { /* Yes. */
+ filename= reasmbuf;
+ pfilename= _("reassembled package file");
+ break;
+ } else if (errno == ENOENT) { /* No. That's it, we skip it. */
+ return;
+ }
+ case 1:
+ /* No, it wasn't a part. */
+ break;
+ default:
+ checksubprocerr(status,SPLITTER,0);
+ }
+ }
+
+ /* Verify the package. */
+ if (!f_nodebsig && (stat(DEBSIGVERIFY, &stab)==0)) {
+ printf(_("Authenticating %s ...\n"), filename);
+ fflush(stdout);
+ c1 = m_fork();
+ if (!c1) {
+ execl(DEBSIGVERIFY, DEBSIGVERIFY, "-q", filename, NULL);
+ ohshite(_("failed to execl debsig-verify"));
+ } else {
+ int status;
+ waitpid(c1, &status, 0);
+ if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0)) {
+ if (! fc_badverify) {
+ ohshit(_("Verification on package %s failed!"), filename);
+ } else {
+ fprintf(stderr, _("Verification on package %s failed,\nbut installing anyway as you request.\n"), filename);
+ }
+ } else {
+ printf(_("passed\n"));
+ }
+ }
+ }
+
+
+ if (f_noact) {
+ cidir= cidirtmpnambuf;
+ /* We use tmpnam here, not to get a unique filename, but to get a unique directory. */
+ if (!tmpnam(cidir)) ohshite(_("unable to get unique filename for control info"));
+ strcat(cidir,"/");
+ } else {
+ /* We want it to be on the same filesystem so that we can
+ * use rename(2) to install the postinst &c.
+ */
+ if (!cidirbuf)
+ cidirbuf= m_malloc(admindirlen+sizeof(CONTROLDIRTMP)+MAXCONTROLFILENAME+10);
+ cidir= cidirbuf;
+ strcpy(cidir,admindir);
+ strcat(cidir, "/" CONTROLDIRTMP);
+ }
+ cidirrest= cidir + strlen(cidir);
+
+ assert(*cidir && cidirrest[-1] == '/'); cidirrest[-1]= 0;
+ ensure_pathname_nonexisting(cidir); cidirrest[-1]= '/';
+
+ push_cleanup(cu_cidir,~0, 0,0, 2,(void*)cidir,(void*)cidirrest);
+ c1= m_fork();
+ if (!c1) {
+ cidirrest[-1]= 0;
+ execlp(BACKEND, BACKEND,"--control",filename,cidir,(char*)0);
+ ohshite(_("failed to exec dpkg-deb to extract control information"));
+ }
+ waitsubproc(c1,BACKEND " --control",0);
+ strcpy(cidirrest,CONTROLFILE);
+
+ parsedb(cidir, pdb_recordavailable|pdb_rejectstatus|pdb_ignorefiles|pdb_weakclassification,
+ &pkg,NULL,NULL);
+ if (!pkg->files) {
+ pkg->files= nfmalloc(sizeof(struct filedetails));
+ pkg->files->next= 0;
+ pkg->files->name= pkg->files->msdosname= pkg->files->md5sum= 0;
+ }
+ /* Always nfmalloc. Otherwise, we may overwrite some other field(like md5sum). */
+ pkg->files->size= nfmalloc(30);
+ sprintf(pkg->files->size,"%lu",(unsigned long)stab.st_size);
+
+ if (cipaction->arg == act_avail) {
+ printf(_("Recorded info about %s from %s.\n"),pkg->name,pfilename);
+ pop_cleanup(ehflag_normaltidy);
+ return;
+ }
+
+ if (pkg->available.architecture && *pkg->available.architecture &&
+ strcmp(pkg->available.architecture,"all") &&
+ strcmp(pkg->available.architecture,architecture))
+ forcibleerr(fc_architecture,
+ _("package architecture (%s) does not match system (%s)"),
+ pkg->available.architecture,architecture);
+
+ if (!pkg->installed.valid) blankpackageperfile(&pkg->installed);
+ assert(pkg->available.valid);
+
+ for (deconpil= deconfigure;
+ deconpil;
+ deconpil= deconpiltemp) {
+ deconpiltemp= deconpil->next;
+ free(deconpil);
+ }
+ deconfigure= 0;
+ clear_istobes();
+
+ if (!wanttoinstall(pkg,&pkg->available.version,1)) {
+ pop_cleanup(ehflag_normaltidy);
+ return;
+ }
+
+ /* Check if anything is installed that we conflict with, or not installed
+ * that we need */
+ pkg->clientdata->istobe= itb_installnew;
+
+ for (dsearch= pkg->available.depends; dsearch; dsearch= dsearch->next) {
+ switch (dsearch->type) {
+ case dep_conflicts:
+ /* Look for things we conflict with. */
+ check_conflict(dsearch, pkg, pfilename);
+ break;
+ case dep_provides:
+ /* Look for things that conflict with what we provide. */
+ if (dsearch->list->ed->installed.valid) {
+ for (psearch= dsearch->list->ed->installed.depended;
+ psearch;
+ psearch= psearch->nextrev) {
+ if (psearch->up->type != dep_conflicts) continue;
+ check_conflict(psearch->up, pkg, pfilename);
+ }
+ }
+ break;
+ case dep_suggests:
+ case dep_recommends:
+ case dep_depends:
+ case dep_replaces:
+ case dep_enhances:
+ /* Ignore these here. */
+ break;
+ case dep_predepends:
+ if (!depisok(dsearch,&depprobwhy,0,1)) {
+ varbufaddc(&depprobwhy,0);
+ fprintf(stderr, _("dpkg: regarding %s containing %s, pre-dependency problem:\n%s"),
+ pfilename, pkg->name, depprobwhy.buf);
+ if (!force_depends(dsearch->list))
+ ohshit(_("pre-dependency problem - not installing %.250s"),pkg->name);
+ fprintf(stderr, _("dpkg: warning - ignoring pre-dependency problem !\n"));
+ }
+ }
+ }
+ /* Look for things that conflict with us. */
+ for (psearch= pkg->installed.depended; psearch; psearch= psearch->nextrev) {
+ if (psearch->up->type != dep_conflicts) continue;
+ check_conflict(psearch->up, pkg, pfilename);
+ }
+
+ ensure_allinstfiles_available();
+ filesdbinit();
+
+ if (pkg->status != stat_notinstalled && pkg->status != stat_configfiles)
+ printf(_("Preparing to replace %s %s (using %s) ...\n"),
+ pkg->name,
+ versiondescribe(&pkg->installed.version,vdew_nonambig),
+ pfilename);
+ else
+ printf(_("Unpacking %s (from %s) ...\n"),pkg->name,pfilename);
+
+ if (f_noact) {
+ pop_cleanup(ehflag_normaltidy);
+ return;
+ }
+
+ /* OK, we're going ahead. First we read the conffiles, and copy the
+ * hashes across.
+ */
+ newconffiles= 0; newconffileslastp= &newconffiles;
+ push_cleanup(cu_fileslist,~0, 0, 0, 0);
+ strcpy(cidirrest,CONFFILESFILE);
+ conff= fopen(cidir,"r");
+ if (conff) {
+ push_cleanup(cu_closefile,ehflag_bombout, 0,0, 1,(void*)conff);
+ while (fgets(conffilenamebuf,MAXCONFFILENAME-2,conff)) {
+ p= conffilenamebuf + strlen(conffilenamebuf);
+ assert(p != conffilenamebuf);
+ if (p[-1] != '\n')
+ ohshit(_("name of conffile (starting `%.250s') is too long (>%d characters)"),
+ conffilenamebuf, MAXCONFFILENAME);
+ while (p > conffilenamebuf && isspace(p[-1])) --p;
+ if (p == conffilenamebuf) continue;
+ *p= 0;
+ newconff= m_malloc(sizeof(struct fileinlist));
+ newconff->next= 0;
+ newconff->namenode= findnamenode(conffilenamebuf, 0);
+ *newconffileslastp= newconff;
+ newconffileslastp= &newconff->next;
+ newconff->namenode->oldhash= NEWCONFFILEFLAG;
+ /* Let's see if any packages have this file. If they do we
+ * check to see if they listed it as a conffile, and if they did
+ * we copy the hash across. Since (for plain file conffiles,
+ * which is the only kind we are supposed to have) there will
+ * only be one package which `has' the file, this will usually
+ * mean we only look in the package which we're installing now.
+ * The `conffiles' data in the status file is ignored when a
+ * package isn't also listed in the file ownership database as
+ * having that file. If several packages are listed as owning
+ * the file we pick one at random.
+ */
+ searchconff= 0;
+ for (packageslump= newconff->namenode->packages;
+ packageslump;
+ packageslump= packageslump->more) {
+ for (i=0; i < PERFILEPACKAGESLUMP && packageslump->pkgs[i]; i++) {
+ otherpkg= packageslump->pkgs[i];
+ debug(dbg_conffdetail,"process_archive conffile `%s' in package %s - conff ?",
+ newconff->namenode->name,otherpkg->name);
+ for (searchconff= otherpkg->installed.conffiles;
+ searchconff && strcmp(newconff->namenode->name,searchconff->name);
+ searchconff= searchconff->next)
+ debug(dbg_conffdetail,
+ "process_archive conffile `%s' in package %s - conff ? not `%s'",
+ newconff->namenode->name,otherpkg->name,searchconff->name);
+ if (searchconff) {
+ debug(dbg_conff,"process_archive conffile `%s' package=%s %s hash=%s",
+ newconff->namenode->name,otherpkg->name,
+ otherpkg == pkg ? "same" : "different!",
+ searchconff->hash);
+ if (otherpkg == pkg) goto xit_conff_hashcopy_srch;
+ }
+ }
+ }
+ xit_conff_hashcopy_srch:
+ if (searchconff) {
+ newconff->namenode->oldhash= searchconff->hash;
+ } else {
+ debug(dbg_conff,"process_archive conffile `%s' no package, no hash",
+ newconff->namenode->name);
+ }
+ newconff->namenode->flags |= fnnf_new_conff;
+ }
+ if (ferror(conff)) ohshite(_("read error in %.250s"),cidir);
+ pop_cleanup(ehflag_normaltidy); /* conff= fopen() */
+ if (fclose(conff)) ohshite(_("error closing %.250s"),cidir);
+ } else {
+ if (errno != ENOENT) ohshite(_("error trying to open %.250s"),cidir);
+ }
+
+ /* All the old conffiles are marked with a flag, so that we don't delete
+ * them if they seem to disappear completely.
+ */
+ oldconffsetflags(pkg->installed.conffiles);
+ for (i = 0 ; i < cflict_index ; i++) {
+ oldconffsetflags(conflictor[i]->installed.conffiles);
+ }
+
+ oldversionstatus= pkg->status;
+
+ assert(oldversionstatus <= stat_configfiles);
+ debug(dbg_general,"process_archive oldversionstatus=%s",
+ statusstrings[oldversionstatus]);
+
+ if (oldversionstatus == stat_halfconfigured || oldversionstatus == stat_installed) {
+ pkg->eflag |= eflagf_reinstreq;
+ pkg->status= stat_halfconfigured;
+ modstatdb_note(pkg);
+ push_cleanup(cu_prermupgrade,~ehflag_normaltidy, 0,0, 1,(void*)pkg);
+ maintainer_script_alternative(pkg, PRERMFILE, "pre-removal", cidir, cidirrest,
+ "upgrade", "failed-upgrade");
+ pkg->status= stat_unpacked;
+ oldversionstatus= stat_unpacked;
+ modstatdb_note(pkg);
+ }
+
+ for (i = 0 ; i < cflict_index ; i++) {
+ if (!(conflictor[i]->status == stat_halfconfigured ||
+ conflictor[i]->status == stat_installed)) continue;
+ for (deconpil= deconfigure; deconpil; deconpil= deconpil->next) {
+ printf(_("De-configuring %s, so that we can remove %s ...\n"),
+ deconpil->pkg->name, conflictor[i]->name);
+ deconpil->pkg->status= stat_halfconfigured;
+ modstatdb_note(deconpil->pkg);
+ /* This means that we *either* go and run postinst abort-deconfigure,
+ * *or* queue the package for later configure processing, depending
+ * on which error cleanup route gets taken.
+ */
+ push_cleanup(cu_prermdeconfigure,~ehflag_normaltidy,
+ ok_prermdeconfigure,ehflag_normaltidy,
+ 3,(void*)deconpil->pkg,
+ (void*)conflictor[i],(void*)pkg);
+ maintainer_script_installed(deconpil->pkg, PRERMFILE, "pre-removal",
+ "deconfigure", "in-favour", pkg->name,
+ versiondescribe(&pkg->available.version,
+ vdew_nonambig),
+ "removing", conflictor[i]->name,
+ versiondescribe(&conflictor[i]->installed.version,
+ vdew_nonambig),
+ (char*)0);
+ }
+ conflictor[i]->status= stat_halfconfigured;
+ modstatdb_note(conflictor[i]);
+ push_cleanup(cu_prerminfavour,~ehflag_normaltidy, 0,0,
+ 2,(void*)conflictor[i],(void*)pkg);
+ maintainer_script_installed(conflictor[i], PRERMFILE, "pre-removal",
+ "remove", "in-favour", pkg->name,
+ versiondescribe(&pkg->available.version,
+ vdew_nonambig),
+ (char*)0);
+ conflictor[i]->status= stat_halfinstalled;
+ modstatdb_note(conflictor[i]);
+ }
+
+ pkg->eflag |= eflagf_reinstreq;
+ if (pkg->status == stat_notinstalled)
+ pkg->installed.version= pkg->available.version;
+ pkg->status= stat_halfinstalled;
+ modstatdb_note(pkg);
+ if (oldversionstatus == stat_notinstalled) {
+ push_cleanup(cu_preinstverynew,~ehflag_normaltidy, 0,0,
+ 3,(void*)pkg,(void*)cidir,(void*)cidirrest);
+ maintainer_script_new(pkg->name, PREINSTFILE, "pre-installation", cidir, cidirrest,
+ "install", (char*)0);
+ } else if (oldversionstatus == stat_configfiles) {
+ push_cleanup(cu_preinstnew,~ehflag_normaltidy, 0,0,
+ 3,(void*)pkg,(void*)cidir,(void*)cidirrest);
+ maintainer_script_new(pkg->name, PREINSTFILE, "pre-installation", cidir, cidirrest,
+ "install", versiondescribe(&pkg->installed.version,
+ vdew_nonambig),
+ (char*)0);
+ } else {
+ push_cleanup(cu_preinstupgrade,~ehflag_normaltidy, 0,0,
+ 4,(void*)pkg,(void*)cidir,(void*)cidirrest,(void*)&oldversionstatus);
+ maintainer_script_new(pkg->name, PREINSTFILE, "pre-installation", cidir, cidirrest,
+ "upgrade", versiondescribe(&pkg->installed.version,
+ vdew_nonambig),
+ (char*)0);
+ printf(_("Unpacking replacement %.250s ...\n"),pkg->name);
+ }
+
+ /*
+ * Now we unpack the archive, backing things up as we go.
+ * For each file, we check to see if it already exists.
+ * There are several possibilities:
+ * + We are trying to install a non-directory ...
+ * - It doesn't exist. In this case we simply extract it.
+ * - It is a plain file, device, symlink, &c. We do an `atomic
+ * overwrite' using link() and rename(), but leave a backup copy.
+ * Later, when we delete the backup, we remove it from any other
+ * packages' lists.
+ * - It is a directory. In this case it depends on whether we're
+ * trying to install a symlink or something else.
+ * = If we're not trying to install a symlink we move the directory
+ * aside and extract the node. Later, when we recursively remove
+ * the backed-up directory, we remove it from any other packages'
+ * lists.
+ * = If we are trying to install a symlink we do nothing - ie,
+ * dpkg will never replace a directory tree with a symlink. This
+ * is to avoid embarrassing effects such as replacing a directory
+ * tree with a link to a link to the original directory tree.
+ * + We are trying to install a directory ...
+ * - It doesn't exist. We create it with the appropriate modes.
+ * - It exists as a directory or a symlink to one. We do nothing.
+ * - It is a plain file or a symlink (other than to a directory).
+ * We move it aside and create the directory. Later, when we
+ * delete the backup, we remove it from any other packages' lists.
+ *
+ * Install non-dir Install symlink Install dir
+ * Exists not X X X
+ * File/node/symlink LXR LXR BXR
+ * Directory BXR - -
+ *
+ * X: extract file/node/link/directory
+ * LX: atomic overwrite leaving backup
+ * B: ordinary backup
+ * R: later remove from other packages' lists
+ * -: do nothing
+ *
+ * After we've done this we go through the remaining things in the
+ * lists of packages we're trying to remove (including the old
+ * version of the current package). This happens in reverse order,
+ * so that we process files before the directories (or symlinks-to-
+ * directories) containing them.
+ * + If the thing is a conffile then we leave it alone for the purge
+ * operation.
+ * + Otherwise, there are several possibilities too:
+ * - The listed thing does not exist. We ignore it.
+ * - The listed thing is a directory or a symlink to a directory.
+ * We delete it only if it isn't listed in any other package.
+ * - The listed thing is not a directory, but was part of the package
+ * that was upgraded, we check to make sure the files aren't the
+ * same ones from the old package by checking dev/inode
+ * - The listed thing is not a directory or a symlink to one (ie,
+ * it's a plain file, device, pipe, &c, or a symlink to one, or a
+ * dangling symlink). We delete it.
+ * The removed packages' list becomes empty (of course, the new
+ * version of the package we're installing will have a new list,
+ * which replaces the old version's list).
+ *
+ * If at any stage we remove a file from a package's list, and the
+ * package isn't one we're already processing, and the package's
+ * list becomes empty as a result, we `vanish' the package. This
+ * means that we run its postrm with the `disappear' argument, and
+ * put the package in the `not-installed' state. Its conffiles are
+ * ignored and forgotten about.
+ *
+ * NOTE THAT THE OLD POSTRM IS RUN AFTER THE NEW PREINST, since the
+ * files get replaced `as we go'.
+ */
+
+ m_pipe(p1);
+ push_cleanup(cu_closepipe,ehflag_bombout, 0,0, 1,(void*)&p1[0]);
+ c1= m_fork();
+ if (!c1) {
+ m_dup2(p1[1],1); close(p1[0]); close(p1[1]);
+ execlp(BACKEND, BACKEND, "--fsys-tarfile", filename, (char*)0);
+ ohshite(_("unable to exec dpkg-deb to get filesystem archive"));
+ }
+ close(p1[1]);
+
+ newfileslist= 0; tc.newfilesp= &newfileslist;
+ push_cleanup(cu_fileslist,~0, 0, 0, 0);
+ tc.pkg= pkg;
+ tc.backendpipe= p1[0];
+ push_cleanup(cu_closefd,~ehflag_bombout, 0,0, 1,&tc.backendpipe);
+
+ r= TarExtractor((void*)&tc, &tf);
+ if (r) {
+ if (errno) {
+ ohshite(_("error reading dpkg-deb tar output"));
+ } else {
+ ohshite(_("corrupted filesystem tarfile - corrupted package archive"));
+ }
+ }
+ fd_null_copy(tc.backendpipe,-1,_("dpkg-deb: zap possible trailing zeros"));
+ close(tc.backendpipe);
+ waitsubproc(c1,BACKEND " --fsys-tarfile",PROCPIPE);
+
+ if (oldversionstatus == stat_halfinstalled || oldversionstatus == stat_unpacked) {
+ /* Packages that were in `installed' and `postinstfailed' have been reduced
+ * to `unpacked' by now, by the running of the prerm script.
+ */
+ pkg->status= stat_halfinstalled;
+ modstatdb_note(pkg);
+ push_cleanup(cu_postrmupgrade,~ehflag_normaltidy, 0,0, 1,(void*)pkg);
+ maintainer_script_alternative(pkg, POSTRMFILE, "post-removal", cidir, cidirrest,
+ "upgrade", "failed-upgrade");
+ }
+
+ /* If anything goes wrong while tidying up it's a bit late to do
+ * anything about it. However, we don't install the new status
+ * info yet, so that a future dpkg installation will put everything
+ * right (we hope).
+ *
+ * If something does go wrong later the `conflictor' package will be
+ * left in the `removal_failed' state. Removing or installing it
+ * will be impossible if it was required because of the conflict with
+ * the package we're installing now and (presumably) the dependency
+ * by other packages. This means that the files it contains in
+ * common with this package will hang around until we successfully
+ * get this package installed, after which point we can trust the
+ * conflicting package's file list, which will have been updated to
+ * remove any files in this package.
+ */
+ push_checkpoint(~ehflag_bombout, ehflag_normaltidy);
+
+ /* Now we delete all the files that were in the old version of
+ * the package only, except (old or new) conffiles, which we leave
+ * alone.
+ */
+ reversefilelist_init(&rlistit,pkg->clientdata->files);
+ while ((namenode= reversefilelist_next(&rlistit))) {
+ if ((namenode->flags & fnnf_old_conff) ||
+ (namenode->flags & fnnf_new_conff) ||
+ (namenode->flags & fnnf_new_inarchive))
+ continue;
+ if (isdirectoryinuse(namenode,pkg)) continue;
+ fnamevb.used= fnameidlu;
+ varbufaddstr(&fnamevb, namenodetouse(namenode,pkg)->name);
+ varbufaddc(&fnamevb,0);
+ if (!rmdir(fnamevb.buf)) continue;
+ if (errno == ENOENT || errno == ELOOP) continue;
+ if (errno == ENOTDIR) {
+ /* Ok, it's an old file, but is it really not in the new package?
+ * We need to check to make sure, so we stat the file, then compare
+ * it to the new list. If we find a dev/inode match, we assume they
+ * are the same file, and leave it alone. NOTE: we don't check in
+ * other packages for sanity reasons (we don't want to stat _all_
+ * the files on the system).
+ *
+ * We run down the list of _new_ files in this package. This keeps
+ * the process a little leaner. We are only worried about new ones
+ * since ones that stayed the same don't really apply here.
+ */
+ struct stat oldfs;
+ int donotrm = 0;
+ /* If we can't stat the old or new file, or it's a directory,
+ * we leave it up to the normal code
+ */
+ debug(dbg_eachfile, "process_archive: checking %s for same files on "
+ "upgrade/downgrade", fnamevb.buf);
+ if (!lstat(fnamevb.buf, &oldfs) && !S_ISDIR(oldfs.st_mode)) {
+ for (cfile = newfileslist; cfile; cfile = cfile->next) {
+ if (!cfile->namenode->filestat) {
+ cfile->namenode->filestat = (struct stat *) nfmalloc(sizeof(struct stat));
+ if (lstat(cfile->namenode->name, cfile->namenode->filestat)) {
+ cfile->namenode->filestat= 0;
+ continue;
+ }
+ }
+ if (S_ISDIR(cfile->namenode->filestat->st_mode))
+ continue;
+ if (oldfs.st_dev == cfile->namenode->filestat->st_dev &&
+ oldfs.st_ino == cfile->namenode->filestat->st_ino) {
+ donotrm = 1;
+ debug(dbg_eachfile, "process_archive: not removing %s, since it matches %s",
+ fnamevb.buf, cfile->namenode->name);
+ }
+ }
+ } else
+ debug(dbg_eachfile, "process_archive: could not stat %s, skipping", fnamevb.buf);
+ if (donotrm) continue;
+ {
+ /*
+ * If file to remove is a device or s[gu]id, change its mode
+ * so that a malicious user cannot use it even if it's linked
+ * to another file.
+ */
+ struct stat stat_buf;
+ if (stat(fnamevb.buf,&stat_buf)==0) {
+ if (S_ISCHR(stat_buf.st_mode) || S_ISBLK(stat_buf.st_mode))
+ chmod(fnamevb.buf, 0);
+ if (stat_buf.st_mode & (S_ISUID|S_ISGID))
+ chmod(fnamevb.buf, stat_buf.st_mode & ~(S_ISUID|S_ISGID));
+ }
+ }
+ if (!unlink(fnamevb.buf)) continue;
+ if (errno == ENOTDIR) continue;
+ }
+ fprintf(stderr,
+ _("dpkg: warning - unable to delete old file `%.250s': %s\n"),
+ namenode->name, strerror(errno));
+ }
+
+ /* OK, now we can write the updated files-in-this package list,
+ * since we've done away (hopefully) with all the old junk.
+ */
+ write_filelist_except(pkg,newfileslist,0);
+
+ /* We also install the new maintainer scripts, and any other
+ * cruft that may have come along with the package. First
+ * we go through the existing scripts replacing or removing
+ * them as appropriate; then we go through the new scripts
+ * (any that are left) and install them.
+ */
+ debug(dbg_general, "process_archive updating info directory");
+ varbufreset(&infofnvb);
+ varbufaddstr(&infofnvb,admindir);
+ varbufaddstr(&infofnvb,"/" INFODIR "/");
+ infodirlen= infofnvb.used;
+ varbufaddc(&infofnvb,0);
+ dsd= opendir(infofnvb.buf);
+ if (!dsd) ohshite(_("cannot read info directory"));
+ push_cleanup(cu_closedir,~0, 0,0, 1,(void*)dsd);
+ while ((de= readdir(dsd)) != 0) {
+ debug(dbg_veryverbose, "process_archive info file `%s'", de->d_name);
+ if (de->d_name[0] == '.') continue; /* ignore dotfiles, including `.' and `..' */
+ p= strrchr(de->d_name,'.'); if (!p) continue; /* ignore anything odd */
+ if (strlen(pkg->name) != (size_t)(p-de->d_name) ||
+ strncmp(de->d_name,pkg->name,p-de->d_name)) continue;
+ debug(dbg_stupidlyverbose, "process_archive info this pkg");
+ /* Right do we have one ? */
+ p++; /* skip past the full stop */
+ if (!strcmp(p,LISTFILE)) continue; /* We do the list separately */
+ if (strlen(p) > MAXCONTROLFILENAME)
+ ohshit(_("old version of package has overly-long info file name starting `%.250s'"),
+ de->d_name);
+ infofnvb.used= infodirlen;
+ varbufaddstr(&infofnvb,de->d_name);
+ varbufaddc(&infofnvb,0);
+ strcpy(cidirrest,p);
+ if (!rename(cidir,infofnvb.buf)) {
+ debug(dbg_scripts, "process_archive info installed %s as %s",
+ cidir, infofnvb.buf);
+ } else if (errno == ENOENT) {
+ /* Right, no new version. */
+ if (unlink(infofnvb.buf))
+ ohshite(_("unable to remove obsolete info file `%.250s'"),infofnvb.buf);
+ debug(dbg_scripts, "process_archive info unlinked %s",infofnvb.buf);
+ } else {
+ ohshite(_("unable to install (supposed) new info file `%.250s'"),cidir);
+ }
+ }
+ pop_cleanup(ehflag_normaltidy); /* closedir */
+
+ *cidirrest= 0; /* the directory itself */
+ dsd= opendir(cidir);
+ if (!dsd) ohshite(_("unable to open temp control directory"));
+ push_cleanup(cu_closedir,~0, 0,0, 1,(void*)dsd);
+ while ((de= readdir(dsd))) {
+ if (strchr(de->d_name,'.')) {
+ debug(dbg_scripts,"process_archive tmp.ci script/file `%s' contains dot",
+ de->d_name);
+ continue;
+ }
+ if (strlen(de->d_name) > MAXCONTROLFILENAME)
+ ohshit(_("package contains overly-long control info file name (starting `%.50s')"),
+ de->d_name);
+ strcpy(cidirrest,de->d_name);
+ /* First we check it's not a directory. */
+ if (!rmdir(cidir))
+ ohshit(_("package control info contained directory `%.250s'"),cidir);
+ else if (errno != ENOTDIR)
+ ohshite(_("package control info rmdir of `%.250s' didn't say not a dir"),de->d_name);
+ if (!strcmp(de->d_name,CONTROLFILE)) {
+ debug(dbg_scripts,"process_archive tmp.ci script/file `%s' is control",cidir);
+ continue; /* ignore the control file */
+ }
+ if (!strcmp(de->d_name,LISTFILE)) {
+ fprintf(stderr, _("dpkg: warning - package %s"
+ " contained list as info file"), pkg->name);
+ continue;
+ }
+ /* Right, install it */
+ newinfofilename= pkgadminfile(pkg,de->d_name);
+ if (rename(cidir,newinfofilename))
+ ohshite(_("unable to install new info file `%.250s' as `%.250s'"),
+ cidir,newinfofilename);
+ debug(dbg_scripts,"process_archive tmp.ci script/file `%s' installed as `%s'",
+ cidir, newinfofilename);
+ }
+ pop_cleanup(ehflag_normaltidy); /* closedir */
+
+ /* Update the status database.
+ * This involves copying each field across from the `available'
+ * to the `installed' half of the pkg structure.
+ * For some of the fields we have to do a complicated construction
+ * operation; for others we can just copy the value.
+ * We tackle the fields in the order they appear, so that
+ * we don't miss any out :-).
+ * At least we don't have to copy any strings that are referred
+ * to, because these are never modified and never freed.
+ */
+
+ /* The dependencies are the most difficult. We have to build
+ * a whole new forward dependency tree. At least the reverse
+ * links (linking our deppossi's into the reverse chains)
+ * can be done by copy_dependency_links.
+ */
+ newdeplist= 0; newdeplistlastp= &newdeplist;
+ for (dep= pkg->available.depends; dep; dep= dep->next) {
+ newdep= nfmalloc(sizeof(struct dependency));
+ newdep->up= pkg;
+ newdep->next= 0;
+ newdep->list= 0; newpossilastp= &newdep->list;
+ for (possi= dep->list; possi; possi= possi->next) {
+ newpossi= nfmalloc(sizeof(struct deppossi));
+ newpossi->up= newdep;
+ newpossi->ed= possi->ed;
+ newpossi->next= 0; newpossi->nextrev= newpossi->backrev= 0;
+ newpossi->verrel= possi->verrel;
+ if (possi->verrel != dvr_none) newpossi->version= possi->version;
+ newpossi->cyclebreak= 0;
+ *newpossilastp= newpossi;
+ newpossilastp= &newpossi->next;
+ }
+ newdep->type= dep->type;
+ *newdeplistlastp= newdep;
+ newdeplistlastp= &newdep->next;
+ }
+ /* Right, now we've replicated the forward tree, we
+ * get copy_dependency_links to remove all the old dependency
+ * structures from the reverse links and add the new dependency
+ * structures in instead. It also copies the new dependency
+ * structure pointer for this package into the right field.
+ */
+ copy_dependency_links(pkg,&pkg->installed.depends,newdeplist,0);
+
+ /* The `depended' pointer in the structure doesn't represent anything
+ * that is actually specified by this package - it's there so we
+ * can find out what other packages refer to this one. So,
+ * we don't copy it. We go straight on to copy the text fields.
+ */
+ pkg->installed.essential= pkg->available.essential;
+ pkg->installed.description= pkg->available.description;
+ pkg->installed.maintainer= pkg->available.maintainer;
+ pkg->installed.source= pkg->available.source;
+ pkg->installed.architecture= pkg->available.architecture;
+ pkg->installed.installedsize= pkg->available.installedsize;
+ pkg->installed.version= pkg->available.version;
+ pkg->installed.origin = pkg->available.origin;
+ pkg->installed.bugs = pkg->available.bugs;
+
+ /* We have to generate our own conffiles structure. */
+ pkg->installed.conffiles= 0; iconffileslastp= &pkg->installed.conffiles;
+ for (cfile= newconffiles; cfile; cfile= cfile->next) {
+ newiconff= nfmalloc(sizeof(struct conffile));
+ newiconff->next= 0;
+ newiconff->name= nfstrsave(cfile->namenode->name);
+ newiconff->hash= nfstrsave(cfile->namenode->oldhash);
+ *iconffileslastp= newiconff;
+ iconffileslastp= &newiconff->next;
+ }
+
+ /* We can just copy the arbitrary fields list, because it is
+ * never even rearragned. Phew !
+ */
+ pkg->installed.arbs= pkg->available.arbs;
+
+ /* Check for disappearing packages:
+ * We go through all the packages on the system looking for ones
+ * whose files are entirely part of the one we've just unpacked
+ * (and which actually *have* some files!).
+ *
+ * Any that we find are removed - we run the postrm with `disappear'
+ * as an argument, and remove their info/... files and status info.
+ * Conffiles are ignored (the new package had better do something
+ * with them !).
+ */
+ it= iterpkgstart();
+ while ((otherpkg= iterpkgnext(it)) != 0) {
+ ensure_package_clientdata(otherpkg);
+ if (otherpkg == pkg ||
+ otherpkg->status == stat_notinstalled ||
+ otherpkg->status == stat_configfiles ||
+ otherpkg->clientdata->istobe == itb_remove ||
+ !otherpkg->clientdata->files) continue;
+ debug(dbg_veryverbose, "process_archive checking disappearance %s",otherpkg->name);
+ assert(otherpkg->clientdata->istobe == itb_normal ||
+ otherpkg->clientdata->istobe == itb_deconfigure);
+ for (cfile= otherpkg->clientdata->files;
+ cfile && !strcmp(cfile->namenode->name,"/.");
+ cfile= cfile->next);
+ if (!cfile) {
+ debug(dbg_stupidlyverbose, "process_archive no non-root, no disappear");
+ continue;
+ }
+ for (cfile= otherpkg->clientdata->files;
+ cfile && !filesavespackage(cfile,otherpkg,pkg);
+ cfile= cfile->next);
+ if (cfile) continue;
+
+ /* So dependency things will give right answers ... */
+ otherpkg->clientdata->istobe= itb_remove;
+ debug(dbg_veryverbose, "process_archive disappear checking dependencies");
+ for (pdep= otherpkg->installed.depended;
+ pdep;
+ pdep= pdep->nextrev) {
+ if (pdep->up->type != dep_depends && pdep->up->type != dep_predepends &&
+ pdep->up->type != dep_recommends) continue;
+ if (depisok(pdep->up, &depprobwhy, 0,0)) continue;
+ varbufaddc(&depprobwhy,0);
+ debug(dbg_veryverbose,"process_archive cannot disappear: %s",depprobwhy.buf);
+ break;
+ }
+ if (!pdep) {
+ /* If we haven't found a reason not to yet, let's look some more. */
+ for (providecheck= otherpkg->installed.depends;
+ providecheck;
+ providecheck= providecheck->next) {
+ if (providecheck->type != dep_provides) continue;
+ for (pdep= providecheck->list->ed->installed.depended;
+ pdep;
+ pdep= pdep->nextrev) {
+ if (pdep->up->type != dep_depends && pdep->up->type != dep_predepends &&
+ pdep->up->type != dep_recommends)
+ continue;
+ if (depisok(pdep->up, &depprobwhy, 0,0)) continue;
+ varbufaddc(&depprobwhy,0);
+ debug(dbg_veryverbose,"process_archive cannot disappear (provides %s): %s",
+ providecheck->list->ed->name, depprobwhy.buf);
+ goto break_from_both_loops_at_once;
+ }
+ }
+ break_from_both_loops_at_once:;
+ }
+ otherpkg->clientdata->istobe= itb_normal;
+ if (pdep) continue;
+
+ printf(_("(Noting disappearance of %s, which has been completely replaced.)\n"),
+ otherpkg->name);
+ debug(dbg_general, "process_archive disappearing %s",otherpkg->name);
+ /* No, we're disappearing it. This is the wrong time to go and
+ * run maintainer scripts and things, as we can't back out. But
+ * what can we do ? It has to be run this late.
+ */
+ maintainer_script_installed(otherpkg, POSTRMFILE,
+ "post-removal script (for disappearance)",
+ "disappear", pkg->name,
+ versiondescribe(&pkg->available.version,
+ vdew_nonambig),
+ (char*)0);
+
+ /* OK, now we delete all the stuff in the `info' directory .. */
+ varbufreset(&fnvb);
+ varbufaddstr(&fnvb,admindir);
+ varbufaddstr(&fnvb,"/" INFODIR);
+ infodirbaseused= fnvb.used;
+ varbufaddc(&fnvb,0);
+ dsd= opendir(fnvb.buf); if (!dsd) ohshite(_("cannot read info directory"));
+ push_cleanup(cu_closedir,~0, 0,0, 1,(void*)dsd);
+
+ debug(dbg_general, "process_archive disappear cleaning info directory");
+
+ while ((de= readdir(dsd)) != 0) {
+ debug(dbg_veryverbose, "process_archive info file `%s'", de->d_name);
+ if (de->d_name[0] == '.') continue;
+ p= strrchr(de->d_name,'.'); if (!p) continue;
+ if (strlen(otherpkg->name) != (size_t)(p-de->d_name) ||
+ strncmp(de->d_name,otherpkg->name,p-de->d_name)) continue;
+ debug(dbg_stupidlyverbose, "process_archive info this pkg");
+ fnvb.used= infodirbaseused;
+ varbufaddstr(&fnvb,de->d_name);
+ varbufaddc(&fnvb,0);
+ if (unlink(fnvb.buf))
+ ohshite(_("unable to delete disappearing control info file `%.250s'"),fnvb.buf);
+ debug(dbg_scripts, "process_archive info unlinked %s",fnvb.buf);
+ }
+ pop_cleanup(ehflag_normaltidy); /* closedir */
+
+ otherpkg->status= stat_notinstalled;
+ otherpkg->want= want_purge;
+ otherpkg->eflag= eflagv_ok;
+
+ otherpkg->installed.depends= 0;
+ otherpkg->installed.essential= 0;
+ otherpkg->installed.description= otherpkg->installed.maintainer= 0;
+ otherpkg->installed.installedsize= otherpkg->installed.source= 0;
+ otherpkg->installed.conffiles= 0;
+ blankversion(&otherpkg->installed.version);
+ otherpkg->installed.arbs= 0;
+ otherpkg->clientdata->fileslistvalid= 0;
+
+ modstatdb_note(otherpkg);
+
+ } /* while (otherpkg= ... */
+ iterpkgend(it);
+
+ /* Delete files from any other packages' lists.
+ * We have to do this before we claim this package is in any
+ * sane kind of state, as otherwise we might delete by mistake
+ * a file that we overwrote, when we remove the package which
+ * had the version we overwrote. To prevent this we make
+ * sure that we don't claim this package is OK until we
+ * have claimed `ownership' of all its files.
+ */
+ for (cfile= newfileslist; cfile; cfile= cfile->next) {
+ if (!(cfile->namenode->flags & fnnf_elide_other_lists)) continue;
+ if (cfile->namenode->divert && cfile->namenode->divert->useinstead) {
+ divpkg= cfile->namenode->divert->pkg;
+ if (divpkg == pkg) {
+ debug(dbg_eachfile,
+ "process_archive not overwriting any `%s' (overriding, `%s')",
+ cfile->namenode->name, cfile->namenode->divert->useinstead->name);
+ continue;
+ } else {
+ debug(dbg_eachfile,
+ "process_archive looking for overwriting `%s' (overridden by %s)",
+ cfile->namenode->name, divpkg ? divpkg->name : "<local>");
+ }
+ } else {
+ divpkg= 0;
+ debug(dbg_eachfile, "process_archive looking for overwriting `%s'",
+ cfile->namenode->name);
+ }
+ for (packageslump= cfile->namenode->packages;
+ packageslump;
+ packageslump= packageslump->more) {
+ for (i=0; i < PERFILEPACKAGESLUMP && packageslump->pkgs[i]; i++) {
+ otherpkg= packageslump->pkgs[i];
+ debug(dbg_eachfiledetail, "process_archive ... found in %s\n",otherpkg->name);
+ /* If !fileslistvalid then it's one of the disappeared packages above
+ * and we don't bother with it here, clearly.
+ */
+ if (otherpkg == pkg || !otherpkg->clientdata->fileslistvalid) continue;
+ if (otherpkg == divpkg) {
+ debug(dbg_eachfiledetail, "process_archive ... diverted, skipping\n");
+ continue;
+ }
+
+ /* Found one. We delete remove the list entry for this file,
+ * (and any others in the same package) and then mark the package
+ * as requiring a reread.
+ */
+ write_filelist_except(otherpkg, otherpkg->clientdata->files, 1);
+ ensure_package_clientdata(otherpkg);
+ debug(dbg_veryverbose, "process_archive overwrote from %s",otherpkg->name);
+ }
+ }
+ }
+
+ /* Right, the package we've unpacked is now in a reasonable state.
+ * The only thing that we have left to do with it is remove
+ * backup files, and we can leave the user to fix that if and when
+ * it happens (we leave the reinstall required flag, of course).
+ */
+ pkg->status= stat_unpacked;
+ modstatdb_note(pkg);
+
+ /* Now we delete all the backup files that we made when
+ * extracting the archive - except for files listed as conffiles
+ * in the new package.
+ * This time we count it as an error if something goes wrong.
+ *
+ * Note that we don't ever delete things that were in the old
+ * package as a conffile and don't appear at all in the new.
+ */
+ for (cfile= newfileslist; cfile; cfile= cfile->next) {
+ if (cfile->namenode->flags & fnnf_new_conff) continue;
+ fnametmpvb.used= fnameidlu;
+ varbufaddstr(&fnametmpvb,namenodetouse(cfile->namenode,pkg)->name);
+ varbufaddstr(&fnametmpvb,DPKGTEMPEXT);
+ varbufaddc(&fnametmpvb,0);
+ ensure_pathname_nonexisting(fnametmpvb.buf);
+ }
+
+ /* OK, we're now fully done with the main package.
+ * This is quite a nice state, so we don't unwind past here.
+ */
+ pkg->eflag= eflagv_ok;
+ modstatdb_note(pkg);
+ push_checkpoint(~ehflag_bombout, ehflag_normaltidy);
+
+ /* Only the removal of the conflictor left to do.
+ * The files list for the conflictor is still a little inconsistent in-core,
+ * as we have not yet updated the filename->packages mappings; however,
+ * the package->filenames mapping is
+ */
+ for (i = 0 ; i < cflict_index ; i++) {
+ /* We need to have the most up-to-date info about which files are what ... */
+ ensure_allinstfiles_available();
+ removal_bulk(conflictor[i]);
+ }
+
+ if (cipaction->arg == act_install) add_to_queue(pkg);
+}
diff --git a/src/query.c b/src/query.c
new file mode 100644
index 000000000..8b43daf12
--- /dev/null
+++ b/src/query.c
@@ -0,0 +1,553 @@
+/*
+ * dpkg-query - program for query the dpkg database
+ * query.c - status enquiry and listing options
+ *
+ * dpkg - main program for package management
+ * enquiry.c - status enquiry and listing options
+ *
+ * Copyright (C) 1995,1996 Ian Jackson <ian@chiark.greenend.org.uk>
+ * Copyright (C) 200,2001 Wichert Akkerman <wakkerma@debian.org>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 dpkg; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <config.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fnmatch.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/termios.h>
+#include <fcntl.h>
+
+#include <dpkg.h>
+#include <dpkg-db.h>
+#include <myopt.h>
+
+#include "filesdb.h"
+#include "main.h"
+
+static const char* showformat = "${Package}\t${Version}\n";
+
+
+void ensure_package_clientdata(struct pkginfo *pkg) {
+ if (pkg->clientdata) return;
+ pkg->clientdata= nfmalloc(sizeof(struct perpackagestate));
+ pkg->clientdata->istobe= itb_normal;
+ pkg->clientdata->fileslistvalid= 0;
+ pkg->clientdata->files= 0;
+}
+
+const char *pkgadminfile(struct pkginfo *pkg, const char *whichfile) {
+ static struct varbuf vb;
+ varbufreset(&vb);
+ varbufaddstr(&vb,admindir);
+ varbufaddstr(&vb,"/" INFODIR);
+ varbufaddstr(&vb,pkg->name);
+ varbufaddc(&vb,'.');
+ varbufaddstr(&vb,whichfile);
+ varbufaddc(&vb,0);
+ return vb.buf;
+}
+
+void cu_closepipe(int argc, void **argv) {
+ int *p1= (int*)argv[0];
+ close(p1[0]); close(p1[1]);
+}
+
+void cu_closefile(int argc, void **argv) {
+ FILE *f= (FILE*)(argv[0]);
+ fclose(f);
+}
+
+void cu_closefd(int argc, void **argv) {
+ int ip= *(int*)argv;
+ close(ip);
+}
+
+int pkglistqsortcmp(const void *a, const void *b) {
+ const struct pkginfo *pa= *(const struct pkginfo**)a;
+ const struct pkginfo *pb= *(const struct pkginfo**)b;
+ return strcmp(pa->name,pb->name);
+}
+
+static void limiteddescription(struct pkginfo *pkg, int maxl,
+ const char **pdesc_r, int *l_r) {
+ const char *pdesc, *p;
+ int l;
+
+ pdesc= pkg->installed.valid ? pkg->installed.description : 0;
+ if (!pdesc) pdesc= _("(no description available)");
+ p= strchr(pdesc,'\n');
+ if (!p) p= pdesc+strlen(pdesc);
+ l= (p - pdesc > maxl) ? maxl : (int)(p - pdesc);
+ *pdesc_r=pdesc; *l_r=l;
+}
+
+static int getwidth(void) {
+ int fd;
+ int res;
+ struct winsize ws;
+ const char* columns;
+
+ if ((columns=getenv("COLUMNS")) && ((res=atoi(columns))>0))
+ ws.ws_col=res;
+ else if (!isatty(1))
+ ws.ws_col=80;
+ else {
+ if ((fd=open("/dev/tty",O_RDONLY))!=-1) {
+ if (ioctl(fd, TIOCGWINSZ, &ws)==-1)
+ ws.ws_col=80;
+ close(fd);
+ }
+ }
+ return ws.ws_col;
+}
+
+static void list1package(struct pkginfo *pkg, int *head) {
+ int l,w;
+ static int nw,vw,dw;
+ const char *pdesc;
+ static char format[80] = "";
+
+ if (format[0]==0) {
+ w=getwidth()-80; /* get spare width */
+ if (w<0) w=0; /* lets not try to deal with terminals that are too small */
+ w>>=2; /* halve that so we can add that to the both the name and description */
+ nw=(14+w); /* name width */
+ vw=(14+w); /* version width */
+ dw=(44+(2*w)); /* description width */
+ sprintf(format,"%%c%%c%%c %%-%d.%ds %%-%d.%ds %%.*s\n", nw, nw, vw, vw);
+ }
+
+ if (!*head) {
+ fputs(_("\
+Desired=Unknown/Install/Remove/Purge/Hold\n\
+| Status=Not/Installed/Config-files/Unpacked/Failed-config/Half-installed\n\
+|/ Err?=(none)/Hold/Reinst-required/X=both-problems (Status,Err: uppercase=bad)\n"), stdout);
+ printf(format,'|','|','/', _("Name"), _("Version"), 40, _("Description"));
+ printf("+++-"); /* status */
+ for (l=0;l<nw;l++) printf("="); printf("-"); /* packagename */
+ for (l=0;l<vw;l++) printf("="); printf("-"); /* version */
+ for (l=0;l<dw;l++) printf("="); /* description */
+ printf("\n");
+ *head= 1;
+ }
+ if (!pkg->installed.valid) blankpackageperfile(&pkg->installed);
+ limiteddescription(pkg,dw,&pdesc,&l);
+ printf(format,
+ "uihrp"[pkg->want],
+ "nUFiHc"[pkg->status],
+ " R?#"[pkg->eflag],
+ pkg->name,
+ versiondescribe(&pkg->installed.version,vdew_never),
+ l, pdesc);
+}
+
+void listpackages(const char *const *argv) {
+ struct pkgiterator *it;
+ struct pkginfo *pkg;
+ struct pkginfo **pkgl;
+ const char *thisarg;
+ int np, i, head, found;
+
+ modstatdb_init(admindir,msdbrw_readonly);
+
+ np= countpackages();
+ pkgl= m_malloc(sizeof(struct pkginfo*)*np);
+ it= iterpkgstart(); i=0;
+ while ((pkg= iterpkgnext(it))) {
+ assert(i<np);
+ pkgl[i++]= pkg;
+ }
+ iterpkgend(it);
+ assert(i==np);
+
+ qsort(pkgl,np,sizeof(struct pkginfo*),pkglistqsortcmp);
+ head=0;
+
+ if (!*argv) {
+ for (i=0; i<np; i++) {
+ pkg= pkgl[i];
+ if (pkg->status == stat_notinstalled) continue;
+ list1package(pkg,&head);
+ }
+ } else {
+ while ((thisarg= *argv++)) {
+ found= 0;
+ for (i=0; i<np; i++) {
+ pkg= pkgl[i];
+ if (fnmatch(thisarg,pkg->name,0)) continue;
+ list1package(pkg,&head); found++;
+ }
+ if (!found) {
+ fprintf(stderr,_("No packages found matching %s.\n"),thisarg);
+ nerrs++;
+ }
+ }
+ }
+ if (ferror(stdout)) werr("stdout");
+ if (ferror(stderr)) werr("stderr");
+ modstatdb_shutdown();
+}
+
+static int searchoutput(struct filenamenode *namenode) {
+ int found, i;
+ struct filepackages *packageslump;
+
+ if (namenode->divert) {
+ for (i=0; i<2; i++) {
+ if (namenode->divert->pkg) printf(_("diversion by %s"),namenode->divert->pkg->name);
+ else printf(_("local diversion"));
+ printf(" %s: %s\n", i ? _("to") : _("from"),
+ i ?
+ (namenode->divert->useinstead
+ ? namenode->divert->useinstead->name
+ : namenode->name)
+ :
+ (namenode->divert->camefrom
+ ? namenode->divert->camefrom->name
+ : namenode->name));
+ }
+ }
+ found= 0;
+ for (packageslump= namenode->packages;
+ packageslump;
+ packageslump= packageslump->more) {
+ for (i=0; i < PERFILEPACKAGESLUMP && packageslump->pkgs[i]; i++) {
+ if (found) fputs(", ",stdout);
+ fputs(packageslump->pkgs[i]->name,stdout);
+ found++;
+ }
+ }
+ if (found) printf(": %s\n",namenode->name);
+ return found + (namenode->divert ? 1 : 0);
+}
+
+void searchfiles(const char *const *argv) {
+ struct filenamenode *namenode;
+ struct fileiterator *it;
+ const char *thisarg;
+ int found;
+ static struct varbuf vb;
+
+ if (!*argv)
+ badusage(_("--search needs at least one file name pattern argument"));
+
+ modstatdb_init(admindir,msdbrw_readonly|msdbrw_noavail);
+ ensure_allinstfiles_available_quiet();
+ ensure_diversions();
+
+ while ((thisarg= *argv++) != 0) {
+ found= 0;
+ if (!strchr("*[?/",*thisarg)) {
+ varbufreset(&vb);
+ varbufaddc(&vb,'*');
+ varbufaddstr(&vb,thisarg);
+ varbufaddc(&vb,'*');
+ varbufaddc(&vb,0);
+ thisarg= vb.buf;
+ }
+ if (strcspn(thisarg,"*[?\\") == strlen(thisarg)) {
+ namenode= findnamenode(thisarg, 0);
+ found += searchoutput(namenode);
+ } else {
+ it= iterfilestart();
+ while ((namenode= iterfilenext(it)) != 0) {
+ if (fnmatch(thisarg,namenode->name,0)) continue;
+ found+= searchoutput(namenode);
+ }
+ iterfileend(it);
+ }
+ if (!found) {
+ fprintf(stderr,_("dpkg: %s not found.\n"),thisarg);
+ nerrs++;
+ if (ferror(stderr)) werr("stderr");
+ } else {
+ if (ferror(stdout)) werr("stdout");
+ }
+ }
+ modstatdb_shutdown();
+}
+
+void enqperpackage(const char *const *argv) {
+ int failures;
+ const char *thisarg;
+ struct fileinlist *file;
+ struct pkginfo *pkg;
+ struct filenamenode *namenode;
+
+ if (!*argv)
+ badusage(_("--%s needs at least one package name argument"), cipaction->olong);
+
+ failures= 0;
+ if (cipaction->arg==act_listfiles)
+ modstatdb_init(admindir,msdbrw_readonly|msdbrw_noavail);
+ else
+ modstatdb_init(admindir,msdbrw_readonly);
+
+ while ((thisarg= *argv++) != 0) {
+ pkg= findpackage(thisarg);
+
+ switch (cipaction->arg) {
+
+ case act_status:
+ if (pkg->status == stat_notinstalled &&
+ pkg->priority == pri_unknown &&
+ !(pkg->section && *pkg->section) &&
+ !pkg->files &&
+ pkg->want == want_unknown &&
+ !informative(pkg,&pkg->installed)) {
+ fprintf(stderr,_("Package `%s' is not installed and no info is available.\n"),pkg->name);
+ failures++;
+ } else {
+ writerecord(stdout, "<stdout>", pkg, &pkg->installed);
+ }
+ break;
+
+ case act_printavail:
+ if (!informative(pkg,&pkg->available)) {
+ fprintf(stderr,_("Package `%s' is not available.\n"),pkg->name);
+ failures++;
+ } else {
+ writerecord(stdout, "<stdout>", pkg, &pkg->available);
+ }
+ break;
+
+ case act_listfiles:
+ switch (pkg->status) {
+ case stat_notinstalled:
+ fprintf(stderr,_("Package `%s' is not installed.\n"),pkg->name);
+ failures++;
+ break;
+
+ default:
+ ensure_packagefiles_available(pkg);
+ ensure_diversions();
+ file= pkg->clientdata->files;
+ if (!file) {
+ printf(_("Package `%s' does not contain any files (!)\n"),pkg->name);
+ } else {
+ while (file) {
+ namenode= file->namenode;
+ puts(namenode->name);
+ if (namenode->divert && !namenode->divert->camefrom) {
+ if (!namenode->divert->pkg) printf(_("locally diverted"));
+ else if (pkg == namenode->divert->pkg) printf(_("package diverts others"));
+ else printf(_("diverted by %s"),namenode->divert->pkg->name);
+ printf(_(" to: %s\n"),namenode->divert->useinstead->name);
+ }
+ file= file->next;
+ }
+ }
+ break;
+ }
+ break;
+
+ default:
+ internerr("unknown action");
+ }
+
+ putchar('\n');
+ if (ferror(stdout)) werr("stdout");
+ }
+
+ if (failures) {
+ nerrs++;
+ fputs(_("Use dpkg --info (= dpkg-deb --info) to examine archive files,\n"
+ "and dpkg --contents (= dpkg-deb --contents) to list their contents.\n"),stderr);
+ if (ferror(stdout)) werr("stdout");
+ }
+ modstatdb_shutdown();
+}
+
+void showpackages(const char *const *argv) {
+ struct pkgiterator *it;
+ struct pkginfo *pkg;
+ struct pkginfo **pkgl;
+ const char *thisarg;
+ int np, i, found;
+ struct lstitem* fmt = parseformat(showformat);
+
+ if (!fmt) {
+ nerrs++;
+ return;
+ }
+
+ modstatdb_init(admindir,msdbrw_readonly);
+
+ np= countpackages();
+ pkgl= m_malloc(sizeof(struct pkginfo*)*np);
+ it= iterpkgstart(); i=0;
+ while ((pkg= iterpkgnext(it))) {
+ assert(i<np);
+ pkgl[i++]= pkg;
+ }
+ iterpkgend(it);
+ assert(i==np);
+
+ qsort(pkgl,np,sizeof(struct pkginfo*),pkglistqsortcmp);
+
+ if (!*argv) {
+ for (i=0; i<np; i++) {
+ pkg= pkgl[i];
+ if (pkg->status == stat_notinstalled) continue;
+ show1package(fmt,pkg);
+ }
+ } else {
+ while ((thisarg= *argv++)) {
+ found= 0;
+ for (i=0; i<np; i++) {
+ pkg= pkgl[i];
+ if (fnmatch(thisarg,pkg->name,0)) continue;
+ show1package(fmt,pkg); found++;
+ }
+ if (!found) {
+ fprintf(stderr,_("No packages found matching %s.\n"),thisarg);
+ nerrs++;
+ }
+ }
+ }
+ if (ferror(stdout)) werr("stdout");
+ if (ferror(stderr)) werr("stderr");
+ freeformat(fmt);
+ modstatdb_shutdown();
+}
+
+static void printversion(void) {
+ if (fputs(_("Debian `"), stdout) < 0) werr("stdout");
+ if (fputs(DPKGQUERY, stdout) < 0) werr("stdout");
+ if (fputs(_("' package management program query tool\n"), stdout) < 0)
+ werr("stdout");
+ if (fputs(_( "This is free software; see the GNU General Public Licence version 2 or\n"
+ "later for copying conditions. There is NO warranty.\n"
+ "See " DPKGQUERY " --licence for copyright and license details.\n"),
+ stdout) < 0) werr("stdout");
+}
+/*
+ options that need fixing:
+ dpkg --yet-to-unpack \n\
+ */
+static void usage(void) {
+ if (fprintf (stdout, _("\
+Usage: " DPKGQUERY " [<option>] <command>\n\
+Commands:\n\
+ -s|--status <package-name> ... display package status details\n\
+ -p|--print-avail <package-name> ... display available version details\n\
+ -L|--listfiles <package-name> ... list files `owned' by package(s)\n\
+ -l|--list [<pattern> ...] list packages concisely\n\
+ -W|--show <pattern> ... show information on package(s)\n\
+ -S|--search <pattern> ... find package(s) owning file(s)\n\
+ --help | --version show this help / version number\n\
+ --licence print copyright licensing terms\n\
+\n\
+Options:\n\
+ --admindir=<directory> Use <directory> instead of %s\n\
+ -f|--showformat=<format> Use alternative format for --show\n\
+\n\
+Format syntax:\n\
+ A format is a string that will be output for each package. The format\n\
+ can include the standard escape sequences \\n (newline), \\r (carriage\n\
+ return) or \\\\ (plain backslash). Package information can be included\n\
+ by inserting variable references to package fields using the ${var[;width]}\n\
+ syntax. Fields will be right-aligned unless the width is negative in which\n\
+ case left aligenment will be used. \n\
+"),
+ ADMINDIR) < 0) werr ("stdout");
+}
+
+const char thisname[]= "dpkg-query";
+const char printforhelp[]= N_("\
+Use --help for help about querying packages;\n\
+Use --licence for copyright licence and lack of warranty (GNU GPL).\n\
+\n");
+
+const struct cmdinfo *cipaction= 0;
+int f_pending=0, f_recursive=0, f_alsoselect=1, f_skipsame=0, f_noact=0;
+int f_autodeconf=0, f_nodebsig=0;
+unsigned long f_debug=0;
+/* Change fc_overwrite to 1 to enable force-overwrite by default */
+int fc_hold=0;
+int fc_conflicts=0, fc_depends=0;
+int fc_badpath=0;
+
+int errabort = 50;
+const char *admindir= ADMINDIR;
+const char *instdir= "";
+struct packageinlist *ignoredependss=0;
+
+static void helponly(const struct cmdinfo *cip, const char *value) NONRETURNING;
+static void helponly(const struct cmdinfo *cip, const char *value) {
+ usage(); exit(0);
+}
+static void versiononly(const struct cmdinfo *cip, const char *value) NONRETURNING;
+static void versiononly(const struct cmdinfo *cip, const char *value) {
+ printversion(); exit(0);
+}
+
+static void setaction(const struct cmdinfo *cip, const char *value) {
+ if (cipaction)
+ badusage(_("conflicting actions --%s and --%s"),cip->olong,cipaction->olong);
+ cipaction= cip;
+}
+
+static const struct cmdinfo cmdinfos[]= {
+ /* This table has both the action entries in it and the normal options.
+ * The action entries are made with the ACTION macro, as they all
+ * have a very similar structure.
+ */
+#define ACTION(longopt,shortopt,code,function) \
+ { longopt, shortopt, 0,0,0, setaction, code, 0, (voidfnp)function }
+#define OBSOLETE(longopt,shortopt) \
+ { longopt, shortopt, 0,0,0, setobsolete, 0, 0, 0 }
+
+ ACTION( "listfiles", 'L', act_listfiles, enqperpackage ),
+ ACTION( "status", 's', act_status, enqperpackage ),
+ ACTION( "print-avail", 'p', act_printavail, enqperpackage ),
+ ACTION( "list", 'l', act_listpackages, listpackages ),
+ ACTION( "search", 'S', act_searchfiles, searchfiles ),
+ ACTION( "show", 'W', act_listpackages, showpackages ),
+
+ { "admindir", 0, 1, 0, &admindir, 0 },
+ { "showformat", 'f', 1, 0, &showformat, 0 },
+ { "help", 'h', 0, 0, 0, helponly },
+ { "version", 0, 0, 0, 0, versiononly },
+ { "licence",/* UK spelling */ 0,0,0,0, showcopyright },
+ { "license",/* US spelling */ 0,0,0,0, showcopyright },
+ { 0, 0 }
+};
+
+int main(int argc, const char *const *argv) {
+ jmp_buf ejbuf;
+ static void (*actionfunction)(const char *const *argv);
+
+ standard_startup(&ejbuf, argc, &argv, NULL, 0, cmdinfos);
+ if (!cipaction) badusage(_("need an action option"));
+
+ setvbuf(stdout,0,_IONBF,0);
+ filesdbinit();
+
+ actionfunction= (void (*)(const char* const*))cipaction->farg;
+
+ actionfunction(argv);
+
+ standard_shutdown(0);
+
+ return reportbroken_retexitstatus();
+}
diff --git a/src/remove.c b/src/remove.c
new file mode 100644
index 000000000..6dbf1a18c
--- /dev/null
+++ b/src/remove.c
@@ -0,0 +1,584 @@
+/* Change: separate removal_bulk handling of halfinstalled or unpacked pkgs
+ * (ie, remove the real files in the .deb) into its own function.
+ * Note that "installed" state is converted to unpacked by
+ * deferred_remove() or halfinstalled by process_archive())
+ *
+ * Change: separate purging of configfiles and running of postrm to its
+ * own function.
+ *
+ * Change: retry removing directories after postrm purge is finished, to
+ * handle directories that had conffiles or config files and so
+ * forth in them. also only warn about non-empty directories at
+ * that point.
+ */
+
+/*
+ * dpkg - main program for package management
+ * remove.c - functionality for removing packages
+ *
+ * Copyright (C) 1995 Ian Jackson <ian@chiark.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 dpkg; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <config.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+
+#include <dpkg.h>
+#include <dpkg-db.h>
+#include <myopt.h>
+
+#include "filesdb.h"
+#include "main.h"
+
+static void checkforremoval(struct pkginfo *pkgtoremove,
+ struct pkginfo *pkgdepcheck, /* may be virtual pkg */
+ int *rokp, struct varbuf *raemsgs) {
+ struct deppossi *possi;
+ struct pkginfo *depender;
+ int before, ok;
+
+ for (possi= pkgdepcheck->installed.depended; possi; possi= possi->nextrev) {
+ if (possi->up->type != dep_depends && possi->up->type != dep_predepends) continue;
+ depender= possi->up->up;
+ debug(dbg_depcon,"checking depending package `%s'",depender->name);
+ if (depender->status != stat_installed) continue;
+ if (ignore_depends(depender)) {
+ debug(dbg_depcon,"ignoring depending package `%s'\n",depender->name);
+ continue;
+ }
+ if (dependtry > 1) { if (findbreakcycle(pkgtoremove)) sincenothing= 0; }
+ before= raemsgs->used;
+ ok= dependencies_ok(depender,pkgtoremove,raemsgs);
+ if (ok == 0 && depender->clientdata->istobe == itb_remove) ok= 1;
+ if (ok == 1) raemsgs->used= before; /* Don't burble about reasons for deferral */
+ if (ok < *rokp) *rokp= ok;
+ }
+}
+
+void deferred_remove(struct pkginfo *pkg) {
+ struct varbuf raemsgs;
+ int rok;
+ struct dependency *dep;
+
+ debug(dbg_general,"deferred_remove package %s",pkg->name);
+
+ if (pkg->status == stat_notinstalled) {
+ fprintf(stderr,
+ _("dpkg - warning: ignoring request to remove %.250s which isn't installed.\n"),
+ pkg->name);
+ pkg->clientdata->istobe= itb_normal;
+ return;
+ } else if (!f_pending &&
+ pkg->status == stat_configfiles &&
+ cipaction->arg != act_purge) {
+ fprintf(stderr,
+ _("dpkg - warning: ignoring request to remove %.250s, only the config\n"
+ " files of which are on the system. Use --purge to remove them too.\n"),
+ pkg->name);
+ pkg->clientdata->istobe= itb_normal;
+ return;
+ }
+
+ assert(pkg->installed.valid);
+ if (pkg->installed.essential && pkg->status != stat_configfiles)
+ forcibleerr(fc_removeessential, _("This is an essential package -"
+ " it should not be removed."));
+
+ if (!f_pending)
+ pkg->want= (cipaction->arg == act_purge) ? want_purge : want_deinstall;
+ if (!f_noact) modstatdb_note(pkg);
+
+ debug(dbg_general,"checking dependencies for remove `%s'",pkg->name);
+ varbufinit(&raemsgs);
+ rok= 2;
+ checkforremoval(pkg,pkg,&rok,&raemsgs);
+ for (dep= pkg->installed.depends; dep; dep= dep->next) {
+ if (dep->type != dep_provides) continue;
+ debug(dbg_depcon,"checking virtual package `%s'",dep->list->ed->name);
+ checkforremoval(pkg,dep->list->ed,&rok,&raemsgs);
+ }
+
+ if (rok == 1) {
+ varbuffree(&raemsgs);
+ pkg->clientdata->istobe= itb_remove;
+ add_to_queue(pkg);
+ return;
+ } else if (rok == 0) {
+ sincenothing= 0;
+ varbufaddc(&raemsgs,0);
+ fprintf(stderr,
+ _("dpkg: dependency problems prevent removal of %s:\n%s"),
+ pkg->name, raemsgs.buf);
+ ohshit(_("dependency problems - not removing"));
+ } else if (raemsgs.used) {
+ varbufaddc(&raemsgs,0);
+ fprintf(stderr,
+ _("dpkg: %s: dependency problems, but removing anyway as you request:\n%s"),
+ pkg->name, raemsgs.buf);
+ }
+ varbuffree(&raemsgs);
+ sincenothing= 0;
+
+ if (pkg->eflag & eflagf_reinstreq)
+ forcibleerr(fc_removereinstreq,
+ _("Package is in a very bad inconsistent state - you should\n"
+ " reinstall it before attempting a removal."));
+
+ ensure_allinstfiles_available();
+ filesdbinit();
+
+ if (f_noact) {
+ printf(_("Would remove or purge %s ...\n"),pkg->name);
+ pkg->status= stat_notinstalled;
+ pkg->clientdata->istobe= itb_normal;
+ return;
+ }
+
+ oldconffsetflags(pkg->installed.conffiles);
+
+ printf(_("Removing %s ...\n"),pkg->name);
+ if (pkg->status == stat_halfconfigured || pkg->status == stat_installed) {
+
+ if (pkg->status == stat_installed || pkg->status == stat_halfconfigured) {
+ pkg->status= stat_halfconfigured;
+ modstatdb_note(pkg);
+ push_cleanup(cu_prermremove,~ehflag_normaltidy, 0,0, 1,(void*)pkg);
+ maintainer_script_installed(pkg, PRERMFILE, "pre-removal",
+ "remove", (char*)0);
+ }
+
+ pkg->status= stat_unpacked; /* Will turn into halfinstalled soon ... */
+ }
+
+ removal_bulk(pkg);
+}
+
+static void push_leftover(struct fileinlist **leftoverp,
+ struct filenamenode *namenode) {
+ struct fileinlist *newentry;
+ newentry= nfmalloc(sizeof(struct fileinlist));
+ newentry->next= *leftoverp;
+ newentry->namenode= namenode;
+ *leftoverp= newentry;
+}
+
+static void removal_bulk_remove_files(
+ struct pkginfo *pkg,
+ int *out_foundpostrm)
+{
+ int before;
+ int infodirbaseused;
+ struct reversefilelistiter rlistit;
+ struct fileinlist *leftover;
+ struct filenamenode *namenode;
+ static struct varbuf fnvb;
+ DIR *dsd;
+ struct dirent *de;
+ struct stat stab;
+
+ pkg->status= stat_halfinstalled;
+ modstatdb_note(pkg);
+ push_checkpoint(~ehflag_bombout, ehflag_normaltidy);
+
+ reversefilelist_init(&rlistit,pkg->clientdata->files);
+ leftover= 0;
+ while ((namenode= reversefilelist_next(&rlistit))) {
+ debug(dbg_eachfile, "removal_bulk `%s' flags=%o",
+ namenode->name, namenode->flags);
+ if (namenode->flags & fnnf_old_conff) {
+ push_leftover(&leftover,namenode);
+ continue;
+ }
+ varbufreset(&fnvb);
+ varbufaddstr(&fnvb,instdir);
+ varbufaddstr(&fnvb,namenodetouse(namenode,pkg)->name);
+ before= fnvb.used;
+
+ varbufaddstr(&fnvb,DPKGTEMPEXT);
+ varbufaddc(&fnvb,0);
+ debug(dbg_eachfiledetail, "removal_bulk cleaning temp `%s'", fnvb.buf);
+
+ ensure_pathname_nonexisting(fnvb.buf);
+
+ fnvb.used= before;
+ varbufaddstr(&fnvb,DPKGNEWEXT);
+ varbufaddc(&fnvb,0);
+ debug(dbg_eachfiledetail, "removal_bulk cleaning new `%s'", fnvb.buf);
+ ensure_pathname_nonexisting(fnvb.buf);
+
+ fnvb.used= before;
+ varbufaddc(&fnvb,0);
+ if (!stat(fnvb.buf,&stab) && S_ISDIR(stab.st_mode)) {
+ debug(dbg_eachfiledetail, "removal_bulk is a directory");
+ /* Only delete a directory or a link to one if we're the only
+ * package which uses it. Other files should only be listed
+ * in this package (but we don't check).
+ */
+ if (isdirectoryinuse(namenode,pkg)) continue;
+ }
+ debug(dbg_eachfiledetail, "removal_bulk removing `%s'", fnvb.buf);
+ if (!rmdir(fnvb.buf) || errno == ENOENT || errno == ELOOP) continue;
+ if (errno == ENOTEMPTY) {
+ push_leftover(&leftover,namenode);
+ continue;
+ } else if (errno == EBUSY || errno == EPERM) {
+ fprintf(stderr, _("dpkg - warning: while removing %.250s,"
+ " unable to remove directory `%.250s':"
+ " %s - directory may be a mount point ?\n"),
+ pkg->name, namenode->name, strerror(errno));
+ push_leftover(&leftover,namenode);
+ continue;
+ }
+ if (errno != ENOTDIR) ohshite(_("cannot remove `%.250s'"),fnvb.buf);
+ debug(dbg_eachfiledetail, "removal_bulk unlinking `%s'", fnvb.buf);
+ {
+ /*
+ * If file to remove is a device or s[gu]id, change its mode
+ * so that a malicious user cannot use it even if it's linked
+ * to another file
+ */
+ struct stat stat_buf;
+ if (stat(fnvb.buf,&stat_buf)==0) {
+ if (S_ISCHR(stat_buf.st_mode) || S_ISBLK(stat_buf.st_mode)) {
+ chmod(fnvb.buf,0);
+ }
+ if (stat_buf.st_mode & (S_ISUID|S_ISGID)) {
+ chmod(fnvb.buf,stat_buf.st_mode & ~(S_ISUID|S_ISGID));
+ }
+ }
+ }
+ if (unlink(fnvb.buf)) ohshite(_("cannot remove file `%.250s'"),fnvb.buf);
+ }
+ write_filelist_except(pkg,leftover,0);
+ maintainer_script_installed(pkg, POSTRMFILE, "post-removal",
+ "remove", (char*)0);
+ varbufreset(&fnvb);
+ varbufaddstr(&fnvb,admindir);
+ varbufaddstr(&fnvb,"/" INFODIR);
+ infodirbaseused= fnvb.used;
+ varbufaddc(&fnvb,0);
+ dsd= opendir(fnvb.buf); if (!dsd) ohshite(_("cannot read info directory"));
+ push_cleanup(cu_closedir,~0, 0,0, 1,(void*)dsd);
+ *out_foundpostrm= 0;
+
+ debug(dbg_general, "removal_bulk cleaning info directory");
+
+ while ((de= readdir(dsd)) != 0) {
+ char *p;
+
+ debug(dbg_veryverbose, "removal_bulk info file `%s'", de->d_name);
+ if (de->d_name[0] == '.') continue;
+ p= strrchr(de->d_name,'.'); if (!p) continue;
+ if (strlen(pkg->name) != (size_t)(p-de->d_name) ||
+ strncmp(de->d_name,pkg->name,p-de->d_name)) continue;
+ debug(dbg_stupidlyverbose, "removal_bulk info this pkg");
+ /* We need the postrm and list files for --purge. */
+ if (!strcmp(p+1,LISTFILE)) continue;
+ if (!strcmp(p+1,POSTRMFILE)) { *out_foundpostrm=1; continue; }
+ debug(dbg_stupidlyverbose, "removal_bulk info not postrm or list");
+ fnvb.used= infodirbaseused;
+ varbufaddstr(&fnvb,de->d_name);
+ varbufaddc(&fnvb,0);
+ if (unlink(fnvb.buf))
+ ohshite(_("unable to delete control info file `%.250s'"),fnvb.buf);
+ debug(dbg_scripts, "removal_bulk info unlinked %s",fnvb.buf);
+ }
+ pop_cleanup(ehflag_normaltidy); /* closedir */
+
+ pkg->status= stat_configfiles;
+ pkg->installed.essential= 0;
+ modstatdb_note(pkg);
+ push_checkpoint(~ehflag_bombout, ehflag_normaltidy);
+}
+
+static void removal_bulk_remove_leftover_dirs(struct pkginfo *pkg) {
+ struct reversefilelistiter rlistit;
+ struct fileinlist *leftover;
+ struct filenamenode *namenode;
+ static struct varbuf fnvb;
+ struct stat stab;
+
+ modstatdb_note(pkg);
+ push_checkpoint(~ehflag_bombout, ehflag_normaltidy);
+
+ reversefilelist_init(&rlistit,pkg->clientdata->files);
+ leftover= 0;
+ while ((namenode= reversefilelist_next(&rlistit))) {
+ debug(dbg_eachfile, "removal_bulk `%s' flags=%o",
+ namenode->name, namenode->flags);
+ if (namenode->flags & fnnf_old_conff) {
+ push_leftover(&leftover,namenode);
+ continue;
+ }
+
+ varbufreset(&fnvb);
+ varbufaddstr(&fnvb,instdir);
+ varbufaddstr(&fnvb,namenodetouse(namenode,pkg)->name);
+ varbufaddc(&fnvb,0);
+
+ if (!stat(fnvb.buf,&stab) && S_ISDIR(stab.st_mode)) {
+ debug(dbg_eachfiledetail, "removal_bulk is a directory");
+ /* Only delete a directory or a link to one if we're the only
+ * package which uses it. Other files should only be listed
+ * in this package (but we don't check).
+ */
+ if (isdirectoryinuse(namenode,pkg)) continue;
+ }
+
+ debug(dbg_eachfiledetail, "removal_bulk removing `%s'", fnvb.buf);
+ if (!rmdir(fnvb.buf) || errno == ENOENT || errno == ELOOP) continue;
+ if (errno == ENOTEMPTY) {
+ fprintf(stderr,
+ _("dpkg - warning: while removing %.250s, directory `%.250s' not empty "
+ "so not removed.\n"),
+ pkg->name, namenode->name);
+ push_leftover(&leftover,namenode);
+ continue;
+ } else if (errno == EBUSY || errno == EPERM) {
+ fprintf(stderr, _("dpkg - warning: while removing %.250s,"
+ " unable to remove directory `%.250s':"
+ " %s - directory may be a mount point ?\n"),
+ pkg->name, namenode->name, strerror(errno));
+ push_leftover(&leftover,namenode);
+ continue;
+ }
+ if (errno != ENOTDIR) ohshite(_("cannot remove `%.250s'"),fnvb.buf);
+
+ push_leftover(&leftover,namenode);
+ continue;
+ }
+ write_filelist_except(pkg,leftover,0);
+
+ modstatdb_note(pkg);
+ push_checkpoint(~ehflag_bombout, ehflag_normaltidy);
+}
+
+static void removal_bulk_remove_configfiles(struct pkginfo *pkg) {
+ static const char *const removeconffexts[]= { REMOVECONFFEXTS, 0 };
+ int r, removevbbase;
+ int conffnameused, conffbasenamelen;
+ char *conffbasename;
+ struct conffile *conff, **lconffp;
+ struct fileinlist *searchfile;
+ DIR *dsd;
+ struct dirent *de;
+ char *p;
+ const char *const *ext;
+
+ printf(_("Purging configuration files for %s ...\n"),pkg->name);
+ ensure_packagefiles_available(pkg); /* We may have modified this above. */
+
+ /* We're about to remove the configuration, so remove the note
+ * about which version it was ...
+ */
+ blankversion(&pkg->configversion);
+ modstatdb_note(pkg);
+
+ /* Remove from our list any conffiles that aren't ours any more or
+ * are involved in diversions, except if we are the package doing the
+ * diverting.
+ */
+ for (lconffp= &pkg->installed.conffiles; (conff= *lconffp) != 0; ) {
+ for (searchfile= pkg->clientdata->files;
+ searchfile && strcmp(searchfile->namenode->name,conff->name);
+ searchfile= searchfile->next);
+ if (!searchfile) {
+ debug(dbg_conff,"removal_bulk conffile not ours any more `%s'",conff->name);
+ *lconffp= conff->next;
+ } else if (searchfile->namenode->divert &&
+ (searchfile->namenode->divert->camefrom ||
+ (searchfile->namenode->divert->useinstead &&
+ searchfile->namenode->divert->pkg != pkg))) {
+ debug(dbg_conff,"removal_bulk conffile `%s' ignored due to diversion\n",
+ conff->name);
+ *lconffp= conff->next;
+ } else {
+ debug(dbg_conffdetail,"removal_bulk set to new conffile `%s'",conff->name);
+ conff->hash= NEWCONFFILEFLAG; /* yes, cast away const */
+ lconffp= &conff->next;
+ }
+ }
+ modstatdb_note(pkg);
+
+ for (conff= pkg->installed.conffiles; conff; conff= conff->next) {
+ static struct varbuf fnvb, removevb;
+ varbufreset(&fnvb);
+ r= conffderef(pkg, &fnvb, conff->name);
+ debug(dbg_conffdetail, "removal_bulk conffile `%s' (= `%s')",
+ conff->name, r == -1 ? "<r==-1>" : fnvb.buf);
+ if (r == -1) continue;
+ conffnameused= fnvb.used-1;
+ if (unlink(fnvb.buf) && errno != ENOENT && errno != ENOTDIR)
+ ohshite(_("cannot remove old config file `%.250s' (= `%.250s')"),
+ conff->name, fnvb.buf);
+ p= strrchr(fnvb.buf,'/'); if (!p) continue;
+ *p= 0;
+ varbufreset(&removevb);
+ varbufaddstr(&removevb,fnvb.buf);
+ varbufaddc(&removevb,'/');
+ removevbbase= removevb.used;
+ varbufaddc(&removevb,0);
+ dsd= opendir(removevb.buf);
+ if (!dsd) {
+ int e=errno;
+ debug(dbg_conffdetail, "removal_bulk conffile no dsd %s %s",
+ fnvb.buf, strerror(e)); errno= e;
+ if (errno == ENOENT || errno == ENOTDIR) continue;
+ ohshite(_("cannot read config file dir `%.250s' (from `%.250s')"),
+ fnvb.buf, conff->name);
+ }
+ debug(dbg_conffdetail, "removal_bulk conffile cleaning dsd %s", fnvb.buf);
+ push_cleanup(cu_closedir,~0, 0,0, 1,(void*)dsd);
+ *p= '/';
+ conffbasenamelen= strlen(++p);
+ conffbasename= fnvb.buf+conffnameused-conffbasenamelen;
+ while ((de= readdir(dsd)) != 0) {
+ debug(dbg_stupidlyverbose, "removal_bulk conffile dsd entry=`%s'"
+ " conffbasename=`%s' conffnameused=%d conffbasenamelen=%d",
+ de->d_name, conffbasename, conffnameused, conffbasenamelen);
+ if (!strncmp(de->d_name,conffbasename,conffbasenamelen)) {
+ debug(dbg_stupidlyverbose, "removal_bulk conffile dsd entry starts right");
+ for (ext= removeconffexts; *ext; ext++)
+ if (!strcmp(*ext,de->d_name+conffbasenamelen)) goto yes_remove;
+ p= de->d_name+conffbasenamelen;
+ if (*p++ == '~') {
+ while (*p && cisdigit(*p)) p++;
+ if (*p == '~' && !*++p) goto yes_remove;
+ }
+ }
+ debug(dbg_stupidlyverbose, "removal_bulk conffile dsd entry starts wrong");
+ if (de->d_name[0] == '#' &&
+ !strncmp(de->d_name+1,conffbasename,conffbasenamelen) &&
+ !strcmp(de->d_name+1+conffbasenamelen,"#"))
+ goto yes_remove;
+ debug(dbg_stupidlyverbose, "removal_bulk conffile dsd entry not it");
+ continue;
+ yes_remove:
+ removevb.used= removevbbase;
+ varbufaddstr(&removevb,de->d_name); varbufaddc(&removevb,0);
+ debug(dbg_conffdetail, "removal_bulk conffile dsd entry removing `%s'",
+ removevb.buf);
+ if (unlink(removevb.buf) && errno != ENOENT && errno != ENOTDIR)
+ ohshite(_("cannot remove old backup config file `%.250s' (of `%.250s')"),
+ removevb.buf, conff->name);
+ }
+ pop_cleanup(ehflag_normaltidy); /* closedir */
+
+ }
+
+ pkg->installed.conffiles= 0;
+ modstatdb_note(pkg);
+
+ maintainer_script_installed(pkg, POSTRMFILE, "post-removal",
+ "purge", (char*)0);
+}
+
+void removal_bulk(struct pkginfo *pkg) {
+ /* This is used both by deferred_remove in this file, and at
+ * the end of process_archive in archives.c if it needs to finish
+ * removing a conflicting package.
+ */
+
+ int pkgnameused;
+ int foundpostrm= 0;
+ const char *postrmfilename;
+
+ debug(dbg_general,"removal_bulk package %s",pkg->name);
+
+ if (pkg->status == stat_halfinstalled || pkg->status == stat_unpacked) {
+
+ removal_bulk_remove_files(pkg, &foundpostrm);
+
+ } else {
+ struct stat stab;
+
+ postrmfilename= pkgadminfile(pkg,POSTRMFILE);
+ if (!lstat(postrmfilename,&stab)) foundpostrm= 1;
+ else if (errno == ENOENT) foundpostrm= 0;
+ else ohshite(_("unable to check existence of `%.250s'"),postrmfilename);
+
+ }
+
+ debug(dbg_general, "removal_bulk purging? foundpostrm=%d",foundpostrm);
+
+ if (!foundpostrm && !pkg->installed.conffiles) {
+ /* If there are no config files and no postrm script then we
+ * go straight into `purge'.
+ */
+ debug(dbg_general, "removal_bulk no postrm, no conffiles, purging");
+ pkg->want= want_purge;
+
+ } else if (pkg->want == want_purge) {
+
+ removal_bulk_remove_configfiles(pkg);
+
+ }
+
+ if (pkg->want == want_purge) {
+ /* ie, either of the two branches above. */
+ static struct varbuf fnvb;
+
+ /* retry empty directories, and warn on any leftovers that aren't */
+ removal_bulk_remove_leftover_dirs(pkg);
+
+ varbufreset(&fnvb);
+ varbufaddstr(&fnvb,admindir);
+ varbufaddstr(&fnvb,"/" INFODIR);
+ varbufaddstr(&fnvb,pkg->name);
+ pkgnameused= fnvb.used;
+
+ varbufaddstr(&fnvb,"." LISTFILE);
+ varbufaddc(&fnvb,0);
+ debug(dbg_general, "removal_bulk purge done, removing list `%s'",fnvb.buf);
+ if (unlink(fnvb.buf) && errno != ENOENT) ohshite(_("cannot remove old files list"));
+
+ fnvb.used= pkgnameused;
+ varbufaddstr(&fnvb,"." POSTRMFILE);
+ varbufaddc(&fnvb,0);
+ debug(dbg_general, "removal_bulk purge done, removing postrm `%s'",fnvb.buf);
+ if (unlink(fnvb.buf) && errno != ENOENT) ohshite(_("can't remove old postrm script"));
+
+ pkg->status= stat_notinstalled;
+
+ /* This will mess up reverse links, but if we follow them
+ * we won't go back because pkg->status is stat_notinstalled.
+ */
+ pkg->installed.depends= 0;
+ pkg->installed.essential= 0;
+ pkg->installed.description= pkg->installed.maintainer= 0;
+ pkg->installed.source= pkg->installed.installedsize= 0;
+ blankversion(&pkg->installed.version);
+ pkg->installed.arbs= 0;
+ }
+
+ pkg->eflag= eflagv_ok;
+ modstatdb_note(pkg);
+
+ debug(dbg_general, "removal done");
+}
+
diff --git a/src/select.c b/src/select.c
new file mode 100644
index 000000000..a8becab28
--- /dev/null
+++ b/src/select.c
@@ -0,0 +1,146 @@
+/*
+ * dpkg - main program for package management
+ * select.c - by-hand (rather than dselect-based) package selection
+ *
+ * Copyright (C) 1995,1996 Ian Jackson <ian@chiark.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 dpkg; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <config.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <fnmatch.h>
+#include <ctype.h>
+
+#include <dpkg.h>
+#include <dpkg-db.h>
+
+#include "filesdb.h"
+#include "main.h"
+
+static void getsel1package(struct pkginfo *pkg) {
+ int l;
+
+ if (pkg->want == want_unknown) return;
+ l= strlen(pkg->name); l >>= 3; l= 6-l; if (l<1) l=1;
+ printf("%s%.*s%s\n",pkg->name,l,"\t\t\t\t\t\t",wantinfos[pkg->want].name);
+}
+
+void getselections(const char *const *argv) {
+ struct pkgiterator *it;
+ struct pkginfo *pkg;
+ struct pkginfo **pkgl;
+ const char *thisarg;
+ int np, i, head, found;
+
+ modstatdb_init(admindir,msdbrw_readonly);
+
+ np= countpackages();
+ pkgl= m_malloc(sizeof(struct pkginfo*)*np);
+ it= iterpkgstart(); i=0;
+ while ((pkg= iterpkgnext(it))) {
+ assert(i<np);
+ pkgl[i++]= pkg;
+ }
+ iterpkgend(it);
+ assert(i==np);
+
+ qsort(pkgl,np,sizeof(struct pkginfo*),pkglistqsortcmp);
+ head=0;
+
+ if (!*argv) {
+ for (i=0; i<np; i++) {
+ pkg= pkgl[i];
+ if (pkg->status == stat_notinstalled) continue;
+ getsel1package(pkg);
+ }
+ } else {
+ while ((thisarg= *argv++)) {
+ found= 0;
+ for (i=0; i<np; i++) {
+ pkg= pkgl[i];
+ if (fnmatch(thisarg,pkg->name,0)) continue;
+ getsel1package(pkg); found++;
+ }
+ if (!found)
+ fprintf(stderr,_("No packages found matching %s.\n"),thisarg);
+ }
+ }
+ if (ferror(stdout)) werr("stdout");
+ if (ferror(stderr)) werr("stderr");
+}
+
+void setselections(const char *const *argv) {
+ const struct namevalue *nvp;
+ struct pkginfo *pkg;
+ const char *e;
+ int c, lno;
+ struct varbuf namevb;
+ struct varbuf selvb;
+
+ if (*argv) badusage(_("--set-selections does not take any argument"));
+
+ modstatdb_init(admindir,msdbrw_write);
+
+ varbufinit(&namevb);
+ varbufinit(&selvb);
+ lno= 1;
+ for (;;) {
+ varbufreset(&namevb);
+ varbufreset(&selvb);
+ do { c= getchar(); if (c == '\n') lno++; } while (c != EOF && isspace(c));
+ if (c == EOF) break;
+ if (c == '#') {
+ do { c= getchar(); if (c == '\n') lno++; } while (c != EOF && c != '\n');
+ continue;
+ }
+ while (!isspace(c)) {
+ varbufaddc(&namevb,c);
+ c= getchar();
+ if (c == EOF) ohshit(_("unexpected eof in package name at line %d"),lno);
+ if (c == '\n') ohshit(_("unexpected end of line in package name at line %d"),lno);
+ }
+ while (c != EOF && isspace(c)) {
+ c= getchar();
+ if (c == EOF) ohshit(_("unexpected eof after package name at line %d"),lno);
+ if (c == '\n') ohshit(_("unexpected end of line after package name at line %d"),lno);
+ }
+ while (c != EOF && !isspace(c)) {
+ varbufaddc(&selvb,c);
+ c= getchar();
+ }
+ while (c != EOF && c != '\n') {
+ c= getchar();
+ if (!isspace(c))
+ ohshit(_("unexpected data after package and selection at line %d"),lno);
+ }
+ varbufaddc(&namevb,0);
+ varbufaddc(&selvb,0);
+ e= illegal_packagename(namevb.buf,0);
+ if (e) ohshit(_("illegal package name at line %d: %.250s"),lno,e);
+ for (nvp=wantinfos; nvp->name && strcmp(nvp->name,selvb.buf); nvp++);
+ if (!nvp->name) ohshit(_("unknown wanted status at line %d: %.250s"),lno,selvb.buf);
+ pkg= findpackage(namevb.buf);
+ pkg->want= nvp->value;
+ if (c == EOF) break;
+ lno++;
+ }
+ if (ferror(stdin)) ohshite(_("read error on standard input"));
+ modstatdb_shutdown();
+ varbufreset(&namevb);
+ varbufreset(&selvb);
+}
diff --git a/src/update.c b/src/update.c
new file mode 100644
index 000000000..14ba4eaf6
--- /dev/null
+++ b/src/update.c
@@ -0,0 +1,128 @@
+/*
+ * dpkg - main program for package management
+ * update.c - options which update the `available' database
+ *
+ * Copyright (C) 1995 Ian Jackson <ian@chiark.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 dpkg; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <config.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fnmatch.h>
+#include <assert.h>
+#include <unistd.h>
+
+#include <dpkg.h>
+#include <dpkg-db.h>
+#include <myopt.h>
+
+#include "main.h"
+
+void updateavailable(const char *const *argv) {
+ const char *sourcefile= argv[0];
+ int count= 0;
+ static struct varbuf vb;
+
+ switch (cipaction->arg) {
+ case act_avclear:
+ if (sourcefile) badusage(_("--%s takes no arguments"),cipaction->olong);
+ break;
+ case act_avreplace: case act_avmerge:
+ if (!sourcefile || argv[1])
+ badusage(_("--%s needs exactly one Packages file argument"),cipaction->olong);
+ break;
+ default:
+ internerr("bad cipaction->arg in updateavailable");
+ }
+
+ if (!f_noact) {
+ if (access(admindir,W_OK)) {
+ if (errno != EACCES)
+ ohshite(_("unable to access dpkg status area for bulk available update"));
+ else
+ ohshit(_("bulk available update requires write access to dpkg status area"));
+ }
+ lockdatabase(admindir);
+ }
+
+ switch (cipaction->arg) {
+ case act_avreplace:
+ printf(_("Replacing available packages info, using %s.\n"),sourcefile);
+ break;
+ case act_avmerge:
+ printf(_("Updating available packages info, using %s.\n"),sourcefile);
+ break;
+ case act_avclear:
+ break;
+ default:
+ internerr("bad cipaction->arg in update available");
+ }
+
+ varbufaddstr(&vb,admindir);
+ varbufaddstr(&vb,"/" AVAILFILE);
+ varbufaddc(&vb,0);
+
+ if (cipaction->arg == act_avmerge)
+ parsedb(vb.buf, pdb_recordavailable|pdb_rejectstatus, 0,0,0);
+
+ if (cipaction->arg != act_avclear)
+ count+= parsedb(sourcefile, pdb_recordavailable|pdb_rejectstatus, 0,0,0);
+
+ if (!f_noact) {
+ writedb(vb.buf,1,0);
+ unlockdatabase(admindir);
+ }
+
+ if (cipaction->arg != act_avclear)
+ printf(_("Information about %d package(s) was updated.\n"),count);
+}
+
+void forgetold(const char *const *argv) {
+ struct pkgiterator *it;
+ struct pkginfo *pkg;
+ enum pkgwant oldwant;
+
+ if (*argv) badusage(_("--forget-old-unavail takes no arguments"));
+
+ modstatdb_init(admindir, f_noact ? msdbrw_readonly : msdbrw_write);
+
+ it= iterpkgstart();
+ while ((pkg= iterpkgnext(it))) {
+ debug(dbg_eachfile,"forgetold checking %s",pkg->name);
+ if (informative(pkg,&pkg->available)) {
+ debug(dbg_eachfile,"forgetold ... informative available");
+ continue;
+ }
+ if (pkg->want != want_purge && pkg->want != want_deinstall) {
+ debug(dbg_eachfile,"forgetold ... informative want");
+ continue;
+ }
+ oldwant= pkg->want;
+ pkg->want= want_unknown;
+ if (informative(pkg,&pkg->installed)) {
+ debug(dbg_eachfile,"forgetold ... informative installed");
+ pkg->want= oldwant;
+ continue;
+ }
+ debug(dbg_general,"forgetold forgetting %s",pkg->name);
+ }
+ iterpkgend(it);
+
+ modstatdb_shutdown();
+}