/* * libdpkg - Debian packaging suite library routines * fields.c - parsing of all the different fields, when reading in * * Copyright (C) 1995 Ian Jackson * * 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 #include #include #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++; } }