diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.in | 91 | ||||
-rw-r--r-- | lib/compat.c | 102 | ||||
-rw-r--r-- | lib/database.c | 249 | ||||
-rw-r--r-- | lib/dbmodify.c | 256 | ||||
-rwxr-xr-x | lib/debugmake | 3 | ||||
-rw-r--r-- | lib/dump.c | 289 | ||||
-rw-r--r-- | lib/ehandle.c | 276 | ||||
-rw-r--r-- | lib/fields.c | 354 | ||||
-rw-r--r-- | lib/lock.c | 82 | ||||
-rw-r--r-- | lib/mlib.c | 124 | ||||
-rw-r--r-- | lib/myopt.c | 84 | ||||
-rw-r--r-- | lib/nfmalloc.c | 91 | ||||
-rw-r--r-- | lib/parse.c | 371 | ||||
-rw-r--r-- | lib/parsedump.h | 73 | ||||
-rw-r--r-- | lib/parsehelp.c | 153 | ||||
-rw-r--r-- | lib/showcright.c | 35 | ||||
-rw-r--r-- | lib/star.c | 158 | ||||
-rw-r--r-- | lib/tarfn.c | 165 | ||||
-rw-r--r-- | lib/varbuf.c | 65 | ||||
-rw-r--r-- | lib/vercmp.c | 64 |
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); +} |