diff options
Diffstat (limited to 'lib/parse.c')
-rw-r--r-- | lib/parse.c | 371 |
1 files changed, 371 insertions, 0 deletions
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; +} |