summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.in91
-rw-r--r--lib/compat.c102
-rw-r--r--lib/database.c249
-rw-r--r--lib/dbmodify.c256
-rwxr-xr-xlib/debugmake3
-rw-r--r--lib/dump.c289
-rw-r--r--lib/ehandle.c276
-rw-r--r--lib/fields.c354
-rw-r--r--lib/lock.c82
-rw-r--r--lib/mlib.c124
-rw-r--r--lib/myopt.c84
-rw-r--r--lib/nfmalloc.c91
-rw-r--r--lib/parse.c371
-rw-r--r--lib/parsedump.h73
-rw-r--r--lib/parsehelp.c153
-rw-r--r--lib/showcright.c35
-rw-r--r--lib/star.c158
-rw-r--r--lib/tarfn.c165
-rw-r--r--lib/varbuf.c65
-rw-r--r--lib/vercmp.c64
20 files changed, 3085 insertions, 0 deletions
diff --git a/lib/Makefile.in b/lib/Makefile.in
new file mode 100644
index 000000000..076c1ae7f
--- /dev/null
+++ b/lib/Makefile.in
@@ -0,0 +1,91 @@
+# Copyright (C) 1994 Ian Murdock <imurdock@debian.org>
+# Copyright (C) 1994,1995 Ian Jackson <ijackson@nyx.cs.du.edu>
+#
+# 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.
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+prefix = @prefix@
+copyingfile = $(prefix)/doc/copyright/dpkg
+
+SRC = ehandle.c mlib.c parse.c parsehelp.c fields.c dump.c nfmalloc.c \
+ varbuf.c database.c myopt.c vercmp.c compat.c lock.c dbmodify.c \
+ showcright.c tarfn.c star.c
+OBJ = ehandle.o mlib.o parse.o parsehelp.o fields.o dump.o nfmalloc.o \
+ varbuf.o database.o myopt.o vercmp.o compat.o lock.o dbmodify.o \
+ showcright.o tarfn.o star.o
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+
+CC = @CC@
+
+CFLAGS = @CFLAGS@ @CWARNS@ $(XCFLAGS) -g # fixme: remove -g
+OPTCFLAGS = @OPTCFLAGS@
+LDFLAGS = -s $(XLDFLAGS)
+ALL_CFLAGS = -I../include -I.. @DEFS@ $(CFLAGS)
+ALL_CFLAGS_OPT = $(ALL_CFLAGS) $(OPTCFLAGS)
+
+AR = ar crv
+RANLIB = @RANLIB@
+
+.SUFFIXES: .c .o
+
+all: libdpkg.a
+
+libdpkg.a: $(OBJ)
+ $(AR) libdpkg.a $(OBJ)
+ $(RANLIB) libdpkg.a
+
+showcright.o: showcright.c
+ $(CC) -DCOPYINGFILE=\"$(copyingfile)\" $(ALL_CFLAGS) -c $<
+
+.c.o:
+ $(CC) $(ALL_CFLAGS) -c $<
+
+# These next few files are very heavily used, and should be optimised
+# for speed rather than space. (ALL_CFLAGS_OPT usually means -O3.)
+database.o: database.c
+ $(CC) $(ALL_CFLAGS_OPT) -c $<
+
+fields.o: fields.c
+ $(CC) $(ALL_CFLAGS_OPT) -c $<
+
+nfmalloc.o: nfmalloc.c
+ $(CC) $(ALL_CFLAGS_OPT) -c $<
+
+parse.o: parse.c
+ $(CC) $(ALL_CFLAGS_OPT) -c $<
+
+parsehelp.o: parsehelp.c
+ $(CC) $(ALL_CFLAGS_OPT) -c $<
+
+varbuf.o: varbuf.c
+ $(CC) $(ALL_CFLAGS_OPT) -c $<
+
+clean:
+ rm -f *.o core libdpkg.a
+
+distclean: clean
+ rm -f Makefile *.orig *~ *.~* ./#*#
+
+install: all
+
+dump.o fields.o parse.o parsehelp.o vercmp.o: parsedump.h
+$(OBJ): ../include/dpkg.h ../include/dpkg-db.h
+myopt.o: ../include/myopt.h
+tarfn.o star.o: ../include/tarfn.h
diff --git a/lib/compat.c b/lib/compat.c
new file mode 100644
index 000000000..94ae40341
--- /dev/null
+++ b/lib/compat.c
@@ -0,0 +1,102 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * compat.c - compatibility functions
+ *
+ * Copyright (C) 1995 Ian Jackson <iwj10@cus.cam.ac.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 <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+
+#include "config.h"
+
+#ifndef HAVE_STRERROR
+extern const char *const sys_errlist[];
+extern const int sys_nerr;
+const char *strerror(int e) {
+ static char buf[100];
+ if (e >= 0 && e < sys_nerr) return sys_errlist[e];
+ sprintf(buf, "System error no.%d", e);
+ return buf;
+}
+#endif
+
+#ifndef HAVE_STRSIGNAL
+extern const char *const sys_siglist[];
+const char *strsignal(int e) {
+ static char buf[100];
+ if (e >= 0 && e < NSIG) return sys_siglist[e];
+ sprintf(buf, "Signal no.%d", e);
+ return buf;
+}
+#endif
+
+#ifndef HAVE_SCANDIR
+
+static int (*scandir_comparfn)(const void*, const void*);
+static int scandir_compar(const void *a, const void *b) {
+ return scandir_comparfn(*(const struct dirent**)a,*(const struct dirent**)b);
+}
+
+int scandir(const char *dir, struct dirent ***namelist,
+ int (*select)(const struct dirent *),
+ int (*compar)(const void*, const void*)) {
+ DIR *d;
+ int used, avail;
+ struct dirent *e, *m;
+ d= opendir(dir); if (!d) return -1;
+ used=0; avail=20;
+ *namelist= malloc(avail*sizeof(struct dirent*));
+ if (!*namelist) return -1;
+ while ((e= readdir(d)) != 0) {
+ if (!select(e)) continue;
+ m= malloc(sizeof(struct dirent) + strlen(e->d_name));
+ if (!m) return -1;
+ *m= *e;
+ strcpy(m->d_name,e->d_name);
+ if (used >= avail-1) {
+ avail+= avail;
+ *namelist= realloc(*namelist, avail*sizeof(struct dirent*));
+ if (!*namelist) return -1;
+ }
+ (*namelist)[used]= m;
+ used++;
+ }
+ (*namelist)[used]= 0;
+ scandir_comparfn= compar;
+ qsort(*namelist, used, sizeof(struct dirent*), scandir_compar);
+ return used;
+}
+#endif
+
+#ifndef HAVE_ALPHASORT
+int alphasort(const struct dirent *a, const struct dirent *b) {
+ return strcmp(a->d_name,b->d_name);
+}
+#endif
+
+#ifndef HAVE_UNSETENV
+void unsetenv(const char *p) {
+ char *q;
+ q= malloc(strlen(p)+3); if (!q) return;
+ strcpy(q,p); strcat(q,"="); putenv(q);
+}
+#endif
diff --git a/lib/database.c b/lib/database.c
new file mode 100644
index 000000000..9561a0fde
--- /dev/null
+++ b/lib/database.c
@@ -0,0 +1,249 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * dpkg-db.h - Low level package database routines (hash tables, etc.)
+ *
+ * Copyright (C) 1995 Ian Jackson <iwj10@cus.cam.ac.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 <ctype.h>
+#include <string.h>
+
+#include "config.h"
+#include "dpkg.h"
+#include "dpkg-db.h"
+
+#define BINS (1 << 7)
+ /* This must always be a power of two. If you change it
+ * consider changing the per-character hashing factor (currently 5) too.
+ */
+
+static struct pkginfo *bins[BINS];
+static int npackages;
+
+static int hash(const char *name) {
+ int v= 0;
+ while (*name) { v *= 5; v += tolower(*name); name++; }
+ return v;
+/* These results were achieved with 128 bins, and the list of packages
+ * shown at the bottom of this file.
+ */
+/* while (*name) { v *= 17; v += tolower(*name); name++; }
+ * size 5 occurs 1 times
+ * size 4 occurs 0 times
+ * size 3 occurs 7 times
+ * size 2 occurs 32 times
+ * size 1 occurs 51 times
+ * size 0 occurs 37 times
+ */
+/* while (*name) { v *= 5; v += tolower(*name); name++; }
+ * size 4 occurs 1 times
+ * size 3 occurs 14 times
+ * size 2 occurs 20 times
+ * size 1 occurs 55 times
+ * size 0 occurs 38 times
+ */
+/* while (*name) { v *= 11; v += tolower(*name); name++; }
+ * size 5 occurs 1 times
+ * size 4 occurs 4 times
+ * size 3 occurs 9 times
+ * size 2 occurs 25 times
+ * size 1 occurs 43 times
+ * size 0 occurs 46 times
+ */
+/* while (*name) { v *= 31; v += tolower(*name); name++; }
+ * size 6 occurs 1 times
+ * size 5 occurs 0 times
+ * size 4 occurs 1 times
+ * size 3 occurs 11 times
+ * size 2 occurs 27 times
+ * size 1 occurs 44 times
+ * size 0 occurs 44 times
+ */
+/* while (*name) { v *= 111; v += tolower(*name); name++; }
+ * size 5 occurs 1 times
+ * size 4 occurs 1 times
+ * size 3 occurs 14 times
+ * size 2 occurs 23 times
+ * size 1 occurs 44 times
+ * size 0 occurs 45 times
+ */
+/* while (*name) { v += (v<<3); v += tolower(*name); name++; }
+ * size 4 occurs 5 times
+ * size 3 occurs 12 times
+ * size 2 occurs 22 times
+ * size 1 occurs 41 times
+ * size 0 occurs 48 times
+ */
+/* while (*name) { v *= 29; v += tolower(*name); name++; }
+ * size 5 occurs 1 times
+ * size 4 occurs 2 times
+ * size 3 occurs 10 times
+ * size 2 occurs 26 times
+ * size 1 occurs 46 times
+ * size 0 occurs 43 times
+ */
+/* while (*name) { v *= 13; v += tolower(*name); name++; }
+ * size 5 occurs 1 times
+ * size 4 occurs 2 times
+ * size 3 occurs 5 times
+ * size 2 occurs 30 times
+ * size 1 occurs 53 times
+ * size 0 occurs 37 times
+ */
+}
+
+void blankpackage(struct pkginfo *pigp) {
+ pigp->name= 0;
+ pigp->status= stat_notinstalled;
+ pigp->eflag= eflagv_ok;
+ pigp->want= want_unknown;
+ pigp->priority= pri_unknown;
+ pigp->section= pigp->configversion= pigp->configrevision= 0;
+ pigp->files= 0;
+ pigp->installed.valid= 0;
+ pigp->available.valid= 0;
+ pigp->clientdata= 0;
+}
+
+void blankpackageperfile(struct pkginfoperfile *pifp) {
+ pifp->essential= 0;
+ pifp->depends= 0;
+ pifp->depended= 0;
+ pifp->description= pifp->maintainer= pifp->source= 0;
+ pifp->architecture= pifp->version= pifp->revision= 0;
+ pifp->conffiles= 0;
+ pifp->arbs= 0;
+ pifp->valid= 1;
+}
+
+static int nes(const char *s) { return s && *s; }
+
+int informativeperfile(struct pkginfoperfile *info) {
+ /* Used by dselect as an aid to decide whether to display things. */
+ if (!info->valid) return 0;
+ if (info->depends ||
+ nes(info->description) ||
+ nes(info->maintainer) ||
+ nes(info->source) ||
+ nes(info->architecture) ||
+ nes(info->version) ||
+ nes(info->revision) ||
+ info->conffiles ||
+ info->arbs) return 1;
+ return 0;
+}
+
+int informative(struct pkginfo *pkg) {
+ return ((pkg->want != want_unknown && pkg->want != want_purge) ||
+ pkg->eflag != eflagv_ok ||
+ pkg->status != stat_notinstalled ||
+ nes(pkg->section) ||
+ pkg->files ||
+ pkg->priority != pri_unknown);
+}
+
+struct pkginfo *findpackage(const char *name) {
+ struct pkginfo **pointerp, *newpkg;
+
+ pointerp= bins + (hash(name) & (BINS-1));
+ while (*pointerp && strcasecmp((*pointerp)->name,name))
+ pointerp= &(*pointerp)->next;
+ if (*pointerp) return *pointerp;
+
+ newpkg= nfmalloc(sizeof(struct pkginfo));
+ blankpackage(newpkg);
+ newpkg->name= nfstrsave(name);
+ newpkg->next= 0;
+ *pointerp= newpkg;
+ npackages++;
+
+ return newpkg;
+}
+
+int countpackages(void) {
+ return npackages;
+}
+
+struct pkgiterator {
+ struct pkginfo *pigp;
+ int nbinn;
+};
+
+struct pkgiterator *iterpkgstart(void) {
+ struct pkgiterator *i;
+ i= m_malloc(sizeof(struct pkgiterator));
+ i->pigp= 0;
+ i->nbinn= 0;
+ return i;
+}
+
+struct pkginfo *iterpkgnext(struct pkgiterator *i) {
+ struct pkginfo *r;
+ while (!i->pigp) {
+ if (i->nbinn >= BINS) return 0;
+ i->pigp= bins[i->nbinn++];
+ }
+ r= i->pigp; i->pigp= r->next; return r;
+}
+
+void iterpkgend(struct pkgiterator *i) {
+ free(i);
+}
+
+void resetpackages(void) {
+ int i;
+ nffreeall();
+ npackages= 0;
+ for (i=0; i<BINS; i++) bins[i]= 0;
+}
+
+void hashreport(FILE *file) {
+ int i, c;
+ struct pkginfo *pkg;
+ int *freq;
+
+ freq= m_malloc(sizeof(int)*npackages+1);
+ for (i=0; i<=npackages; i++) freq[i]= 0;
+ for (i=0; i<BINS; i++) {
+ for (c=0, pkg= bins[i]; pkg; c++, pkg= pkg->next);
+ fprintf(file,"bin %5d has %7d\n",i,c);
+ freq[c]++;
+ }
+ for (i=npackages; i>0 && freq[i]==0; i--);
+ while (i>=0) { fprintf(file,"size %7d occurs %5d times\n",i,freq[i]); i--; }
+ if (ferror(file)) ohshite("failed write during hashreport");
+}
+
+/*
+ * Test dataset package names were:
+ *
+ * agetty bash bc bdflush biff bin86 binutil binutils bison bsdutils
+ * byacc chfn cron dc dictionaries diff dlltools dpkg e2fsprogs ed
+ * elisp19 elm emacs emacs-nox emacs-x emacs19 file fileutils find
+ * flex fsprogs gas gawk gcc gcc1 gcc2 gdb ghostview ghstview glibcdoc
+ * gnuplot grep groff gs gs_both gs_svga gs_x gsfonts gxditviw gzip
+ * hello hostname idanish ifrench igerman indent inewsinn info inn
+ * ispell kbd kern1148 language ldso less libc libgr libgrdev librl
+ * lilo linuxsrc login lout lpr m4 mailx make man manpages more mount
+ * mtools ncurses netbase netpbm netstd patch perl4 perl5 procps
+ * psutils rcs rdev sed sendmail seyon shar shellutils smail svgalib
+ * syslogd sysvinit tar tcpdump tcsh tex texidoc texinfo textutils
+ * time timezone trn unzip uuencode wenglish wu-ftpd x8514 xaxe xbase
+ * xbdm2 xcomp xcoral xdevel xfig xfnt100 xfnt75 xfntbig xfntscl
+ * xgames xherc xmach32 xmach8 xmono xnet xs3 xsvga xtexstuff xv
+ * xvga16 xxgdb zip
+ */
diff --git a/lib/dbmodify.c b/lib/dbmodify.c
new file mode 100644
index 000000000..9fdd646d3
--- /dev/null
+++ b/lib/dbmodify.c
@@ -0,0 +1,256 @@
+/*
+ * dpkg - main program for package management
+ * dbmodify.c - routines for managing dpkg database updates
+ *
+ * Copyright (C) 1994,1995 Ian Jackson <iwj10@cus.cam.ac.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 <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 <assert.h>
+
+#include "config.h"
+#include "dpkg.h"
+#include "dpkg-db.h"
+
+char *statusfile=0, *availablefile=0;
+
+static enum modstatdb_rw cstatus=-1, cflags=0;
+static char *importanttmpfile=0;
+static FILE *importanttmp;
+static int nextupdate;
+static int updateslength;
+static char *updatefnbuf, *updatefnrest;
+static const char *admindir;
+static struct varbuf uvb;
+
+static int ulist_select(const struct dirent *de) {
+ const char *p;
+ int l;
+ for (p= de->d_name, l=0; *p; p++, l++)
+ if (!isdigit(*p)) return 0;
+ if (l > IMPORTANTMAXLEN)
+ ohshit("updates directory contains file `%.250s' whose name is too long "
+ "(length=%d, max=%d)", de->d_name, l, IMPORTANTMAXLEN);
+ if (updateslength == -1) updateslength= l;
+ else if (l != updateslength)
+ ohshit("updates directory contains files with different length names "
+ "(both %d and %d)", l, updateslength);
+ return 1;
+}
+
+static void cleanupdates(void) {
+ struct dirent **cdlist;
+ int cdn, i;
+
+ parsedb(statusfile, pdb_weakclassification, 0,0,0);
+
+ *updatefnrest= 0;
+ updateslength= -1;
+ cdn= scandir(updatefnbuf, &cdlist, &ulist_select, alphasort);
+ if (cdn == -1) ohshite("cannot scan updates directory `%.255s'",updatefnbuf);
+
+ if (cdn) {
+
+ for (i=0; i<cdn; i++) {
+ strcpy(updatefnrest, cdlist[i]->d_name);
+ parsedb(updatefnbuf, pdb_weakclassification, 0,0,0);
+ if (cstatus < msdbrw_write) free(cdlist[i]);
+ }
+
+ if (cstatus >= msdbrw_write) {
+ writedb(statusfile,0,1);
+
+ for (i=0; i<cdn; i++) {
+ strcpy(updatefnrest, cdlist[i]->d_name);
+ if (unlink(updatefnbuf))
+ ohshite("failed to remove incorporated update file %.255s",updatefnbuf);
+ free(cdlist[i]);
+ }
+ }
+
+ }
+ free(cdlist);
+
+ nextupdate= 0;
+}
+
+static void createimptmp(void) {
+ int i;
+
+ onerr_abort++;
+
+ importanttmp= fopen(importanttmpfile,"w");
+ if (!importanttmp) ohshite("unable to create %.250s",importanttmpfile);
+ for (i=0; i<512; i++) fputs("#padding\n",importanttmp);
+ if (ferror(importanttmp))
+ ohshite("unable to fill %.250s with padding",importanttmpfile);
+ if (fflush(importanttmp))
+ ohshite("unable flush %.250s after padding",importanttmpfile);
+ if (fseek(importanttmp,0,SEEK_SET))
+ ohshite("unable seek to start of %.250s after padding",importanttmpfile);
+
+ onerr_abort--;
+}
+
+enum modstatdb_rw modstatdb_init(const char *adir, enum modstatdb_rw readwritereq) {
+ static const struct fni { const char *suffix; char **store; } fnis[]= {
+ { STATUSFILE, &statusfile },
+ { AVAILFILE, &availablefile },
+ { UPDATESDIR IMPORTANTTMP, &importanttmpfile },
+ { 0 }
+ }, *fnip;
+
+ admindir= adir;
+
+ for (fnip=fnis; fnip->suffix; fnip++) {
+ free(*fnip->store);
+ *fnip->store= m_malloc(strlen(adir)+strlen(fnip->suffix)+2);
+ sprintf(*fnip->store, "%s/%s", adir, fnip->suffix);
+ }
+
+ cflags= readwritereq & msdbrw_flagsmask;
+ readwritereq &= ~msdbrw_flagsmask;
+
+ switch (readwritereq) {
+ case msdbrw_needsuperuser:
+ case msdbrw_needsuperuserlockonly:
+ if (getuid() || geteuid())
+ ohshit("requested operation requires superuser privilege");
+ /* fall through */
+ case msdbrw_write: case msdbrw_writeifposs:
+ if (access(adir,W_OK)) {
+ if (errno != EACCES)
+ ohshite("unable to access dpkg status area");
+ else if (readwritereq == msdbrw_write)
+ ohshit("operation requires read/write access to dpkg status area");
+ cstatus= msdbrw_readonly;
+ } else {
+ lockdatabase(adir);
+ cstatus= (readwritereq == msdbrw_needsuperuserlockonly ?
+ msdbrw_needsuperuserlockonly :
+ msdbrw_write);
+ }
+ break;
+ case msdbrw_readonly:
+ cstatus= msdbrw_readonly; break;
+ default:
+ internerr("unknown readwritereq");
+ }
+
+ updatefnbuf= m_malloc(strlen(adir)+sizeof(UPDATESDIR)+IMPORTANTMAXLEN+5);
+ strcpy(updatefnbuf,adir);
+ strcat(updatefnbuf,"/" UPDATESDIR);
+ updatefnrest= updatefnbuf+strlen(updatefnbuf);
+
+ cleanupdates();
+
+ if (cstatus >= msdbrw_write) {
+ createimptmp();
+ uvb.used= 0;
+ uvb.size= 10240;
+ uvb.buf= m_malloc(uvb.size);
+ }
+
+ if (cstatus != msdbrw_needsuperuserlockonly) {
+ parsedb(statusfile, pdb_weakclassification, 0,0,0);
+ parsedb(statusfile, pdb_recordavailable, 0,0,0);
+ parsedb(availablefile,
+ pdb_recordavailable|pdb_rejectstatus|
+ (cflags & msdbrw_availablepreferversion ? pdb_preferversion : 0),
+ 0,0,0);
+ }
+
+ return cstatus;
+}
+
+static void checkpoint(void) {
+ int i;
+
+ assert(cstatus >= msdbrw_write);
+ writedb(statusfile,0,1);
+
+ for (i=0; i<nextupdate; i++) {
+ sprintf(updatefnrest, IMPORTANTFMT, i);
+ assert(strlen(updatefnrest)<=IMPORTANTMAXLEN); /* or we've made a real mess */
+ if (unlink(updatefnbuf))
+ ohshite("failed to remove my own update file %.255s",updatefnbuf);
+ }
+ nextupdate= 0;
+}
+
+void modstatdb_shutdown(void) {
+ switch (cstatus) {
+ case msdbrw_write:
+ checkpoint();
+ if (!(cflags & msdbrw_availablepreferversion))
+ writedb(availablefile,1,0);
+
+ /* tidy up a bit, but don't worry too much about failure */
+ fclose(importanttmp);
+ strcpy(updatefnrest, IMPORTANTTMP); unlink(updatefnbuf);
+ varbuffree(&uvb);
+ /* fall through */
+ case msdbrw_needsuperuserlockonly:
+ unlockdatabase(admindir);
+ default:
+ break;
+ }
+
+ free(updatefnbuf);
+}
+
+void modstatdb_note(struct pkginfo *pkg) {
+ assert(cstatus >= msdbrw_write);
+
+ onerr_abort++;
+
+ varbufreset(&uvb);
+ varbufrecord(&uvb, pkg, &pkg->installed);
+ if (fwrite(uvb.buf, 1, uvb.used, importanttmp) != uvb.used)
+ ohshite("unable to write updated status of `%.250s'", pkg->name);
+ if (fflush(importanttmp))
+ ohshite("unable to flush updated status of `%.250s'", pkg->name);
+ if (ftruncate(fileno(importanttmp), uvb.used))
+ ohshite("unable to truncate for updated status of `%.250s'", pkg->name);
+ if (fsync(fileno(importanttmp)))
+ ohshite("unable to fsync updated status of `%.250s'", pkg->name);
+ if (fclose(importanttmp))
+ ohshite("unable to close updated status of `%.250s'", pkg->name);
+ sprintf(updatefnrest, IMPORTANTFMT, nextupdate);
+ if (rename(importanttmpfile, updatefnbuf))
+ ohshite("unable to install updated status of `%.250s'", pkg->name);
+ assert(strlen(updatefnrest)<=IMPORTANTMAXLEN); /* or we've made a real mess */
+
+ nextupdate++;
+
+ if (nextupdate > MAXUPDATES) checkpoint();
+
+ createimptmp();
+
+ onerr_abort--;
+}
diff --git a/lib/debugmake b/lib/debugmake
new file mode 100755
index 000000000..57ac0b17d
--- /dev/null
+++ b/lib/debugmake
@@ -0,0 +1,3 @@
+#!/bin/sh
+set -x
+make 'XCFLAGS=-g -DMDEBUG -O0' LDFLAGS=-g 'LIBS= -lefence -L../lib -ldpkg' "$@"
diff --git a/lib/dump.c b/lib/dump.c
new file mode 100644
index 000000000..97d5c4304
--- /dev/null
+++ b/lib/dump.c
@@ -0,0 +1,289 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * dump.c - code to write in-core database to a file
+ *
+ * Copyright (C) 1995 Ian Jackson <iwj10@cus.cam.ac.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: don't write uninteresting packages */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "config.h"
+#include "dpkg.h"
+#include "dpkg-db.h"
+#include "parsedump.h"
+
+void w_name(struct varbuf *vb,
+ const struct pkginfo *pigp, const struct pkginfoperfile *pifp,
+ const struct fieldinfo *fip) {
+ assert(pigp->name);
+ varbufaddstr(vb,"Package: "); varbufaddstr(vb, pigp->name);
+ varbufaddc(vb,'\n');
+}
+
+void w_version(struct varbuf *vb,
+ const struct pkginfo *pigp, const struct pkginfoperfile *pifp,
+ const struct fieldinfo *fip) {
+ /* Revision information is printed in version field too. */
+ if ((!pifp->version || !*pifp->version) &&
+ (!pifp->revision || !*pifp->revision)) return;
+ varbufaddstr(vb,"Version: ");
+ varbufaddstr(vb,pifp->version ? pifp->version : "");
+ if (pifp->revision && *pifp->revision) {
+ varbufaddc(vb,'-');
+ varbufaddstr(vb,pifp->revision);
+ }
+ varbufaddc(vb,'\n');
+}
+
+void w_configversion(struct varbuf *vb,
+ const struct pkginfo *pigp, const struct pkginfoperfile *pifp,
+ const struct fieldinfo *fip) {
+ if ((!pigp->configversion || !*pigp->configversion) &&
+ (!pigp->configrevision || !*pigp->configrevision)) return;
+ if (pifp != &pigp->installed) return;
+ if (pigp->status == stat_installed || pigp->status == stat_notinstalled) return;
+ varbufaddstr(vb,"Config-Version: ");
+ varbufaddstr(vb,pigp->configversion ? pigp->configversion : "");
+ if (pigp->configrevision && *pigp->configrevision) {
+ varbufaddc(vb,'-');
+ varbufaddstr(vb,pigp->configrevision);
+ }
+ varbufaddc(vb,'\n');
+}
+
+void w_null(struct varbuf *vb,
+ const struct pkginfo *pigp, const struct pkginfoperfile *pifp,
+ const struct fieldinfo *fip) {
+}
+
+void w_section(struct varbuf *vb,
+ const struct pkginfo *pigp, const struct pkginfoperfile *pifp,
+ const struct fieldinfo *fip) {
+ const char *value= pigp->section;
+ if (!value || !*value) return;
+ varbufaddstr(vb,"Section: ");
+ varbufaddstr(vb,value);
+ varbufaddc(vb,'\n');
+}
+
+void w_charfield(struct varbuf *vb,
+ const struct pkginfo *pigp, const struct pkginfoperfile *pifp,
+ const struct fieldinfo *fip) {
+ const char *value= pifp->valid ? PKGPFIELD(pifp,fip->integer,char*) : 0;
+ if (!value || !*value) return;
+ varbufaddstr(vb,fip->name); varbufaddstr(vb, ": "); varbufaddstr(vb,value);
+ varbufaddc(vb,'\n');
+}
+
+void w_filecharf(struct varbuf *vb,
+ const struct pkginfo *pigp, const struct pkginfoperfile *pifp,
+ const struct fieldinfo *fip) {
+ struct filedetails *fdp;
+
+ if (pifp != &pigp->available) return;
+ fdp= pigp->files;
+ if (!fdp || !FILEFFIELD(fdp,fip->integer,char*)) return;
+ varbufaddstr(vb,fip->name); varbufaddc(vb,':');
+ while (fdp) {
+ varbufaddc(vb,' ');
+ varbufaddstr(vb,FILEFFIELD(fdp,fip->integer,char*));
+ fdp= fdp->next;
+ }
+ varbufaddc(vb,'\n');
+}
+
+void w_booleandefno(struct varbuf *vb,
+ const struct pkginfo *pigp, const struct pkginfoperfile *pifp,
+ const struct fieldinfo *fip) {
+ int value= pifp->valid ? PKGPFIELD(pifp,fip->integer,int) : 0;
+ if (!value) return;
+ assert(value==1);
+ varbufaddstr(vb,"Essential: yes\n");
+}
+
+void w_priority(struct varbuf *vb,
+ const struct pkginfo *pigp, const struct pkginfoperfile *pifp,
+ const struct fieldinfo *fip) {
+ if (pigp->priority == pri_unknown) return;
+ assert(pigp->priority <= pri_unknown);
+ varbufaddstr(vb,"Priority: ");
+ varbufaddstr(vb,
+ pigp->priority == pri_other
+ ? pigp->otherpriority
+ : priorityinfos[pigp->priority].name);
+ varbufaddc(vb,'\n');
+}
+
+void w_status(struct varbuf *vb,
+ const struct pkginfo *pigp, const struct pkginfoperfile *pifp,
+ const struct fieldinfo *fip) {
+ if (pifp != &pigp->installed) return;
+ assert(pigp->want <= want_purge &&
+ pigp->eflag <= eflagv_both &&
+ pigp->status <= stat_configfiles);
+ varbufaddstr(vb,"Status: ");
+ varbufaddstr(vb,wantinfos[pigp->want].name); varbufaddc(vb,' ');
+ varbufaddstr(vb,eflaginfos[pigp->eflag].name); varbufaddc(vb,' ');
+ varbufaddstr(vb,statusinfos[pigp->status].name); varbufaddc(vb,'\n');
+}
+
+void varbufdependency(struct varbuf *vb, struct dependency *dep) {
+ struct deppossi *dop;
+ const char *possdel;
+
+ possdel= "";
+ for (dop= dep->list; dop; dop= dop->next) {
+ assert(dop->up == dep);
+ varbufaddstr(vb,possdel); possdel= " | ";
+ varbufaddstr(vb,dop->ed->name);
+ if (dop->verrel != dvr_none) {
+ varbufaddstr(vb," (");
+ switch (dop->verrel) {
+ case dvr_exact: varbufaddc(vb,'='); break;
+ case dvr_laterequal: varbufaddstr(vb,">="); break;
+ case dvr_earlierequal: varbufaddstr(vb,"<="); break;
+ case dvr_laterstrict: varbufaddstr(vb,">>"); break;
+ case dvr_earlierstrict: varbufaddstr(vb,"<<"); break;
+ default: internerr("unknown verrel");
+ }
+ if (!isalnum(dop->version[0])) varbufaddc(vb,' ');
+ varbufaddstr(vb,dop->version);
+ if (dop->revision) { varbufaddc(vb,'-'); varbufaddstr(vb,dop->revision); }
+ varbufaddc(vb,')');
+ }
+ }
+}
+
+void w_dependency(struct varbuf *vb,
+ const struct pkginfo *pigp, const struct pkginfoperfile *pifp,
+ const struct fieldinfo *fip) {
+ char fnbuf[50];
+ const char *depdel;
+ struct dependency *dyp;
+
+ if (!pifp->valid) return;
+ sprintf(fnbuf,"%s: ",fip->name); depdel= fnbuf;
+ for (dyp= pifp->depends; dyp; dyp= dyp->next) {
+ if (dyp->type != fip->integer) continue;
+ assert(dyp->up == pigp);
+ varbufaddstr(vb,depdel); depdel= ", ";
+ varbufdependency(vb,dyp);
+ }
+ if (depdel != fnbuf) varbufaddc(vb,'\n');
+}
+
+void w_conffiles(struct varbuf *vb,
+ const struct pkginfo *pigp, const struct pkginfoperfile *pifp,
+ const struct fieldinfo *fip) {
+ struct conffile *i;
+
+ if (!pifp->valid || !pifp->conffiles || pifp == &pigp->available) return;
+ varbufaddstr(vb,"Conffiles:\n");
+ for (i=pifp->conffiles; i; i= i->next) {
+ varbufaddc(vb,' '); varbufaddstr(vb,i->name); varbufaddc(vb,' ');
+ varbufaddstr(vb,i->hash); varbufaddc(vb,'\n');
+ }
+}
+
+void varbufrecord(struct varbuf *vb,
+ const struct pkginfo *pigp, const struct pkginfoperfile *pifp) {
+ const struct fieldinfo *fip;
+ const struct arbitraryfield *afp;
+
+ for (fip= fieldinfos; fip->name; fip++) {
+ fip->wcall(vb,pigp,pifp,fip);
+ }
+ if (pifp->valid) {
+ for (afp= pifp->arbs; afp; afp= afp->next) {
+ varbufaddstr(vb,afp->name); varbufaddstr(vb,": ");
+ varbufaddstr(vb,afp->value); varbufaddc(vb,'\n');
+ }
+ }
+}
+
+void writerecord(FILE *file, const char *filename,
+ const struct pkginfo *pigp, const struct pkginfoperfile *pifp) {
+ struct varbuf vb;
+
+ varbufinit(&vb);
+ varbufrecord(&vb,pigp,pifp);
+ varbufaddc(&vb,'\0');
+ if (!fputs(vb.buf,file))
+ ohshite("failed to write details of `%.50s' to `%.250s'", pigp->name, filename);
+}
+
+void writedb(const char *filename, int available, int mustsync) {
+ static char writebuf[8192];
+
+ struct pkgiterator *it;
+ struct pkginfo *pigp;
+ char *oldfn, *newfn;
+ const char *which;
+ FILE *file;
+ struct varbuf vb;
+
+ which= available ? "available" : "status";
+ oldfn= m_malloc(strlen(filename)+sizeof(OLDDBEXT));
+ strcpy(oldfn,filename); strcat(oldfn,OLDDBEXT);
+ newfn= m_malloc(strlen(filename)+sizeof(NEWDBEXT));
+ strcpy(newfn,filename); strcat(newfn,NEWDBEXT);
+ varbufinit(&vb);
+
+ file= fopen(newfn,"w");
+ if (!file) ohshite("failed to open `%s' for writing %s information",filename,which);
+
+ if (setvbuf(file,writebuf,_IOFBF,sizeof(writebuf)))
+ ohshite("unable to set buffering on status file");
+
+ it= iterpkgstart();
+ while ((pigp= iterpkgnext(it)) != 0) {
+ if (!(informative(pigp) ||
+ informativeperfile(&pigp->available) ||
+ informativeperfile(&pigp->installed)))
+ /* Don't dump records which have no useful content. */
+ continue;
+ varbufrecord(&vb, pigp, available ? &pigp->available : &pigp->installed);
+ varbufaddc(&vb,'\n'); varbufaddc(&vb,0);
+ if (!fputs(vb.buf,file))
+ ohshite("failed to write %s record about `%.50s' to `%.250s'",
+ which, pigp->name, filename);
+ varbufreset(&vb);
+ }
+ varbuffree(&vb);
+ if (mustsync) {
+ if (fflush(file))
+ ohshite("failed to flush %s information to `%.250s'", which, filename);
+ if (fsync(fileno(file)))
+ ohshite("failed to fsync %s information to `%.250s'", which, filename);
+ }
+ if (fclose(file)) ohshite("failed to close `%.250s' after writing %s information",
+ filename, which);
+ unlink(oldfn);
+ if (link(filename,oldfn) && errno != ENOENT)
+ ohshite("failed to link `%.250s' to `%.250s' for backup of %s info",
+ filename, oldfn, which);
+ if (rename(newfn,filename))
+ ohshite("failed to install `%.250s' as `%.250s' containing %s info",
+ newfn, filename, which);
+}
diff --git a/lib/ehandle.c b/lib/ehandle.c
new file mode 100644
index 000000000..f0843b9d7
--- /dev/null
+++ b/lib/ehandle.c
@@ -0,0 +1,276 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * ehandle.c - error handling
+ *
+ * Copyright (C) 1994,1995 Ian Jackson <iwj10@cus.cam.ac.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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <assert.h>
+
+#include "config.h"
+#include "dpkg.h"
+#include "dpkg-db.h"
+
+static const char *errmsg; /* points to errmsgbuf or malloc'd */
+static char errmsgbuf[4096];
+/* 6x255 for inserted strings (%.255s &c in fmt; also %s with limited length arg)
+ * 1x255 for constant text
+ * 1x255 for strerror()
+ * same again just in case.
+ */
+
+#define NCALLS 2
+
+struct cleanupentry {
+ struct cleanupentry *next;
+ struct {
+ int mask;
+ void (*call)(int argc, void **argv);
+ } calls[NCALLS];
+ int cpmask, cpvalue;
+ int argc;
+ void *argv[1];
+};
+
+struct errorcontext {
+ struct errorcontext *next;
+ jmp_buf *jbufp;
+ struct cleanupentry *cleanups;
+ void (*printerror)(const char *emsg, const char *contextstring);
+ const char *contextstring;
+};
+
+static struct errorcontext *volatile econtext= 0;
+static struct { struct cleanupentry ce; void *args[20]; } emergency;
+
+void set_error_display(void (*printerror)(const char *, const char *),
+ const char *contextstring) {
+ assert(econtext);
+ econtext->printerror= printerror;
+ econtext->contextstring= contextstring;
+}
+
+void push_error_handler(jmp_buf *jbufp,
+ void (*printerror)(const char *, const char *),
+ const char *contextstring) {
+ struct errorcontext *necp;
+ necp= malloc(sizeof(struct errorcontext));
+ if (!necp) {
+ int e= errno;
+ strcpy(errmsgbuf, "out of memory pushing error handler: ");
+ strcat(errmsgbuf, strerror(e)); errmsg= errmsgbuf;
+ if (econtext) longjmp(*econtext->jbufp,1);
+ fprintf(stderr, "%s: %s\n", thisname, errmsgbuf); exit(2);
+ }
+ necp->next= econtext;
+ necp->jbufp= jbufp;
+ necp->cleanups= 0;
+ necp->printerror= printerror;
+ necp->contextstring= contextstring;
+ econtext= necp;
+ onerr_abort= 0;
+}
+
+static void print_error_cleanup(const char *emsg, const char *contextstring) {
+ fprintf(stderr, "%s: error while cleaning up:\n %s\n",thisname,emsg);
+}
+
+static void run_cleanups(struct errorcontext *econ, int flagsetin) {
+ static volatile int preventrecurse= 0;
+ struct cleanupentry *volatile cep;
+ struct cleanupentry *ncep;
+ struct errorcontext recurserr, *oldecontext;
+ jmp_buf recurejbuf;
+ volatile int i, flagset;
+
+ if (econ->printerror) econ->printerror(errmsg,econ->contextstring);
+
+ if (++preventrecurse > 3) {
+ onerr_abort++;
+ fprintf(stderr, DPKG ": too many nested errors during error recovery !!\n");
+ flagset= 0;
+ } else {
+ flagset= flagsetin;
+ }
+ cep= econ->cleanups;
+ oldecontext= econtext;
+ while (cep) {
+ for (i=0; i<NCALLS; i++) {
+ if (cep->calls[i].call && cep->calls[i].mask & flagset) {
+ if (setjmp(recurejbuf)) {
+ run_cleanups(&recurserr, ehflag_bombout | ehflag_recursiveerror);
+ } else {
+ recurserr.jbufp= &recurejbuf;
+ recurserr.cleanups= 0;
+ recurserr.next= 0;
+ recurserr.printerror= print_error_cleanup;
+ recurserr.contextstring= 0;
+ econtext= &recurserr;
+ cep->calls[i].call(cep->argc,cep->argv);
+ }
+ econtext= oldecontext;
+ }
+ }
+ ncep= cep->next;
+ if (cep != &emergency.ce) free(cep);
+ cep= ncep;
+ }
+ preventrecurse--;
+}
+
+void error_unwind(int flagset) {
+ struct cleanupentry *cep;
+ struct errorcontext *tecp;
+
+ tecp= econtext;
+ cep= tecp->cleanups;
+ econtext= tecp->next;
+ run_cleanups(tecp,flagset);
+ free(tecp);
+}
+
+void push_checkpoint(int mask, int value) {
+ /* This will arrange that when error_unwind() is called,
+ * all previous cleanups will be executed with
+ * flagset= (original_flagset & mask) | value
+ * where original_flagset is the argument to error_unwind
+ * (as modified by any checkpoint which was pushed later).
+ */
+ struct cleanupentry *cep;
+ int i;
+
+ cep= m_malloc(sizeof(struct cleanupentry) + sizeof(char*));
+ for (i=0; i<NCALLS; i++) { cep->calls[i].call=0; cep->calls[i].mask=0; }
+ cep->cpmask= mask; cep->cpvalue= value;
+ cep->argc= 0; cep->argv[0]= 0;
+ cep->next= econtext->cleanups;
+ econtext->cleanups= cep;
+}
+
+void push_cleanup(void (*call1)(int argc, void **argv), int mask1,
+ void (*call2)(int argc, void **argv), int mask2,
+ int nargs, ...) {
+ struct cleanupentry *cep;
+ void **args;
+ int e;
+ va_list al;
+
+ onerr_abort++;
+
+ cep= malloc(sizeof(struct cleanupentry) + sizeof(char*)*(nargs+1));
+ if (!cep) {
+ if (nargs > sizeof(emergency.args)/sizeof(void*))
+ ohshite("out of memory for new cleanup entry with many arguments");
+ e= errno; cep= &emergency.ce;
+ }
+ cep->calls[0].call= call1; cep->calls[0].mask= mask1;
+ cep->calls[1].call= call2; cep->calls[1].mask= mask2;
+ cep->cpmask=~0; cep->cpvalue=0; cep->argc= nargs;
+ va_start(al,nargs);
+ args= cep->argv; while (nargs-- >0) *args++= va_arg(al,void*);
+ *args++= 0;
+ va_end(al);
+ cep->next= econtext->cleanups;
+ econtext->cleanups= cep;
+ if (cep == &emergency.ce) { e= errno; ohshite("out of memory for new cleanup entry"); }
+
+ onerr_abort--;
+}
+
+void pop_cleanup(int flagset) {
+ struct cleanupentry *cep;
+ int i;
+
+ cep= econtext->cleanups;
+ econtext->cleanups= cep->next;
+ for (i=0; i<NCALLS; i++) {
+ if (cep->calls[i].call && cep->calls[i].mask & flagset)
+ cep->calls[i].call(cep->argc,cep->argv);
+ flagset &= cep->cpmask;
+ flagset |= cep->cpvalue;
+ }
+ if (cep != &emergency.ce) free(cep);
+}
+
+void ohshit(const char *fmt, ...) {
+ va_list al;
+ va_start(al,fmt);
+ vsprintf(errmsgbuf,fmt,al);
+ va_end(al);
+ errmsg= errmsgbuf;
+ longjmp(*econtext->jbufp,1);
+}
+
+void print_error_fatal(const char *emsg, const char *contextstring) {
+ fprintf(stderr, "%s: %s\n",thisname,emsg);
+}
+
+void ohshitvb(struct varbuf *vb) {
+ char *m;
+ varbufaddc(vb,0);
+ m= m_malloc(strlen(vb->buf));
+ strcpy(m,vb->buf);
+ errmsg= m;
+ longjmp(*econtext->jbufp,1);
+}
+
+void ohshitv(const char *fmt, va_list al) {
+ vsprintf(errmsgbuf,fmt,al);
+ errmsg= errmsgbuf;
+ longjmp(*econtext->jbufp,1);
+}
+
+void ohshite(const char *fmt, ...) {
+ int e;
+ va_list al;
+
+ e=errno;
+ va_start(al,fmt);
+ vsprintf(errmsgbuf,fmt,al);
+ va_end(al);
+
+ strcat(errmsgbuf,": ");
+ strcat(errmsgbuf,strerror(e));
+ errmsg= errmsgbuf;
+ longjmp(*econtext->jbufp,1);
+}
+
+void badusage(const char *fmt, ...) {
+ va_list al;
+ va_start(al,fmt);
+ vsprintf(errmsgbuf,fmt,al);
+ va_end(al);
+ strcat(errmsgbuf,"\n\n");
+ strcat(errmsgbuf,printforhelp);
+ errmsg= errmsgbuf;
+ longjmp(*econtext->jbufp,1);
+}
+
+void werr(const char *fn) {
+ ohshite("error writing `%.250s'",fn);
+}
+
+void do_internerr(const char *string, int line, const char *file) {
+ fprintf(stderr,"%s:%d: internal error `%s'\n",file,line,string);
+ abort();
+}
diff --git a/lib/fields.c b/lib/fields.c
new file mode 100644
index 000000000..9066c0438
--- /dev/null
+++ b/lib/fields.c
@@ -0,0 +1,354 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * fields.c - parsing of all the different fields, when reading in
+ *
+ * Copyright (C) 1995 Ian Jackson <iwj10@cus.cam.ac.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 <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "config.h"
+#include "dpkg.h"
+#include "dpkg-db.h"
+#include "parsedump.h"
+
+static int convert_string(const char *filename, int lno, const char *what, int otherwise,
+ FILE *warnto, int *warncount, const struct pkginfo *pigp,
+ const char *startp, const struct namevalue *nvip,
+ const char **endpp) {
+ const char *ep;
+ int c, l;
+
+ ep= startp;
+ if (!*ep) parseerr(0,filename,lno, warnto,warncount,pigp,0, "%s is missing",what);
+ while ((c= *ep) && !isspace(c)) ep++;
+ l= (int)(ep-startp);
+ while (nvip->name && (strncasecmp(nvip->name,startp,l) || nvip->name[l])) nvip++;
+ if (!nvip->name) {
+ if (otherwise != -1) return otherwise;
+ parseerr(0,filename,lno, warnto,warncount,pigp,0, "`%.*s' is not allowed for %s",
+ l > 50 ? 50 : l, startp, what);
+ }
+ while (isspace(c)) c= *++ep;
+ if (c && !endpp)
+ parseerr(0,filename,lno, warnto,warncount,pigp,0, "junk after %s",what);
+ if (endpp) *endpp= ep;
+ return nvip->value;
+}
+
+void f_name(struct pkginfo *pigp, struct pkginfoperfile *pifp, enum parsedbflags flags,
+ const char *filename, int lno, FILE *warnto, int *warncount,
+ const char *value, const struct fieldinfo *fip) {
+ const char *e;
+ if ((e= illegal_packagename(value,0)) != 0)
+ parseerr(0,filename,lno, warnto,warncount,pigp,0, "invalid package name (%.250s)",e);
+ pigp->name= nfstrsave(value);
+ findpackage(value); /* We discard the value from findpackage, but calling it
+ * forces an entry in the hash table to be made if it isn't
+ * already. This is so that we don't reorder the file
+ * unnecessarily (doing so is bad for debugging).
+ */
+}
+
+void f_filecharf(struct pkginfo *pigp, struct pkginfoperfile *pifp,
+ enum parsedbflags flags,
+ const char *filename, int lno, FILE *warnto, int *warncount,
+ const char *value, const struct fieldinfo *fip) {
+ struct filedetails *fdp, **fdpp;
+ char *cpos, *space;
+ int allowextend;
+
+ if (!*value)
+ parseerr(0,filename,lno, warnto,warncount,pigp,0,
+ "empty file details field `%s'",fip->name);
+ if (!(flags & pdb_recordavailable))
+ parseerr(0,filename,lno, warnto,warncount,pigp,0,
+ "file details field `%s' not allowed in status file",fip->name);
+ allowextend= !pigp->files;
+ fdpp= &pigp->files;
+ cpos= nfstrsave(value);
+ while (*cpos) {
+ space= cpos; while (*space && !isspace(*space)) space++;
+ if (*space) *space++= 0;
+ fdp= *fdpp;
+ if (!fdp) {
+ if (!allowextend)
+ parseerr(0,filename,lno, warnto,warncount,pigp,0, "too many values "
+ "in file details field `%s' (compared to others)",fip->name);
+ fdp= nfmalloc(sizeof(struct filedetails));
+ fdp->next= 0;
+ fdp->name= fdp->msdosname= fdp->size= fdp->md5sum= 0;
+ *fdpp= fdp;
+ }
+ FILEFFIELD(fdp,fip->integer,char*)= cpos;
+ fdpp= &fdp->next;
+ while (*space && isspace(*space)) space++;
+ cpos= space;
+ }
+ if (*fdpp)
+ parseerr(0,filename,lno, warnto,warncount,pigp,0, "too few values "
+ "in file details field `%s' (compared to others)",fip->name);
+}
+
+void f_charfield(struct pkginfo *pigp, struct pkginfoperfile *pifp,
+ enum parsedbflags flags,
+ const char *filename, int lno, FILE *warnto, int *warncount,
+ const char *value, const struct fieldinfo *fip) {
+ if (*value) PKGPFIELD(pifp,fip->integer,char*)= nfstrsave(value);
+}
+
+void f_boolean(struct pkginfo *pigp, struct pkginfoperfile *pifp,
+ enum parsedbflags flags,
+ const char *filename, int lno, FILE *warnto, int *warncount,
+ const char *value, const struct fieldinfo *fip) {
+ pifp->essential=
+ *value ? convert_string(filename,lno,"yes/no in `essential' field", -1,
+ warnto,warncount,pigp,
+ value,booleaninfos,0)
+ : 0;
+}
+
+void f_section(struct pkginfo *pigp, struct pkginfoperfile *pifp,
+ enum parsedbflags flags,
+ const char *filename, int lno, FILE *warnto, int *warncount,
+ const char *value, const struct fieldinfo *fip) {
+ if (!*value) return;
+ pigp->section= nfstrsave(value);
+}
+
+void f_priority(struct pkginfo *pigp, struct pkginfoperfile *pifp,
+ enum parsedbflags flags,
+ const char *filename, int lno, FILE *warnto, int *warncount,
+ const char *value, const struct fieldinfo *fip) {
+ if (!*value) return;
+ pigp->priority= convert_string(filename,lno,"word in `priority' field", pri_other,
+ warnto,warncount,pigp,
+ value,priorityinfos,0);
+ if (pigp->priority == pri_other) pigp->otherpriority= nfstrsave(value);
+}
+
+void f_status(struct pkginfo *pigp, struct pkginfoperfile *pifp,
+ enum parsedbflags flags,
+ const char *filename, int lno, FILE *warnto, int *warncount,
+ const char *value, const struct fieldinfo *fip) {
+ const char *ep;
+
+ if (flags & pdb_rejectstatus)
+ parseerr(0,filename,lno, warnto,warncount,pigp,0,
+ "value for `status' field not allowed in this context");
+ if (flags & pdb_recordavailable) return;
+
+ pigp->want= convert_string(filename,lno,"first (want) word in `status' field", -1,
+ warnto,warncount,pigp, value,wantinfos,&ep);
+ pigp->eflag= convert_string(filename,lno,"second (error) word in `status' field", -1,
+ warnto,warncount,pigp, ep,eflaginfos,&ep);
+ pigp->status= convert_string(filename,lno,"third (status) word in `status' field", -1,
+ warnto,warncount,pigp, ep,statusinfos,0);
+}
+
+void f_configversion(struct pkginfo *pigp, struct pkginfoperfile *pifp,
+ enum parsedbflags flags,
+ const char *filename, int lno, FILE *warnto, int *warncount,
+ const char *value, const struct fieldinfo *fip) {
+ char *mycopy, *hyphen;
+
+ if (flags & pdb_rejectstatus)
+ parseerr(0,filename,lno, warnto,warncount,pigp,0,
+ "value for `config-version' field not allowed in this context");
+ if (flags & pdb_recordavailable) return;
+
+ mycopy= nfstrsave(value);
+ hyphen= strrchr(mycopy,'-');
+ if (hyphen) *hyphen++= 0;
+
+ pigp->configversion= mycopy;
+ pigp->configrevision= hyphen ? hyphen : nfstrsave("");
+}
+
+void f_conffiles(struct pkginfo *pigp, struct pkginfoperfile *pifp,
+ enum parsedbflags flags,
+ const char *filename, int lno, FILE *warnto, int *warncount,
+ const char *value, const struct fieldinfo *fip) {
+ struct conffile **lastp, *newlink;
+ const char *endent, *endfn;
+ int c, namelen, hashlen;
+
+ lastp= &pifp->conffiles;
+ while (*value) {
+ c= *value++;
+ if (c == '\n') continue;
+ if (c != ' ') parseerr(0,filename,lno, warnto,warncount,pigp,0, "value for"
+ " `conffiles' has line starting with non-space `%c'", c);
+ for (endent= value; (c= *endent)!=0 && c != '\n'; endent++);
+ for (endfn= endent; *endfn != ' '; endfn--);
+ if (endfn <= value+1 || endfn >= endent-1)
+ parseerr(0,filename,lno, warnto,warncount,pigp,0,
+ "value for `conffiles' has malformatted line `%.*s'",
+ (int)(endent-value > 250 ? 250 : endent-value), value);
+ newlink= nfmalloc(sizeof(struct conffile));
+ value= skip_slash_dotslash(value);
+ namelen= (int)(endfn-value);
+ if (namelen <= 0) parseerr(0,filename,lno, warnto,warncount,pigp,0,
+ "root or null directory is listed as a conffile");
+ newlink->name= nfmalloc(namelen+2);
+ newlink->name[0]= '/';
+ memcpy(newlink->name+1,value,namelen);
+ newlink->name[namelen+1]= 0;
+ hashlen= (int)(endent-endfn)-1; newlink->hash= nfmalloc(hashlen+1);
+ memcpy(newlink->hash,endfn+1,hashlen); newlink->hash[hashlen]= 0;
+ newlink->next =0;
+ *lastp= newlink;
+ lastp= &newlink->next;
+ value= endent;
+ }
+}
+
+void f_dependency(struct pkginfo *pigp, struct pkginfoperfile *pifp,
+ enum parsedbflags flags,
+ const char *filename, int lno, FILE *warnto, int *warncount,
+ const char *value, const struct fieldinfo *fip) {
+ char *q, c1, c2;
+ const char *p, *emsg;
+ struct varbuf depname, version;
+ struct dependency *dyp, **ldypp;
+ struct deppossi *dop, **ldopp;
+
+ if (!*value) return; /* empty fields are ignored */
+ varbufinit(&depname); varbufinit(&version);
+ p= value;
+ ldypp= &pifp->depends; while (*ldypp) ldypp= &(*ldypp)->next;
+ for (;;) { /* loop creating new struct dependency's */
+ dyp= nfmalloc(sizeof(struct dependency));
+ dyp->up= 0; /* Set this to zero for now, as we don't know what our real
+ * struct pkginfo address (in the database) is going to be yet.
+ */
+ dyp->next= 0; *ldypp= dyp; ldypp= &dyp->next;
+ dyp->list= 0; ldopp= &dyp->list;
+ dyp->type= fip->integer;
+ for (;;) { /* loop creating new struct deppossi's */
+ varbufreset(&depname);
+ while (*p && !isspace(*p) && *p != '(' && *p != ',' && *p != '|') {
+ varbufaddc(&depname,*p); p++;
+ }
+ varbufaddc(&depname,0);
+ if (!*depname.buf)
+ parseerr(0,filename,lno, warnto,warncount,pigp,0, "`%s' field, missing"
+ " package name, or garbage where package name expected", fip->name);
+ emsg= illegal_packagename(depname.buf,0);
+ if (emsg) parseerr(0,filename,lno, warnto,warncount,pigp,0, "`%s' field,"
+ " invalid package name `%.255s': %s",
+ fip->name, depname.buf, emsg);
+ dop= nfmalloc(sizeof(struct deppossi));
+ dop->up= dyp;
+ dop->ed= findpackage(depname.buf);
+ dop->next= 0; *ldopp= dop; ldopp= &dop->next;
+ dop->nextrev= 0; /* Don't link this (which is after all only `newpig' from
+ dop->backrev= 0; * the main parsing loop in parsedb) into the depended on
+ * packages' lists yet. This will be done later when we
+ * install this (in parse.c). For the moment we do the
+ * `forward' links in deppossi (`ed') only, and the backward
+ * links from the depended on packages to dop are left undone.
+ */
+ dop->cyclebreak= 0;
+ while (isspace(*p)) p++;
+ if (*p == '(') {
+ varbufreset(&version);
+ p++; while (isspace(*p)) p++;
+ c1= *p;
+ if (c1 == '<' || c1 == '>') {
+ c2= *++p;
+ dop->verrel= (c1 == '<') ? dvrf_earlier : dvrf_later;
+ if (c2 == '=') {
+ dop->verrel |= (dvrf_orequal | dvrf_builtup);
+ p++;
+ } else if (c2 == c1) {
+ dop->verrel |= (dvrf_strict | dvrf_builtup);
+ p++;
+ } else if (c2 == '<' || c2 == '>') {
+ parseerr(0,filename,lno, warnto,warncount,pigp,0,
+ "`%s' field, reference to `%.255s':\n"
+ " bad version relationship %c%c",
+ fip->name,depname.buf,c1,c2);
+ dop->verrel= dvr_none;
+ } else {
+ parseerr(0,filename,lno, warnto,warncount,pigp,1,
+ "`%s' field, reference to `%.255s':\n"
+ " `%c' is obsolete, use `%c=' or `%c%c' instead",
+ fip->name,depname.buf,c1,c1,c1,c1);
+ dop->verrel |= (dvrf_orequal | dvrf_builtup);
+ }
+ } else if (c1 == '=') {
+ dop->verrel= dvr_exact;
+ p++;
+ } else {
+ parseerr(0,filename,lno, warnto,warncount,pigp,1,
+ "`%s' field, reference to `%.255s':\n"
+ " implicit exact match on version number, suggest using `=' instead",
+ fip->name,depname.buf);
+ dop->verrel= dvr_exact;
+ }
+ if (!isspace(*p) && !isalnum(*p)) {
+ parseerr(0,filename,lno, warnto,warncount,pigp,1,
+ "`%s' field, reference to `%.255s':\n"
+ " version value starts with non-alphanumeric, suggest adding a space",
+ fip->name,depname.buf);
+ }
+ while (isspace(*p)) p++;
+ while (*p && *p != ')' && *p != '(') {
+ if (!isspace(*p)) varbufaddc(&version,*p);
+ p++;
+ }
+ if (*p == '(') parseerr(0,filename,lno, warnto,warncount,pigp,0,
+ "`%s' field, reference to `%.255s'"
+ " version contains (", fip->name, depname.buf);
+ else if (*p == 0) parseerr(0,filename,lno, warnto,warncount,pigp,0,
+ "`%s' field, reference to `%.255s'"
+ "version unterminated", fip->name, depname.buf);
+ varbufaddc(&version,0);
+ q= strrchr(version.buf,'-');
+ if (q) {
+ *q++= 0;
+ dop->revision= nfstrsave(q);
+ } else {
+ dop->revision= 0;
+ }
+ dop->version= nfstrsave(version.buf);
+ p++; while (isspace(*p)) p++;
+ } else {
+ dop->verrel= dvr_none;
+ dop->revision= dop->version= 0;
+ }
+ if (!*p || *p == ',') break;
+ if (*p != '|')
+ parseerr(0,filename,lno, warnto,warncount,pigp,0, "`%s' field, syntax"
+ " error after reference to package `%.255s'",
+ fip->name, dop->ed->name);
+ if (fip->integer == dep_conflicts ||
+ fip->integer == dep_provides ||
+ fip->integer == dep_replaces)
+ parseerr(0,filename,lno, warnto,warncount,pigp,0,
+ "alternatives (`|') not allowed in %s field",
+ fip->name);
+ p++; while (isspace(*p)) p++;
+ }
+ if (!*p) break;
+ p++; while (isspace(*p)) p++;
+ }
+}
+
diff --git a/lib/lock.c b/lib/lock.c
new file mode 100644
index 000000000..e0af62a78
--- /dev/null
+++ b/lib/lock.c
@@ -0,0 +1,82 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * lock.c - packages database locking
+ *
+ * Copyright (C) 1994,1995 Ian Jackson <iwj10@cus.cam.ac.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 <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <sys/file.h>
+
+#include "config.h"
+#include "dpkg.h"
+#include "dpkg-db.h"
+
+static char *dblockfile= 0;
+static int dblockfd= -1;
+
+static void cu_unlockdb(int argc, void **argv) {
+ struct flock fl;
+ assert(dblockfile);
+ assert(dblockfd >= 0);
+ fl.l_type= F_UNLCK;
+ fl.l_whence= SEEK_SET;
+ fl.l_start= 0;
+ fl.l_len= 1;
+ if (fcntl(dblockfd,F_SETLK,&fl) == -1)
+ ohshite("unable to unlock dpkg status database");
+}
+
+void unlockdatabase(const char *admindir) {
+ pop_cleanup(ehflag_normaltidy); /* calls cu_unlockdb */
+}
+
+void lockdatabase(const char *admindir) {
+ int n;
+ struct flock fl;
+
+ if (!dblockfile) {
+ n= strlen(admindir);
+ dblockfile= m_malloc(n+sizeof(LOCKFILE)+2);
+ strcpy(dblockfile,admindir);
+ strcpy(dblockfile+n, "/" LOCKFILE);
+ }
+ if (dblockfd == -1) {
+ dblockfd= open(dblockfile, O_RDWR|O_CREAT|O_TRUNC, 0660);
+ if (dblockfd == -1) {
+ if (errno == EPERM)
+ ohshit("you do not have permission to lock the dpkg status database");
+ ohshite("unable to open/create status database lockfile");
+ }
+ }
+ fl.l_type= F_WRLCK;
+ fl.l_whence= SEEK_SET;
+ fl.l_start= 0;
+ fl.l_len= 1;
+ if (fcntl(dblockfd,F_SETLK,&fl) == -1) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN)
+ ohshit("status database area is locked - another dpkg/dselect is running");
+ ohshite("unable to lock dpkg status database");
+ }
+ push_cleanup(cu_unlockdb,~0, 0,0, 0);
+}
diff --git a/lib/mlib.c b/lib/mlib.c
new file mode 100644
index 000000000..5fee2a406
--- /dev/null
+++ b/lib/mlib.c
@@ -0,0 +1,124 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * mlib.c - `must' library: routines will succeed or longjmp
+ *
+ * Copyright (C) 1994,1995 Ian Jackson <iwj10@cus.cam.ac.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 <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+
+#include "config.h"
+#include "dpkg.h"
+
+/* Incremented when we do some kind of generally necessary operation, so that
+ * loops &c know to quit if we take an error exit. Decremented again afterwards.
+ */
+volatile int onerr_abort= 0;
+
+void *m_malloc(size_t amount) {
+#ifdef MDEBUG
+ unsigned short *r2, x;
+#endif
+ void *r;
+
+ onerr_abort++;
+ r= malloc(amount);
+ if (!r) ohshite("malloc failed (%ld bytes)",(long)amount);
+ onerr_abort--;
+
+#ifdef MDEBUG
+ r2= r; x= (unsigned short)amount ^ 0xf000;
+ while (amount >= 2) { *r2++= x; amount -= 2; }
+#endif
+ return r;
+}
+
+void *m_realloc(void *r, size_t amount) {
+ onerr_abort++;
+ r= realloc(r,amount);
+ if (!r) ohshite("realloc failed (%ld bytes)",(long)amount);
+ onerr_abort--;
+
+ return r;
+}
+
+static void print_error_forked(const char *emsg, const char *contextstring) {
+ fprintf(stderr, "%s (subprocess): %s\n", thisname, emsg);
+}
+
+static void cu_m_fork(int argc, void **argv) {
+ exit(2);
+ /* Don't do the other cleanups, because they'll be done by/in the parent
+ * process.
+ */
+}
+
+int m_fork(void) {
+ pid_t r;
+ r= fork();
+ if (r == -1) { onerr_abort++; ohshite("fork failed"); }
+ if (r > 0) return r;
+ push_cleanup(cu_m_fork,~0, 0,0, 0);
+ set_error_display(print_error_forked,0);
+ return r;
+}
+
+void m_dup2(int oldfd, int newfd) {
+ const char *const stdstrings[]= { "in", "out", "err" };
+
+ if (dup2(oldfd,newfd) == newfd) return;
+
+ onerr_abort++;
+ if (newfd < 3) ohshite("failed to dup for std%s",stdstrings[newfd]);
+ ohshite("failed to dup for fd %d",newfd);
+}
+
+void m_pipe(int *fds) {
+ if (!pipe(fds)) return;
+ onerr_abort++;
+ ohshite("failed to create pipe");
+}
+
+void checksubprocerr(int status, const char *description, int sigpipeok) {
+ int n;
+ if (WIFEXITED(status)) {
+ n= WEXITSTATUS(status); if (!n) return;
+ ohshit("subprocess %s returned error exit status %d",description,n);
+ } else if (WIFSIGNALED(status)) {
+ n= WTERMSIG(status); if (!n || (sigpipeok && n==SIGPIPE)) return;
+ ohshit("subprocess %s killed by signal (%s)%s",
+ description, strsignal(n), WCOREDUMP(status) ? ", core dumped" : "");
+ } else {
+ ohshit("subprocess %s failed with wait status code %d",description,status);
+ }
+}
+
+void waitsubproc(pid_t pid, const char *description, int sigpipeok) {
+ pid_t r;
+ int status;
+
+ while ((r= waitpid(pid,&status,0)) == -1 && errno == EINTR);
+ if (r != pid) { onerr_abort++; ohshite("wait for %s failed",description); }
+ checksubprocerr(status,description,sigpipeok);
+}
diff --git a/lib/myopt.c b/lib/myopt.c
new file mode 100644
index 000000000..b3c216739
--- /dev/null
+++ b/lib/myopt.c
@@ -0,0 +1,84 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * myopt.c - my very own option parsing
+ *
+ * Copyright (C) 1994,1995 Ian Jackson <iwj10@cus.cam.ac.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 this file; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+
+#include "config.h"
+#include "myopt.h"
+#include "dpkg.h"
+
+void myopt(const char *const **argvp, const struct cmdinfo *cmdinfos) {
+ const struct cmdinfo *cip;
+ const char *p, *value;
+ int l;
+
+ ++(*argvp);
+ while ((p= **argvp) && *p == '-') {
+ ++(*argvp);
+ if (!strcmp(p,"--")) break;
+ if (*++p == '-') {
+ ++p; value=0;
+ for (cip= cmdinfos;
+ cip->olong || cip->oshort;
+ cip++) {
+ if (!cip->olong) continue;
+ if (!strcmp(p,cip->olong)) break;
+ l= strlen(cip->olong);
+ if (!strncmp(p,cip->olong,l) &&
+ (p[l]== ((cip->takesvalue==2) ? '-' : '='))) { value=p+l+1; break; }
+ }
+ if (!cip->olong) badusage("unknown option --%s",p);
+ if (cip->takesvalue) {
+ if (!value) {
+ value= *(*argvp)++;
+ if (!value) badusage("--%s option takes a value",cip->olong);
+ }
+ if (cip->call) cip->call(cip,value);
+ else *cip->sassignto= value;
+ } else {
+ if (value) badusage("--%s option does not take a value",cip->olong);
+ if (cip->call) cip->call(cip,0);
+ else *cip->iassignto= cip->arg;
+ }
+ } else {
+ while (*p) {
+ for (cip= cmdinfos; (cip->olong || cip->oshort) && *p != cip->oshort; cip++);
+ if (!cip->oshort) badusage("unknown option -%c",*p);
+ p++;
+ if (cip->takesvalue) {
+ if (!*p) {
+ value= *(*argvp)++;
+ if (!value) badusage("-%c option takes a value",cip->oshort);
+ } else {
+ value= p; p="";
+ if (*value == '=') value++;
+ }
+ if (cip->call) cip->call(cip,value);
+ else *cip->sassignto= value;
+ } else {
+ if (*p == '=') badusage("-%c option does not take a value",cip->oshort);
+ if (cip->call) cip->call(cip,0);
+ else *cip->iassignto= cip->arg;
+ }
+ }
+ }
+ }
+}
diff --git a/lib/nfmalloc.c b/lib/nfmalloc.c
new file mode 100644
index 000000000..225e69846
--- /dev/null
+++ b/lib/nfmalloc.c
@@ -0,0 +1,91 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * nfmalloc.c - non-freeing malloc, used for in-core database
+ *
+ * Copyright (C) 1994,1995 Ian Jackson <iwj10@cus.cam.ac.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 <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+#include "dpkg.h"
+#include "dpkg-db.h"
+
+#define ABLOCKSIZE 65536
+#define UNIQUE 4096
+
+union maxalign {
+ long l; long double d;
+ void *pv; char *pc; union maxalign *ps; void (*pf)(void);
+};
+
+static unsigned char *playground= 0;
+static long remaining= 0;
+static struct piece { struct piece *next; union maxalign space; } *pieces= 0;
+
+void nffreeall(void) {
+ struct piece *a,*b;
+ for (a=pieces; a; a=b) { b=a->next; free(a); }
+ pieces= 0; remaining= 0;
+}
+
+static void *nfmalloc_sysmalloc(size_t size) {
+ struct piece *alc;
+ alc= m_malloc(size + sizeof(struct piece));
+ alc->next= pieces; pieces= alc;
+ return &alc->space;
+}
+
+#ifndef MDEBUG
+void *nfmalloc(size_t size) {
+#else
+static void *nfmalloc_r(size_t size) {
+#endif
+ const size_t alignment= sizeof(union maxalign);
+
+ size -= (size + alignment-1) % alignment;
+ size += alignment-1;
+ if (size > UNIQUE) return nfmalloc_sysmalloc(size);
+ remaining -= size;
+ if (remaining > 0) return playground -= size;
+ playground= (unsigned char*)nfmalloc_sysmalloc(ABLOCKSIZE) + ABLOCKSIZE - size;
+ remaining= ABLOCKSIZE-size;
+ return playground;
+}
+
+#ifdef MDEBUG
+/* If we haven't switched off debugging we wrap nfmalloc in something
+ * that fills the space with junk that may tell us what happened if
+ * we dereference a wild pointer. It's better than leaving it full of
+ * nulls, anyway.
+ */
+void *nfmalloc(size_t size) {
+ unsigned short *r, *r2, x;
+ r= nfmalloc_r(size); r2=r; x= (unsigned short)size;
+ while (size >= 2) { *r2++= x; size -= 2; }
+ return r;
+}
+#endif
+
+char *nfstrsave(const char *string) {
+ char *r;
+
+ r= nfmalloc(strlen(string)+1);
+ strcpy(r,string);
+ return r;
+}
diff --git a/lib/parse.c b/lib/parse.c
new file mode 100644
index 000000000..15be97b52
--- /dev/null
+++ b/lib/parse.c
@@ -0,0 +1,371 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * parse.c - database file parsing, main package/field loop
+ *
+ * Copyright (C) 1995 Ian Jackson <iwj10@cus.cam.ac.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 <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+#include "config.h"
+#include "dpkg.h"
+#include "dpkg-db.h"
+#include "parsedump.h"
+
+const struct fieldinfo fieldinfos[]= {
+ /* NB: capitalisation of these strings is important. */
+ { "Package", f_name, w_name },
+ { "Essential", f_boolean, w_booleandefno, PKGIFPOFF(essential) },
+ { "Status", f_status, w_status },
+ { "Priority", f_priority, w_priority },
+ { "Section", f_section, w_section },
+ { "Maintainer", f_charfield, w_charfield, PKGIFPOFF(maintainer) },
+ { "Architecture", f_charfield, w_charfield, PKGIFPOFF(architecture) },
+ { "Source", f_charfield, w_charfield, PKGIFPOFF(source) },
+ { "Version", f_charfield, w_version, PKGIFPOFF(version) },
+ { "Revision", f_charfield, w_null, PKGIFPOFF(revision) },
+ { "Config-Version", f_configversion, w_configversion },
+ { "Replaces", f_dependency, w_dependency, dep_replaces },
+ { "Provides", f_dependency, w_dependency, dep_provides },
+ { "Depends", f_dependency, w_dependency, dep_depends },
+ { "Pre-Depends", f_dependency, w_dependency, dep_predepends },
+ { "Recommends", f_dependency, w_dependency, dep_recommends },
+ { "Suggests", f_dependency, w_dependency, dep_suggests },
+ { "Conflicts", f_dependency, w_dependency, dep_conflicts },
+ { "Conffiles", f_conffiles, w_conffiles },
+ { "Filename", f_filecharf, w_filecharf, FILEFOFF(name) },
+ { "Size", f_filecharf, w_filecharf, FILEFOFF(size) },
+ { "MD5sum", f_filecharf, w_filecharf, FILEFOFF(md5sum) },
+ { "MSDOS-Filename", f_filecharf, w_filecharf, FILEFOFF(msdosname) },
+ { "Description", f_charfield, w_charfield, PKGIFPOFF(description) },
+ /* Note that aliases are added to the nicknames table in parsehelp.c. */
+ { 0 /* sentinel - tells code that list is ended */ }
+};
+#define NFIELDS (sizeof(fieldinfos)/sizeof(struct fieldinfo))
+const int nfields= NFIELDS;
+
+static void cu_parsedb(int argc, void **argv) { fclose((FILE*)*argv); }
+
+int parsedb(const char *filename, enum parsedbflags flags,
+ struct pkginfo **donep, FILE *warnto, int *warncount) {
+ /* warnto, warncount and donep may be null.
+ * If donep is not null only one package's information is expected.
+ */
+ static char readbuf[16384];
+
+ FILE *file;
+ struct pkginfo newpig, *pigp;
+ struct pkginfoperfile *newpifp, *pifp;
+ struct arbitraryfield *arp, **larpp;
+ struct varbuf field, value;
+ int lno;
+ int pdone;
+ int fieldencountered[NFIELDS];
+ const struct fieldinfo *fip;
+ const struct nickname *nick;
+ const char *fieldname;
+ char *hyphen;
+ int *ip, i, c;
+
+ if (warncount) *warncount= 0;
+ newpifp= (flags & pdb_recordavailable) ? &newpig.available : &newpig.installed;
+ file= fopen(filename,"r");
+ if (!file) ohshite("failed to open package info file `%.255s' for reading",filename);
+
+ if (!donep) /* Reading many packages, use a nice big buffer. */
+ if (setvbuf(file,readbuf,_IOFBF,sizeof(readbuf)))
+ ohshite("unable to set buffering on status file");
+
+ push_cleanup(cu_parsedb,~0, 0,0, 1,(void*)file);
+ varbufinit(&field); varbufinit(&value);
+
+ lno= 1;
+ pdone= 0;
+ for (;;) { /* loop per package */
+ i= sizeof(fieldencountered)/sizeof(int); ip= fieldencountered;
+ while (i--) *ip++= 0;
+ blankpackage(&newpig);
+ blankpackageperfile(newpifp);
+ for (;;) {
+ c= getc(file); if (c!='\n' && c!=MSDOS_EOF_CHAR) break;
+ lno++;
+ }
+ if (c == EOF) break;
+ for (;;) { /* loop per field */
+ varbufreset(&field);
+ while (c!=EOF && !isspace(c) && c!=':' && c!=MSDOS_EOF_CHAR) {
+ varbufaddc(&field,c);
+ c= getc(file);
+ }
+ varbufaddc(&field,0);
+ while (c != EOF && c != '\n' && isspace(c)) c= getc(file);
+ if (c == EOF)
+ parseerr(file,filename,lno, warnto,warncount,&newpig,0,
+ "EOF after field name `%.50s'",field.buf);
+ if (c == '\n')
+ parseerr(file,filename,lno, warnto,warncount,&newpig,0,
+ "newline in field name `%.50s'",field.buf);
+ if (c == MSDOS_EOF_CHAR)
+ parseerr(file,filename,lno, warnto,warncount,&newpig,0,
+ "MSDOS EOF (^Z) in field name `%.50s'", field.buf);
+ if (c != ':')
+ parseerr(file,filename,lno, warnto,warncount,&newpig,0,
+ "field name `%.50s' must be followed by colon", field.buf);
+ varbufreset(&value);
+ for (;;) {
+ c= getc(file);
+ if (c == EOF || c == '\n' || !isspace(c)) break;
+ }
+ if (c == EOF)
+ parseerr(file,filename,lno, warnto,warncount,&newpig,0,
+ "EOF before value of field `%.50s' (missing final newline)",
+ field.buf);
+ if (c == MSDOS_EOF_CHAR)
+ parseerr(file,filename,lno, warnto,warncount,&newpig,0,
+ "MSDOS EOF char in value of field `%.50s' (missing newline?)",
+ field.buf);
+ for (;;) {
+ if (c == '\n' || c == MSDOS_EOF_CHAR) {
+ lno++;
+ c= getc(file);
+ if (c == EOF || c == '\n' || !isspace(c)) break;
+ ungetc(c,file);
+ c= '\n';
+ } else if (c == EOF) {
+ parseerr(file,filename,lno, warnto,warncount,&newpig,0,
+ "EOF during value of field `%.50s' (missing final newline)",
+ field.buf);
+ }
+ varbufaddc(&value,c);
+ c= getc(file);
+ }
+ while (value.used && isspace(value.buf[value.used-1])) value.used--;
+ varbufaddc(&value,0);
+ fieldname= field.buf;
+ for (nick= nicknames; nick->nick && strcasecmp(nick->nick,fieldname); nick++);
+ if (nick->nick) fieldname= nick->canon;
+ for (fip= fieldinfos, ip= fieldencountered;
+ fip->name && strcasecmp(fieldname,fip->name);
+ fip++, ip++);
+ if (fip->name) {
+ if (*ip++)
+ parseerr(file,filename,lno, warnto,warncount,&newpig,0,
+ "duplicate value for `%s' field", fip->name);
+ fip->rcall(&newpig,newpifp,flags,filename,lno-1,warnto,warncount,value.buf,fip);
+ } else {
+ if (strlen(fieldname)<2)
+ parseerr(file,filename,lno, warnto,warncount,&newpig,0,
+ "user-defined field name `%s' too short", fieldname);
+ larpp= &newpifp->arbs;
+ while ((arp= *larpp) != 0) {
+ if (!strcasecmp(arp->name,fieldname))
+ parseerr(file,filename,lno, warnto,warncount,&newpig,0,
+ "duplicate value for user-defined field `%.50s'", fieldname);
+ larpp= &arp->next;
+ }
+ arp= nfmalloc(sizeof(struct arbitraryfield));
+ arp->name= nfstrsave(fieldname);
+ arp->value= nfstrsave(value.buf);
+ arp->next= 0;
+ *larpp= arp;
+ }
+ if (c == EOF || c == '\n' || c == MSDOS_EOF_CHAR) break;
+ } /* loop per field */
+ if (pdone && donep)
+ parseerr(file,filename,lno, warnto,warncount,&newpig,0,
+ "several package info entries found, only one allowed");
+ parsemustfield(file,filename,lno, warnto,warncount,&newpig,0,
+ &newpig.name, "package name");
+ if ((flags & pdb_recordavailable) || newpig.status != stat_notinstalled) {
+ parsemustfield(file,filename,lno, warnto,warncount,&newpig,1,
+ &newpifp->description, "description");
+ parsemustfield(file,filename,lno, warnto,warncount,&newpig,1,
+ &newpifp->maintainer, "maintainer");
+ parsemustfield(file,filename,lno, warnto,warncount,&newpig,1,
+ &newpifp->version, "version");
+ }
+ if (flags & pdb_recordavailable)
+ parsemustfield(file,filename,lno, warnto,warncount,&newpig,1,
+ &newpifp->architecture, "architecture");
+ else if (newpifp->architecture && *newpifp->architecture)
+ newpifp->architecture= 0;
+
+ /* Break out the revision */
+ if (newpifp->revision) {
+ parseerr(file,filename,lno, warnto,warncount,&newpig,1,
+ "obsolete `Revision' or `Package-Revision' field used");
+ } else if (newpifp->version) {
+ hyphen= strrchr(newpifp->version,'-');
+ if (hyphen) {
+ *hyphen++= 0;
+ newpifp->revision= hyphen;
+ } else {
+ newpifp->revision= nfstrsave("");
+ }
+ }
+
+ /* Check the Config-Version information:
+ * If there is a Config-Version it is definitely to be used, but
+ * there shouldn't be one if the package is `installed' (in which case
+ * the Version and/or Revision will be copied) or if the package is
+ * `not-installed' (in which case there is no Config-Version).
+ */
+ if (!(flags & pdb_recordavailable)) {
+ if (newpig.configversion) {
+ if (newpig.status == stat_installed || newpig.status == stat_notinstalled)
+ parseerr(file,filename,lno, warnto,warncount,&newpig,0,
+ "Configured-Version for package with inappropriate Status");
+ } else {
+ if (newpig.status == stat_installed) {
+ newpig.configversion= newpifp->version;
+ newpig.configrevision= newpifp->revision;
+ }
+ }
+ }
+
+ pigp= findpackage(newpig.name);
+ pifp= (flags & pdb_recordavailable) ? &pigp->available : &pigp->installed;
+ if (!pifp->valid) blankpackageperfile(pifp);
+
+ if (!(flags & pdb_preferversion) ||
+ versioncompare(newpifp->version,newpifp->revision,
+ pifp->version,pifp->revision) >= 0) {
+ /* If we're ignoring older versions compare version numbers
+ * and only process this entry if it's a higher version.
+ */
+
+ /* Copy the priority and section across, but don't overwrite existing
+ * values if the pdb_weakclassification flag is set.
+ */
+ if (newpig.section && *newpig.section &&
+ !((flags & pdb_weakclassification) && pigp->section && *pigp->section))
+ pigp->section= newpig.section;
+ if (newpig.priority != pri_unknown &&
+ !((flags & pdb_weakclassification) && pigp->priority != pri_unknown)) {
+ pigp->priority= newpig.priority;
+ if (newpig.priority == pri_other) pigp->otherpriority= newpig.otherpriority;
+ }
+
+ /* Sort out the dependency mess. */
+ copy_dependency_links(pigp,&pifp->depends,newpifp->depends,
+ (flags & pdb_recordavailable) ? 1 : 0);
+ /* Leave the `depended' pointer alone, we've just gone to such
+ * trouble to get it right :-). The `depends' pointer in
+ * pifp was indeed also updated by copy_dependency_links,
+ * but since the value was that from newpifp anyway there's
+ * no need to copy it back.
+ */
+ newpifp->depended= pifp->depended;
+
+ /* Copy across data */
+ memcpy(pifp,newpifp,sizeof(struct pkginfoperfile));
+ if (!(flags & pdb_recordavailable)) {
+ pigp->want= newpig.want;
+ pigp->eflag= newpig.eflag;
+ pigp->status= newpig.status;
+ pigp->configversion= newpig.configversion;
+ pigp->configrevision= newpig.configrevision;
+ pigp->files= 0;
+ } else {
+ pigp->files= newpig.files;
+ }
+ }
+ if (donep) *donep= pigp;
+ pdone++;
+ if (c == EOF) break;
+ if (c == '\n') lno++;
+ }
+ if (ferror(file)) ohshite("failed to read from `%.255s'",filename);
+ pop_cleanup(0);
+ if (fclose(file)) ohshite("failed to close after read: `%.255s'",filename);
+ if (donep && !pdone) ohshit("no package information in `%.255s'",filename);
+
+ varbuffree(&field); varbuffree(&value);
+ return pdone;
+}
+
+void copy_dependency_links(struct pkginfo *pkg,
+ struct dependency **updateme,
+ struct dependency *newdepends,
+ int available) {
+ /* This routine is used to update the `reverse' dependency pointers
+ * when new `forwards' information has been constructed. It first
+ * removes all the links based on the old information. The old
+ * information starts in *updateme; after much brou-ha-ha
+ * the reverse structures are created and *updateme is set
+ * to the value from newdepends.
+ *
+ * Parameters are:
+ * pkg - the package we're doing this for. This is used to
+ * construct correct uplinks.
+ * updateme - the forwards dependency pointer that we are to
+ * update. This starts out containing the old forwards
+ * info, which we use to unthread the old reverse
+ * links. After we're done it is updated.
+ * newdepends - the value that we ultimately want to have in
+ * updateme.
+ * It is likely that the backward pointer for the package in
+ * question (`depended') will be updated by this routine,
+ * but this will happen by the routine traversing the dependency
+ * data structures. It doesn't need to be told where to update
+ * that; I just mention it as something that one should be
+ * cautious about.
+ */
+ struct dependency *dyp;
+ struct deppossi *dop;
+ struct pkginfoperfile *addtopifp;
+
+ /* Delete `backward' (`depended') links from other packages to
+ * dependencies listed in old version of this one. We do this by
+ * going through all the dependencies in the old version of this
+ * one and following them down to find which deppossi nodes to
+ * remove.
+ */
+ for (dyp= *updateme; dyp; dyp= dyp->next) {
+ for (dop= dyp->list; dop; dop= dop->next) {
+ if (dop->backrev)
+ dop->backrev->nextrev= dop->nextrev;
+ else
+ if (available)
+ dop->ed->available.depended= dop->nextrev;
+ else
+ dop->ed->installed.depended= dop->nextrev;
+ if (dop->nextrev)
+ dop->nextrev->backrev= dop->backrev;
+ }
+ }
+ /* Now fill in new `ed' links from other packages to dependencies listed
+ * in new version of this one, and set our uplinks correctly.
+ */
+ for (dyp= newdepends; dyp; dyp= dyp->next) {
+ dyp->up= pkg;
+ for (dop= dyp->list; dop; dop= dop->next) {
+ addtopifp= available ? &dop->ed->available : &dop->ed->installed;
+ if (!addtopifp->valid) blankpackageperfile(addtopifp);
+ dop->nextrev= addtopifp->depended;
+ dop->backrev= 0;
+ if (addtopifp->depended)
+ addtopifp->depended->backrev= dop;
+ addtopifp->depended= dop;
+ }
+ }
+ /* Finally, we fill in the new value. */
+ *updateme= newdepends;
+}
diff --git a/lib/parsedump.h b/lib/parsedump.h
new file mode 100644
index 000000000..ced6ec787
--- /dev/null
+++ b/lib/parsedump.h
@@ -0,0 +1,73 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * parse.c - declarations for in-core database reading/writing
+ *
+ * Copyright (C) 1995 Ian Jackson <iwj10@cus.cam.ac.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 DPKG_PARSEDUMP_H
+#define DPKG_PARSEDUMP_H
+
+struct fieldinfo;
+
+struct nickname {
+ const char *nick;
+ const char *canon;
+};
+
+extern const struct fieldinfo fieldinfos[];
+extern const struct nickname nicknames[];
+extern const int nfields; /* = elements in fieldinfos, including the sentinels */
+
+#define PKGIFPOFF(f) ((char*)(&(((struct pkginfoperfile *)0x1000)->f)) - \
+ (char*)(struct pkginfoperfile *)0x1000)
+#define PKGPFIELD(pifp,of,type) (*(type*)((char*)(pifp)+(of)))
+
+#define FILEFOFF(f) ((char*)(&(((struct filedetails *)0x1000)->f)) - \
+ (char*)(struct filedetails *)0x1000)
+#define FILEFFIELD(filedetail,of,type) (*(type*)((char*)(filedetail)+(of)))
+
+typedef void freadfunction(struct pkginfo *pigp, struct pkginfoperfile *pifp,
+ enum parsedbflags flags,
+ const char *filename, int lno, FILE *warnto, int *warncount,
+ const char *value, const struct fieldinfo *fip);
+freadfunction f_name, f_charfield, f_priority, f_section, f_status, f_filecharf;
+freadfunction f_boolean, f_dependency, f_conffiles, f_configversion;
+
+typedef void fwritefunction(struct varbuf*, const struct pkginfo*,
+ const struct pkginfoperfile*, const struct fieldinfo*);
+fwritefunction w_name, w_charfield, w_priority, w_section, w_status, w_configversion;
+fwritefunction w_version, w_null, w_booleandefno, w_dependency, w_conffiles, w_filecharf;
+
+struct fieldinfo {
+ const char *name;
+ freadfunction *rcall;
+ fwritefunction *wcall;
+ int integer;
+};
+
+void parseerr(FILE *file, const char *filename, int lno, FILE *warnto, int *warncount,
+ const struct pkginfo *pigp, int warnonly,
+ const char *fmt, ...) PRINTFFORMAT(8,9);
+void parsemustfield(FILE *file, const char *filename, int lno,
+ FILE *warnto, int *warncount,
+ const struct pkginfo *pigp, int warnonly,
+ char **value, const char *what);
+
+#define MSDOS_EOF_CHAR '\032' /* ^Z */
+
+#endif /* DPKG_PARSEDUMP_H */
diff --git a/lib/parsehelp.c b/lib/parsehelp.c
new file mode 100644
index 000000000..8d9c85954
--- /dev/null
+++ b/lib/parsehelp.c
@@ -0,0 +1,153 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * parsehelp.c - helpful routines for parsing and writing
+ *
+ * Copyright (C) 1995 Ian Jackson <iwj10@cus.cam.ac.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 <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "config.h"
+#include "dpkg.h"
+#include "dpkg-db.h"
+#include "parsedump.h"
+
+void parseerr(FILE *file, const char *filename, int lno, FILE *warnto, int *warncount,
+ const struct pkginfo *pigp, int warnonly,
+ const char *fmt, ...) {
+ va_list al;
+ char buf1[768], buf2[1000], *p, *q;
+ if (file && ferror(file)) ohshite("failed to read `%s' at line %d",filename,lno);
+ sprintf(buf1, "%s, in file `%.255s' near line %d",
+ warnonly ? "warning" : "parse error", filename, lno);
+ if (pigp && pigp->name) {
+ sprintf(buf2, " package `%.255s'", pigp->name);
+ strcat(buf1,buf2);
+ }
+ for (p=buf1, q=buf2; *p; *q++= *p++) if (*p == '%') *q++= '%';
+ strcpy(q,":\n "); strcat(q,fmt);
+ va_start(al,fmt);
+ if (!warnonly) ohshitv(buf2,al);
+ if (warncount) (*warncount)++;
+ if (warnto) {
+ strcat(q,"\n");
+ if (vfprintf(warnto,buf2,al) == EOF)
+ ohshite("failed to write parsing warning");
+ }
+ va_end(al);
+}
+
+const struct namevalue booleaninfos[]= { /* Note ! These must be in order ! */
+ { "no", 0 },
+ { "yes", 1 },
+ { 0 }
+};
+
+const struct namevalue priorityinfos[]= { /* Note ! These must be in order ! */
+ { "required", pri_required },
+ { "important", pri_important },
+ { "standard", pri_standard },
+ { "recommended", pri_recommended }, /* fixme: obsolete */
+ { "optional", pri_optional },
+ { "extra", pri_extra },
+ { "contrib", pri_contrib }, /* fixme: keep? */
+ { "this is a bug - please report", pri_other },
+ { "unknown", pri_unknown },
+
+ { "base", pri_required }, /* fixme: alias, remove */
+ { 0 }
+};
+
+const struct namevalue statusinfos[]= { /* Note ! These must be in order ! */
+ { "not-installed", stat_notinstalled },
+ { "unpacked", stat_unpacked },
+ { "half-configured", stat_halfconfigured },
+ { "installed", stat_installed },
+ { "half-installed", stat_halfinstalled },
+ { "config-files", stat_configfiles },
+ /* These are additional entries for reading only, in any order ... */
+ { "postinst-failed", stat_halfconfigured }, /* fixme: backwards compat., remove */
+ { "removal-failed", stat_halfinstalled }, /* fixme: backwards compat., remove */
+ { 0 }
+};
+
+const struct namevalue eflaginfos[]= { /* Note ! These must be in order ! */
+ { "ok", eflagv_ok },
+ { "hold", eflagv_hold },
+ { "reinstreq", eflagv_reinstreq },
+ { "hold-reinstreq", eflagv_both },
+ { 0 }
+};
+
+const struct namevalue wantinfos[]= { /* Note ! These must be in order ! */
+ { "unknown", want_unknown },
+ { "install", want_install },
+ { "deinstall", want_deinstall },
+ { "purge", want_purge },
+ { 0 }
+};
+
+const char *illegal_packagename(const char *p, const char **ep) {
+ static const char alsoallowed[]= "-_+.@:=%";
+ static char buf[150];
+ int c;
+
+ if (!*p) return "may not be empty string";
+ if (!isalnum(*p)) return "must start with an alphanumeric";
+ if (!*++p) return "must be at least two characters";
+ while ((c= *p++)!=0)
+ if (!isalnum(c) && !strchr(alsoallowed,c)) break;
+ if (!c) return 0;
+ if (isspace(c) && ep) {
+ while (isspace(*p)) p++;
+ *ep= p; return 0;
+ }
+ sprintf(buf,
+ "character `%c' not allowed - only letters, digits and %s allowed",
+ c, alsoallowed);
+ return buf;
+}
+
+const struct nickname nicknames[]= {
+ /* NB: capitalisation of these strings is important. */
+ { "Recommended", "Recommends" },
+ { "Optional", "Suggests" },
+ { "Class", "Priority" },
+ { "Package-Revision", "Revision" },
+ { "Package_Revision", "Revision" },
+ { 0 }
+};
+
+void parsemustfield(FILE *file, const char *filename, int lno,
+ FILE *warnto, int *warncount,
+ const struct pkginfo *pigp, int warnonly,
+ char **value, const char *what) {
+ if (!*value) {
+ parseerr(file,filename,lno, warnto,warncount,pigp,warnonly, "missing %s",what);
+ *value= nfstrsave("");
+ } else if (!**value) {
+ parseerr(file,filename,lno, warnto,warncount,pigp,warnonly,
+ "empty value for %s",what);
+ }
+}
+
+const char *skip_slash_dotslash(const char *p) {
+ while (p[0] == '/' || (p[0] == '.' && p[1] == '/')) p++;
+ return p;
+}
diff --git a/lib/showcright.c b/lib/showcright.c
new file mode 100644
index 000000000..57de25f24
--- /dev/null
+++ b/lib/showcright.c
@@ -0,0 +1,35 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * showcright.c - show copyright file routine
+ *
+ * Copyright (C) 1994,1995 Ian Jackson <iwj10@cus.cam.ac.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 <unistd.h>
+#include <fcntl.h>
+
+#include "config.h"
+#include "dpkg.h"
+
+void showcopyright(const struct cmdinfo *c, const char *v) {
+ int fd;
+ fd= open(COPYINGFILE,O_RDONLY);
+ if (fd < 0) ohshite("cannot open GPL file " COPYINGFILE);
+ m_dup2(fd,0);
+ execlp(CAT,CAT,"-",(char*)0);
+ ohshite("unable to exec cat for displaying GPL file");
+}
diff --git a/lib/star.c b/lib/star.c
new file mode 100644
index 000000000..e43ddf09b
--- /dev/null
+++ b/lib/star.c
@@ -0,0 +1,158 @@
+#include "tarfn.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <utime.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+
+static int
+Read(void * userData, char * buffer, int length)
+{
+ /*
+ * If the status of the read function is < 0, it will be returned to
+ * the caller of TarExtractor().
+ */
+ return read((int)userData, buffer, length);
+}
+
+static int
+IOError(TarInfo * i)
+{
+ int error = errno; /* fflush() could cause errno to change */
+ fflush(stdout);
+ fprintf(stderr, "%s: %s\n", i->Name, strerror(error));
+
+ /*
+ * The status returned by a coroutine of TarExtractor(), if it
+ * is non-zero, will be returned to the caller of TarExtractor().
+ */
+ return -2;
+}
+
+static int
+ExtractFile(TarInfo * i)
+{
+ /*
+ * If you don't want to extract the file, you must advance the tape
+ * by the file size rounded up to the next 512-byte boundary and
+ * return 0.
+ */
+
+ int fd = open(i->Name, O_CREAT|O_TRUNC|O_WRONLY, i->Mode & ~S_IFMT);
+ char buffer[512];
+ size_t size = i->Size;
+ struct utimbuf t;
+
+ if ( fd < 0 )
+ return IOError(i);
+
+ printf("File: %s\n", i->Name);
+
+ while ( size > 0 ) {
+ size_t writeSize = size >= 512 ? 512 : size;
+
+ if ( Read(i->UserData, buffer, 512) != 512 )
+ return -1; /* Something wrong with archive */
+ if ( write(fd, buffer, writeSize) != writeSize )
+ return IOError(i); /* Write failure. */
+
+ size -= writeSize;
+ }
+ /* fchown() and fchmod() are cheaper than chown() and chmod(). */
+ fchown(fd, i->UserID, i->GroupID);
+ fchmod(fd, i->Mode & ~S_IFMT);
+ close(fd);
+ t.actime = time(0);
+ t.modtime = i->ModTime;
+ utime(i->Name, &t);
+ return 0;
+}
+
+static int
+SetModes(TarInfo * i)
+{
+ struct utimbuf t;
+ chown(i->Name, i->UserID, i->GroupID);
+ chmod(i->Name, i->Mode & ~S_IFMT);
+ t.actime = time(0);
+ t.modtime = i->ModTime;
+ utime(i->Name, &t);
+ return 0;
+}
+
+static int
+MakeDirectory(TarInfo * i)
+{
+ printf("Directory: %s\n", i->Name);
+ if ( mkdir(i->Name, i->Mode & ~S_IFMT) != 0 ) {
+ if ( errno == EEXIST ) {
+ struct stat s;
+ if ( stat(i->Name, &s) != 0 || !(s.st_mode & S_IFDIR) )
+ return IOError(i);
+ }
+ else
+ return IOError(i);
+ }
+ SetModes(i);
+ return 0;
+}
+
+static int
+MakeHardLink(TarInfo * i)
+{
+ printf("Hard Link: %s\n", i->Name);
+
+ if ( link(i->LinkName, i->Name) != 0 )
+ return IOError(i);
+ SetModes(i);
+ return 0;
+}
+
+static int
+MakeSymbolicLink(TarInfo * i)
+{
+ printf("Symbolic Link: %s\n", i->Name);
+
+ if ( symlink(i->LinkName, i->Name) != 0 )
+ return -2;
+ SetModes(i);
+ return 0;
+}
+
+static int
+MakeSpecialFile(TarInfo * i)
+{
+ printf("Special File: %s\n", i->Name);
+
+ if ( mknod(i->Name, i->Mode, i->Device) != 0 )
+ return -2;
+ SetModes(i);
+ return 0;
+}
+
+static const TarFunctions functions = {
+ Read,
+ ExtractFile,
+ MakeDirectory,
+ MakeHardLink,
+ MakeSymbolicLink,
+ MakeSpecialFile
+};
+
+int
+main(int argc, char * * argv)
+{
+ int status = TarExtractor((void *)0, &functions);
+
+ if ( status == -1 ) {
+ fflush(stdout);
+ fprintf(stderr, "Error in archive format.\n");
+ return -1;
+ }
+ else
+ return status;
+}
diff --git a/lib/tarfn.c b/lib/tarfn.c
new file mode 100644
index 000000000..5a16a1642
--- /dev/null
+++ b/lib/tarfn.c
@@ -0,0 +1,165 @@
+/*
+ * Functions for extracting tar archives.
+ * Bruce Perens, April-May 1995
+ * Copyright (C) 1995 Bruce Perens
+ * This is free software under the GNU General Public License.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include "tarfn.h"
+
+struct TarHeader {
+ char Name[100];
+ char Mode[8];
+ char UserID[8];
+ char GroupID[8];
+ char Size[12];
+ char ModificationTime[12];
+ char Checksum[8];
+ char LinkFlag;
+ char LinkName[100];
+ char MagicNumber[8];
+ char UserName[32];
+ char GroupName[32];
+ char MajorDevice[8];
+ char MinorDevice[8];
+};
+typedef struct TarHeader TarHeader;
+
+static const unsigned int TarChecksumOffset
+ = (unsigned int)&(((TarHeader *)0)->Checksum);
+
+/* Octal-ASCII-to-long */
+static long
+OtoL(const char * s, int size)
+{
+ int n = 0;
+
+ while ( *s == ' ' ) {
+ s++;
+ size--;
+ }
+
+ while ( --size >= 0 && *s >= '0' && *s <= '7' )
+ n = (n * 010) + (*s++ - '0');
+
+ return n;
+}
+
+static int
+DecodeTarHeader(char * block, TarInfo * d)
+{
+ TarHeader * h = (TarHeader *)block;
+ unsigned char * s = (unsigned char *)block;
+ struct passwd * passwd = 0;
+ struct group * group = 0;
+ unsigned int i;
+ long sum;
+ long checksum;
+
+ if ( *h->UserName )
+ passwd = getpwnam(h->UserName);
+ if ( *h->GroupName )
+ group = getgrnam(h->GroupName);
+
+ d->Name = h->Name;
+ d->LinkName = h->LinkName;
+ d->Mode = (mode_t)OtoL(h->Mode, sizeof(h->Mode));
+ d->Size = (size_t)OtoL(h->Size, sizeof(h->Size));
+ d->ModTime = (time_t)OtoL(h->ModificationTime
+ ,sizeof(h->ModificationTime));
+ d->Device = ((OtoL(h->MajorDevice, sizeof(h->MajorDevice)) & 0xff) << 8)
+ | (OtoL(h->MinorDevice, sizeof(h->MinorDevice)) & 0xff);
+ checksum = OtoL(h->Checksum, sizeof(h->Checksum));
+ d->UserID = (uid_t)OtoL(h->UserID, sizeof(h->UserID));
+ d->GroupID = (gid_t)OtoL(h->GroupID, sizeof(h->GroupID));
+ d->Type = (TarFileType)h->LinkFlag;
+
+ if ( passwd )
+ d->UserID = passwd->pw_uid;
+
+ if ( group )
+ d->GroupID = group->gr_gid;
+
+
+ sum = ' ' * sizeof(h->Checksum);/* Treat checksum field as all blank */
+ for ( i = TarChecksumOffset; i > 0; i-- )
+ sum += *s++;
+ s += sizeof(h->Checksum); /* Skip the real checksum field */
+ for ( i = (512 - TarChecksumOffset - sizeof(h->Checksum)); i > 0; i-- )
+ sum += *s++;
+
+ return ( sum == checksum );
+}
+
+extern int
+TarExtractor(
+ void * userData
+,const TarFunctions * functions)
+{
+ int status;
+ char buffer[512];
+ TarInfo h;
+
+ h.UserData = userData;
+
+ while ( (status = functions->Read(userData, buffer, 512)) == 512 ) {
+ int nameLength;
+
+ if ( !DecodeTarHeader(buffer, &h) ) {
+ if ( h.Name[0] == '\0' ) {
+ return 0; /* End of tape */
+ } else {
+ errno = 0; /* Indicates broken tarfile */
+ return -1; /* Header checksum error */
+ }
+ }
+ if ( h.Name[0] == '\0' ) {
+ errno = 0; /* Indicates broken tarfile */
+ return -1; /* Bad header data */
+ }
+
+ nameLength = strlen(h.Name);
+
+ switch ( h.Type ) {
+ case NormalFile0:
+ case NormalFile1:
+ /* Compatibility with pre-ANSI ustar */
+ if ( h.Name[nameLength - 1] != '/' ) {
+ status = (*functions->ExtractFile)(&h);
+ break;
+ }
+ /* Else, Fall Through */
+ case Directory:
+ h.Name[nameLength - 1] = '\0';
+ status = (*functions->MakeDirectory)(&h);
+ break;
+ case HardLink:
+ status = (*functions->MakeHardLink)(&h);
+ break;
+ case SymbolicLink:
+ status = (*functions->MakeSymbolicLink)(&h);
+ break;
+ case CharacterDevice:
+ case BlockDevice:
+ case FIFO:
+ status = (*functions->MakeSpecialFile)(&h);
+ break;
+ default:
+ errno = 0; /* Indicates broken tarfile */
+ return -1; /* Bad header field */
+ }
+ if ( status != 0 )
+ return status; /* Pass on status from coroutine */
+ }
+ if ( status > 0 ) { /* Read partial header record */
+ errno = 0; /* Indicates broken tarfile */
+ return -1;
+ } else {
+ return status; /* Whatever I/O function returned */
+ }
+}
diff --git a/lib/varbuf.c b/lib/varbuf.c
new file mode 100644
index 000000000..2f12db774
--- /dev/null
+++ b/lib/varbuf.c
@@ -0,0 +1,65 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * varbuf.c - variable length expandable buffer handling
+ *
+ * Copyright (C) 1994,1995 Ian Jackson <iwj10@cus.cam.ac.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 <stdlib.h>
+
+#include "config.h"
+#include "dpkg.h"
+#include "dpkg-db.h"
+
+void varbufaddc(struct varbuf *v, int c) {
+ if (v->used >= v->size) varbufextend(v);
+ v->buf[v->used++]= c;
+}
+
+void varbufaddstr(struct varbuf *v, const char *s) {
+ int l, ou;
+ l= strlen(s);
+ ou= v->used;
+ v->used += l;
+ if (v->used >= v->size) varbufextend(v);
+ memcpy(v->buf + ou, s, l);
+}
+
+void varbufinit(struct varbuf *v) {
+ /* NB: dbmodify.c does its own init to get a big buffer */
+ v->size= v->used= 0;
+ v->buf= 0;
+}
+
+void varbufreset(struct varbuf *v) {
+ v->used= 0;
+}
+
+void varbufextend(struct varbuf *v) {
+ int newsize;
+ char *newbuf;
+
+ newsize= v->size + 80 + v->used;
+ newbuf= realloc(v->buf,newsize);
+ if (!newbuf) ohshite("failed to realloc for variable buffer");
+ v->size= newsize;
+ v->buf= newbuf;
+}
+
+void varbuffree(struct varbuf *v) {
+ free(v->buf); v->buf=0; v->size=0; v->used=0;
+}
diff --git a/lib/vercmp.c b/lib/vercmp.c
new file mode 100644
index 000000000..a2fac63b2
--- /dev/null
+++ b/lib/vercmp.c
@@ -0,0 +1,64 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * vercmp.c - comparison of version numbers
+ *
+ * Copyright (C) 1995 Ian Jackson <iwj10@cus.cam.ac.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 <ctype.h>
+#include <string.h>
+
+#include "config.h"
+#include "dpkg.h"
+#include "dpkg-db.h"
+#include "parsedump.h"
+
+static int verrevcmp(const char *val, const char *ref) {
+ int vc, rc;
+ long vl, rl;
+ const char *vp, *rp;
+
+ if (!val) val= "";
+ if (!ref) ref= "";
+ for (;;) {
+ vp= val; while (*vp && !isdigit(*vp)) vp++;
+ rp= ref; while (*rp && !isdigit(*rp)) rp++;
+ for (;;) {
+ vc= val == vp ? 0 : *val++;
+ rc= ref == rp ? 0 : *ref++;
+ if (!rc && !vc) break;
+ if (vc && !isalpha(vc)) vc += 256; /* assumes ASCII character set */
+ if (rc && !isalpha(rc)) rc += 256;
+ if (vc != rc) return vc - rc;
+ }
+ val= vp;
+ ref= rp;
+ vl=0; if (isdigit(*vp)) vl= strtol(val,(char**)&val,10);
+ rl=0; if (isdigit(*rp)) rl= strtol(ref,(char**)&ref,10);
+ if (vl != rl) return vl - rl;
+ if (!*val && !*ref) return 0;
+ if (!*val) return -1;
+ if (!*ref) return +1;
+ }
+}
+
+int versioncompare(const char *version, const char *revision,
+ const char *refversion, const char *refrevision) {
+ int r;
+ r= verrevcmp(version,refversion); if (r) return r;
+ return verrevcmp(revision,refrevision);
+}