123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702 |
- /*
- * libdpkg - Debian packaging suite library routines
- * fields.c - parsing of all the different fields, when reading in
- *
- * Copyright © 1995 Ian Jackson <ian@chiark.greenend.org.uk>
- * Copyright © 2001 Wichert Akkerman
- * Copyright © 2006-2015 Guillem Jover <guillem@debian.org>
- * Copyright © 2009 Canonical Ltd.
- * Copyright © 2011 Linaro Limited
- * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org>
- *
- * This is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, 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 program. If not, see <https://www.gnu.org/licenses/>.
- */
- #include <config.h>
- #include <compat.h>
- #include <string.h>
- #include <stdio.h>
- #include <dpkg/i18n.h>
- #include <dpkg/c-ctype.h>
- #include <dpkg/dpkg.h>
- #include <dpkg/dpkg-db.h>
- #include <dpkg/arch.h>
- #include <dpkg/string.h>
- #include <dpkg/path.h>
- #include <dpkg/parsedump.h>
- #include <dpkg/pkg-spec.h>
- #include <dpkg/triglib.h>
- /**
- * Flags to parse a name associated to a value.
- */
- enum parse_nv_flags {
- /** Expect no more words (default). */
- PARSE_NV_LAST = 0,
- /** Expect another word after the parsed name. */
- PARSE_NV_NEXT = DPKG_BIT(0),
- /** Do not fail if there is no name with an associated value found. */
- PARSE_NV_FALLBACK = DPKG_BIT(1),
- };
- /**
- * Parses a name and returns its associated value.
- *
- * Gets a pointer to the string to parse in @a strp, and modifies the pointer
- * to the string to point to the end of the parsed text. If no value is found
- * for the name and #PARSE_NV_FALLBACK is set in @a flags then @a strp is set
- * to NULL and returns -1, otherwise a parse error is emitted.
- */
- static int
- parse_nv(struct parsedb_state *ps, enum parse_nv_flags flags,
- const char **strp, const struct namevalue *nv_head, const char *what)
- {
- const char *str_start = *strp, *str_end;
- const struct namevalue *nv;
- int value;
- if (str_start[0] == '\0')
- parse_error(ps, _("%s is missing"), what);
- nv = namevalue_find_by_name(nv_head, str_start);
- if (nv == NULL) {
- /* We got no match, skip further string validation. */
- if (!(flags & PARSE_NV_FALLBACK))
- parse_error(ps, _("'%.50s' is not allowed for %s"), str_start, what);
- str_end = NULL;
- value = -1;
- } else {
- str_end = str_start + nv->length;
- while (c_isspace(str_end[0]))
- str_end++;
- value = nv->value;
- }
- if (!(flags & PARSE_NV_NEXT) && str_is_set(str_end))
- parse_error(ps, _("junk after %s"), what);
- *strp = str_end;
- return value;
- }
- void
- f_name(struct pkginfo *pkg, struct pkgbin *pkgbin,
- struct parsedb_state *ps,
- const char *value, const struct fieldinfo *fip)
- {
- const char *e;
- e = pkg_name_is_illegal(value);
- if (e != NULL)
- parse_error(ps, _("invalid package name (%.250s)"), e);
- /* We use the new name, as pkg_db_find_set() may have done a tolower for us. */
- pkg->set->name = pkg_db_find_set(value)->name;
- }
- void
- f_filecharf(struct pkginfo *pkg, struct pkgbin *pkgbin,
- struct parsedb_state *ps,
- const char *value, const struct fieldinfo *fip)
- {
- struct filedetails *fdp, **fdpp;
- char *cpos, *space;
- int allowextend;
- if (!*value)
- parse_error(ps, _("empty file details field '%s'"), fip->name);
- if (!(ps->flags & pdb_recordavailable))
- parse_error(ps,
- _("file details field '%s' not allowed in status file"),
- fip->name);
- allowextend = !pkg->files;
- fdpp = &pkg->files;
- cpos= nfstrsave(value);
- while (*cpos) {
- space = cpos;
- while (*space && !c_isspace(*space))
- space++;
- if (*space)
- *space++ = '\0';
- fdp= *fdpp;
- if (!fdp) {
- if (!allowextend)
- parse_error(ps,
- _("too many values in file details field '%s' "
- "(compared to others)"), fip->name);
- fdp= nfmalloc(sizeof(struct filedetails));
- fdp->next= NULL;
- fdp->name= fdp->msdosname= fdp->size= fdp->md5sum= NULL;
- *fdpp= fdp;
- }
- STRUCTFIELD(fdp, fip->integer, const char *) = cpos;
- fdpp= &fdp->next;
- while (*space && c_isspace(*space))
- space++;
- cpos= space;
- }
- if (*fdpp)
- parse_error(ps,
- _("too few values in file details field '%s' "
- "(compared to others)"), fip->name);
- }
- void
- f_charfield(struct pkginfo *pkg, struct pkgbin *pkgbin,
- struct parsedb_state *ps,
- const char *value, const struct fieldinfo *fip)
- {
- if (*value)
- STRUCTFIELD(pkgbin, fip->integer, char *) = nfstrsave(value);
- }
- void
- f_boolean(struct pkginfo *pkg, struct pkgbin *pkgbin,
- struct parsedb_state *ps,
- const char *value, const struct fieldinfo *fip)
- {
- bool boolean;
- if (!*value)
- return;
- boolean = parse_nv(ps, PARSE_NV_LAST, &value, booleaninfos,
- _("yes/no in boolean field"));
- STRUCTFIELD(pkgbin, fip->integer, bool) = boolean;
- }
- void
- f_multiarch(struct pkginfo *pkg, struct pkgbin *pkgbin,
- struct parsedb_state *ps,
- const char *value, const struct fieldinfo *fip)
- {
- int multiarch;
- if (!*value)
- return;
- multiarch = parse_nv(ps, PARSE_NV_LAST, &value, multiarchinfos,
- _("foreign/allowed/same/no in quadstate field"));
- STRUCTFIELD(pkgbin, fip->integer, int) = multiarch;
- }
- void
- f_architecture(struct pkginfo *pkg, struct pkgbin *pkgbin,
- struct parsedb_state *ps,
- const char *value, const struct fieldinfo *fip)
- {
- pkgbin->arch = dpkg_arch_find(value);
- if (pkgbin->arch->type == DPKG_ARCH_ILLEGAL)
- parse_warn(ps, _("'%s' is not a valid architecture name: %s"),
- value, dpkg_arch_name_is_illegal(value));
- }
- void
- f_section(struct pkginfo *pkg, struct pkgbin *pkgbin,
- struct parsedb_state *ps,
- const char *value, const struct fieldinfo *fip)
- {
- if (!*value) return;
- pkg->section = nfstrsave(value);
- }
- void
- f_priority(struct pkginfo *pkg, struct pkgbin *pkgbin,
- struct parsedb_state *ps,
- const char *value, const struct fieldinfo *fip)
- {
- const char *str = value;
- int priority;
- if (!*value) return;
- priority = parse_nv(ps, PARSE_NV_LAST | PARSE_NV_FALLBACK, &str,
- priorityinfos, _("word in 'Priority' field"));
- if (str == NULL) {
- pkg->priority = PKG_PRIO_OTHER;
- pkg->otherpriority = nfstrsave(value);
- } else {
- pkg->priority = priority;
- }
- }
- void
- f_status(struct pkginfo *pkg, struct pkgbin *pkgbin,
- struct parsedb_state *ps,
- const char *value, const struct fieldinfo *fip)
- {
- if (ps->flags & pdb_rejectstatus)
- parse_error(ps,
- _("value for '%s' field not allowed in this context"),
- "Status");
- if (ps->flags & pdb_recordavailable)
- return;
- pkg->want = parse_nv(ps, PARSE_NV_NEXT, &value, wantinfos,
- _("first (want) word in 'Status' field"));
- pkg->eflag = parse_nv(ps, PARSE_NV_NEXT, &value, eflaginfos,
- _("second (error) word in 'Status' field"));
- pkg->status = parse_nv(ps, PARSE_NV_LAST, &value, statusinfos,
- _("third (status) word in 'Status' field"));
- }
- void
- f_version(struct pkginfo *pkg, struct pkgbin *pkgbin,
- struct parsedb_state *ps,
- const char *value, const struct fieldinfo *fip)
- {
- parse_db_version(ps, &pkgbin->version, value,
- _("error in '%s' field string '%.250s'"),
- "Version", value);
- }
- void
- f_revision(struct pkginfo *pkg, struct pkgbin *pkgbin,
- struct parsedb_state *ps,
- const char *value, const struct fieldinfo *fip)
- {
- char *newversion;
- parse_warn(ps,
- _("obsolete '%s' or '%s' field used"),
- "Revision", "Package-Revision");
- if (!*value) return;
- if (str_is_set(pkgbin->version.revision)) {
- newversion = nfmalloc(strlen(pkgbin->version.version) +
- strlen(pkgbin->version.revision) + 2);
- sprintf(newversion, "%s-%s", pkgbin->version.version,
- pkgbin->version.revision);
- pkgbin->version.version = newversion;
- }
- pkgbin->version.revision = nfstrsave(value);
- }
- void
- f_configversion(struct pkginfo *pkg, struct pkgbin *pkgbin,
- struct parsedb_state *ps,
- const char *value, const struct fieldinfo *fip)
- {
- if (ps->flags & pdb_rejectstatus)
- parse_error(ps,
- _("value for '%s' field not allowed in this context"),
- "Config-Version");
- if (ps->flags & pdb_recordavailable)
- return;
- parse_db_version(ps, &pkg->configversion, value,
- _("error in '%s' field string '%.250s'"),
- "Config-Version", value);
- }
- /*
- * The code in f_conffiles ensures that value[-1] == ' ', which is helpful.
- */
- static void conffvalue_lastword(const char *value, const char *from,
- const char *endent,
- const char **word_start_r, int *word_len_r,
- const char **new_from_r,
- struct parsedb_state *ps)
- {
- const char *lastspc;
- if (from <= value+1) goto malformed;
- for (lastspc= from-1; *lastspc != ' '; lastspc--);
- if (lastspc <= value+1 || lastspc >= endent-1) goto malformed;
- *new_from_r= lastspc;
- *word_start_r= lastspc + 1;
- *word_len_r= (int)(from - *word_start_r);
- return;
- malformed:
- parse_error(ps,
- _("value for '%s' field has malformatted line '%.*s'"),
- "Conffiles", (int)min(endent - value, 250), value);
- }
- void
- f_conffiles(struct pkginfo *pkg, struct pkgbin *pkgbin,
- struct parsedb_state *ps,
- const char *value, const struct fieldinfo *fip)
- {
- static const char obsolete_str[]= "obsolete";
- struct conffile **lastp, *newlink;
- const char *endent, *endfn, *hashstart;
- int c, namelen, hashlen;
- bool obsolete;
- char *newptr;
- lastp = &pkgbin->conffiles;
- while (*value) {
- c= *value++;
- if (c == '\n') continue;
- if (c != ' ')
- parse_error(ps,
- _("value for '%s' has line starting with non-space '%c'"),
- "Conffiles", c);
- for (endent = value; (c = *endent) != '\0' && c != '\n'; endent++) ;
- conffvalue_lastword(value, endent, endent,
- &hashstart, &hashlen, &endfn,
- ps);
- obsolete= (hashlen == sizeof(obsolete_str)-1 &&
- memcmp(hashstart, obsolete_str, hashlen) == 0);
- if (obsolete)
- conffvalue_lastword(value, endfn, endent,
- &hashstart, &hashlen, &endfn,
- ps);
- newlink= nfmalloc(sizeof(struct conffile));
- value = path_skip_slash_dotslash(value);
- namelen= (int)(endfn-value);
- if (namelen <= 0)
- parse_error(ps,
- _("root or null directory is listed as a conffile"));
- newptr = nfmalloc(namelen+2);
- newptr[0]= '/';
- memcpy(newptr+1,value,namelen);
- newptr[namelen+1] = '\0';
- newlink->name= newptr;
- newptr= nfmalloc(hashlen+1);
- memcpy(newptr, hashstart, hashlen);
- newptr[hashlen] = '\0';
- newlink->hash= newptr;
- newlink->obsolete= obsolete;
- newlink->next =NULL;
- *lastp= newlink;
- lastp= &newlink->next;
- value= endent;
- }
- }
- void
- f_dependency(struct pkginfo *pkg, struct pkgbin *pkgbin,
- struct parsedb_state *ps,
- const char *value, const struct fieldinfo *fip)
- {
- char c1, c2;
- const char *p, *emsg;
- const char *depnamestart, *versionstart;
- int depnamelength, versionlength;
- static struct varbuf depname, version;
- struct dependency *dyp, **ldypp;
- struct deppossi *dop, **ldopp;
- /* Empty fields are ignored. */
- if (!*value)
- return;
- p= value;
- ldypp = &pkgbin->depends;
- while (*ldypp)
- ldypp = &(*ldypp)->next;
- /* Loop creating new struct dependency's. */
- for (;;) {
- dyp= nfmalloc(sizeof(struct dependency));
- /* Set this to NULL for now, as we don't know what our real
- * struct pkginfo address (in the database) is going to be yet. */
- dyp->up = NULL;
- dyp->next= NULL; *ldypp= dyp; ldypp= &dyp->next;
- dyp->list= NULL; ldopp= &dyp->list;
- dyp->type= fip->integer;
- /* Loop creating new struct deppossi's. */
- for (;;) {
- depnamestart= p;
- /* Skip over package name characters. */
- while (*p && !c_isspace(*p) && *p != ':' && *p != '(' && *p != ',' &&
- *p != '|')
- p++;
- depnamelength= p - depnamestart ;
- if (depnamelength == 0)
- parse_error(ps,
- _("'%s' field, missing package name, or garbage where "
- "package name expected"), fip->name);
- varbuf_reset(&depname);
- varbuf_add_buf(&depname, depnamestart, depnamelength);
- varbuf_end_str(&depname);
- emsg = pkg_name_is_illegal(depname.buf);
- if (emsg)
- parse_error(ps,
- _("'%s' field, invalid package name '%.255s': %s"),
- fip->name, depname.buf, emsg);
- dop= nfmalloc(sizeof(struct deppossi));
- dop->up= dyp;
- dop->ed = pkg_db_find_set(depname.buf);
- dop->next= NULL; *ldopp= dop; ldopp= &dop->next;
- /* Don't link this (which is after all only ‘new_pkg’ from
- * 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->rev_next = NULL;
- dop->rev_prev = NULL;
- dop->cyclebreak = false;
- /* See if we have an architecture qualifier. */
- if (*p == ':') {
- static struct varbuf arch;
- const char *archstart;
- int archlength;
- archstart = ++p;
- while (*p && !c_isspace(*p) && *p != '(' && *p != ',' && *p != '|')
- p++;
- archlength = p - archstart;
- if (archlength == 0)
- parse_error(ps, _("'%s' field, missing architecture name, or garbage "
- "where architecture name expected"), fip->name);
- varbuf_reset(&arch);
- varbuf_add_buf(&arch, archstart, archlength);
- varbuf_end_str(&arch);
- dop->arch_is_implicit = false;
- dop->arch = dpkg_arch_find(arch.buf);
- if (dop->arch->type == DPKG_ARCH_ILLEGAL)
- emsg = dpkg_arch_name_is_illegal(arch.buf);
- if (emsg)
- parse_error(ps, _("'%s' field, reference to '%.255s': "
- "invalid architecture name '%.255s': %s"),
- fip->name, depname.buf, arch.buf, emsg);
- } else if (fip->integer == dep_conflicts || fip->integer == dep_breaks ||
- fip->integer == dep_replaces) {
- /* Conflics/Breaks/Replaces get an implicit "any" arch qualifier. */
- dop->arch_is_implicit = true;
- dop->arch = dpkg_arch_get(DPKG_ARCH_WILDCARD);
- } else {
- /* Otherwise use the pkgbin architecture, which will be assigned to
- * later on by parse.c, once we can guarantee we have parsed it from
- * the control stanza. */
- dop->arch_is_implicit = true;
- dop->arch = NULL;
- }
- /* Skip whitespace after package name. */
- while (c_isspace(*p))
- p++;
- /* See if we have a versioned relation. */
- if (*p == '(') {
- p++;
- while (c_isspace(*p))
- p++;
- c1= *p;
- if (c1 == '<' || c1 == '>') {
- c2= *++p;
- dop->verrel = (c1 == '<') ? DPKG_RELATION_LT : DPKG_RELATION_GT;
- if (c2 == '=') {
- dop->verrel |= DPKG_RELATION_EQ;
- p++;
- } else if (c2 == c1) {
- /* Either ‘<<’ or ‘>>’. */
- p++;
- } else if (c2 == '<' || c2 == '>') {
- parse_error(ps,
- _("'%s' field, reference to '%.255s':\n"
- " bad version relationship %c%c"),
- fip->name, depname.buf, c1, c2);
- dop->verrel = DPKG_RELATION_NONE;
- } else {
- parse_warn(ps,
- _("'%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 |= DPKG_RELATION_EQ;
- }
- } else if (c1 == '=') {
- dop->verrel = DPKG_RELATION_EQ;
- p++;
- } else {
- parse_warn(ps,
- _("'%s' field, reference to '%.255s':\n"
- " implicit exact match on version number, "
- "suggest using '=' instead"),
- fip->name, depname.buf);
- dop->verrel = DPKG_RELATION_EQ;
- }
- if ((dop->verrel != DPKG_RELATION_EQ) && (fip->integer == dep_provides))
- parse_warn(ps,
- _("only exact versions may be used for '%s' field"),
- "Provides");
- if (!c_isspace(*p) && !c_isalnum(*p)) {
- parse_warn(ps,
- _("'%s' field, reference to '%.255s':\n"
- " version value starts with non-alphanumeric, "
- "suggest adding a space"),
- fip->name, depname.buf);
- }
- /* Skip spaces between the relation and the version. */
- while (c_isspace(*p))
- p++;
- versionstart= p;
- while (*p && *p != ')' && *p != '(') {
- if (c_isspace(*p))
- break;
- p++;
- }
- versionlength= p - versionstart;
- while (c_isspace(*p))
- p++;
- if (*p == '(')
- parse_error(ps,
- _("'%s' field, reference to '%.255s': "
- "version contains '%c'"), fip->name, depname.buf, ')');
- else if (*p != ')')
- parse_error(ps,
- _("'%s' field, reference to '%.255s': "
- "version contains '%c'"), fip->name, depname.buf, ' ');
- else if (*p == '\0')
- parse_error(ps,
- _("'%s' field, reference to '%.255s': "
- "version unterminated"), fip->name, depname.buf);
- varbuf_reset(&version);
- varbuf_add_buf(&version, versionstart, versionlength);
- varbuf_end_str(&version);
- parse_db_version(ps, &dop->version, version.buf,
- _("'%s' field, reference to '%.255s': "
- "error in version"), fip->name, depname.buf);
- p++;
- while (c_isspace(*p))
- p++;
- } else {
- dop->verrel = DPKG_RELATION_NONE;
- dpkg_version_blank(&dop->version);
- }
- if (!*p || *p == ',') break;
- if (*p != '|')
- parse_error(ps,
- _("'%s' field, syntax error after reference to package '%.255s'"),
- fip->name, dop->ed->name);
- if (fip->integer == dep_conflicts ||
- fip->integer == dep_breaks ||
- fip->integer == dep_provides ||
- fip->integer == dep_replaces)
- parse_error(ps,
- _("alternatives ('|') not allowed in %s field"), fip->name);
- p++;
- while (c_isspace(*p))
- p++;
- }
- if (!*p) break;
- p++;
- while (c_isspace(*p))
- p++;
- }
- }
- static const char *
- scan_word(const char **valp)
- {
- static struct varbuf word;
- const char *p, *start, *end;
- p = *valp;
- for (;;) {
- if (!*p) {
- *valp = p;
- return NULL;
- }
- if (c_iswhite(*p)) {
- p++;
- continue;
- }
- start = p;
- break;
- }
- for (;;) {
- if (*p && !c_iswhite(*p)) {
- p++;
- continue;
- }
- end = p;
- break;
- }
- varbuf_reset(&word);
- varbuf_add_buf(&word, start, end - start);
- varbuf_end_str(&word);
- *valp = p;
- return word.buf;
- }
- void
- f_trigpend(struct pkginfo *pend, struct pkgbin *pkgbin,
- struct parsedb_state *ps,
- const char *value, const struct fieldinfo *fip)
- {
- const char *word, *emsg;
- if (ps->flags & pdb_rejectstatus)
- parse_error(ps,
- _("value for '%s' field not allowed in this context"),
- "Triggers-Pending");
- while ((word = scan_word(&value))) {
- emsg = trig_name_is_illegal(word);
- if (emsg)
- parse_error(ps,
- _("illegal pending trigger name '%.255s': %s"), word, emsg);
- if (!trig_note_pend_core(pend, nfstrsave(word)))
- parse_error(ps,
- _("duplicate pending trigger '%.255s'"), word);
- }
- }
- void
- f_trigaw(struct pkginfo *aw, struct pkgbin *pkgbin,
- struct parsedb_state *ps,
- const char *value, const struct fieldinfo *fip)
- {
- const char *word;
- struct pkginfo *pend;
- if (ps->flags & pdb_rejectstatus)
- parse_error(ps,
- _("value for '%s' field not allowed in this context"),
- "Triggers-Awaited");
- while ((word = scan_word(&value))) {
- struct dpkg_error err;
- pend = pkg_spec_parse_pkg(word, &err);
- if (pend == NULL)
- parse_error(ps,
- _("illegal package name in awaited trigger '%.255s': %s"),
- word, err.str);
- if (!trig_note_aw(pend, aw))
- parse_error(ps,
- _("duplicate awaited trigger package '%.255s'"), word);
- trig_awaited_pend_enqueue(pend);
- }
- }
|