/** * Parse the field and value into the package being constructed. */ static void pkg_parse_field(struct parsedb_state *ps, struct field_state *fs, void *parse_obj) { struct pkg_parse_object *pkg_obj = parse_obj; const struct nickname *nick; const struct fieldinfo *fip; int *ip; for (nick = nicknames; nick->nick; nick++) if (strncasecmp(nick->nick, fs->fieldstart, fs->fieldlen) == 0 && nick->nick[fs->fieldlen] == '\0') break; if (nick->nick) { fs->fieldstart = nick->canon; fs->fieldlen = strlen(fs->fieldstart); } for (fip = fieldinfos, ip = fs->fieldencountered; fip->name; fip++, ip++) if (strncasecmp(fip->name, fs->fieldstart, fs->fieldlen) == 0) break; if (fip->name) { if ((*ip)++) parse_error(ps, _("duplicate value for `%s' field"), fip->name); varbuf_reset(&fs->value); varbuf_add_buf(&fs->value, fs->valuestart, fs->valuelen); varbuf_end_str(&fs->value); fip->rcall(pkg_obj->pkg, pkg_obj->pkgbin, ps, fs->value.buf, fip); } else { struct arbitraryfield *arp, **larpp; if (fs->fieldlen < 2) parse_error(ps, _("user-defined field name `%.*s' too short"), fs->fieldlen, fs->fieldstart); larpp = &pkg_obj->pkgbin->arbs; while ((arp = *larpp) != NULL) { if (!strncasecmp(arp->name, fs->fieldstart, fs->fieldlen)) parse_error(ps, _("duplicate value for user-defined field `%.*s'"), fs->fieldlen, fs->fieldstart); larpp = &arp->next; } arp = nfmalloc(sizeof(struct arbitraryfield)); arp->name = nfstrnsave(fs->fieldstart, fs->fieldlen); arp->value = nfstrnsave(fs->valuestart, fs->valuelen); arp->next = NULL; *larpp = arp; } }
/** * Parse a version string and check for invalid syntax. * * Distinguish between lax (warnings) and strict (error) parsing. * * @param rversion The parsed version. * @param string The version string to parse. * @param err The warning or error message if any. * * @retval 0 On success. * @retval -1 On failure, and err is set accordingly. */ int parseversion(struct versionrevision *rversion, const char *string, struct dpkg_error *err) { char *hyphen, *colon, *eepochcolon; const char *end, *ptr; unsigned long epoch; if (!*string) return dpkg_put_error(err, _("version string is empty")); /* Trim leading and trailing space. */ while (*string && isblank(*string)) string++; /* String now points to the first non-whitespace char. */ end = string; /* Find either the end of the string, or a whitespace char. */ while (*end && !isblank(*end)) end++; /* Check for extra chars after trailing space. */ ptr = end; while (*ptr && isblank(*ptr)) ptr++; if (*ptr) return dpkg_put_error(err, _("version string has embedded spaces")); colon= strchr(string,':'); if (colon) { epoch= strtoul(string,&eepochcolon,10); if (colon != eepochcolon) return dpkg_put_error(err, _("epoch in version is not number")); if (!*++colon) return dpkg_put_error(err, _("nothing after colon in version number")); string= colon; rversion->epoch= epoch; } else { rversion->epoch= 0; } rversion->version= nfstrnsave(string,end-string); hyphen= strrchr(rversion->version,'-'); if (hyphen) *hyphen++ = '\0'; rversion->revision= hyphen ? hyphen : ""; /* XXX: Would be faster to use something like cisversion and cisrevision. */ ptr = rversion->version; if (*ptr && !cisdigit(*ptr++)) return dpkg_put_warn(err, _("version number does not start with digit")); for (; *ptr; ptr++) { if (!cisdigit(*ptr) && !cisalpha(*ptr) && strchr(".-+~:", *ptr) == NULL) return dpkg_put_warn(err, _("invalid character in version number")); } for (ptr = rversion->revision; *ptr; ptr++) { if (!cisdigit(*ptr) && !cisalpha(*ptr) && strchr(".+~", *ptr) == NULL) return dpkg_put_warn(err, _("invalid character in revision number")); } return 0; }
static const char * parseversion_lax(struct versionrevision *rversion, const char *string) { char *hyphen, *colon, *eepochcolon; const char *end, *ptr; unsigned long epoch; if (!*string) return _("version string is empty"); /* Trim leading and trailing space. */ while (*string && isblank(*string)) string++; /* String now points to the first non-whitespace char. */ end = string; /* Find either the end of the string, or a whitespace char. */ while (*end && !isblank(*end)) end++; /* Check for extra chars after trailing space. */ ptr = end; while (*ptr && isblank(*ptr)) ptr++; if (*ptr) return _("version string has embedded spaces"); colon= strchr(string,':'); if (colon) { epoch= strtoul(string,&eepochcolon,10); if (colon != eepochcolon) return _("epoch in version is not number"); if (!*++colon) return _("nothing after colon in version number"); string= colon; rversion->epoch= epoch; } else { rversion->epoch= 0; } rversion->version= nfstrnsave(string,end-string); hyphen= strrchr(rversion->version,'-'); if (hyphen) *hyphen++ = '\0'; rversion->revision= hyphen ? hyphen : ""; return NULL; }
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 int fd; struct pkginfo newpig, *pigp; struct pkginfoperfile *newpifp, *pifp; struct arbitraryfield *arp, **larpp; struct trigaw *ta; int pdone; int fieldencountered[array_count(fieldinfos)]; const struct fieldinfo *fip; const struct nickname *nick; char *data, *dataptr, *endptr; const char *fieldstart, *valuestart; char *value= NULL; int fieldlen= 0, valuelen= 0; int *ip, c; struct stat st; struct parsedb_state ps; ps.filename = filename; ps.flags = flags; ps.lno = 0; ps.warnto = warnto; ps.warncount = 0; newpifp= (flags & pdb_recordavailable) ? &newpig.available : &newpig.installed; fd= open(filename, O_RDONLY); if (fd == -1) ohshite(_("failed to open package info file `%.255s' for reading"),filename); push_cleanup(cu_closefd, ~ehflag_normaltidy, NULL, 0, 1, &fd); if (fstat(fd, &st) == -1) ohshite(_("can't stat package info file `%.255s'"),filename); if (st.st_size > 0) { #ifdef USE_MMAP dataptr = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if (dataptr == MAP_FAILED) ohshite(_("can't mmap package info file `%.255s'"),filename); #else dataptr = m_malloc(st.st_size); fd_buf_copy(fd, dataptr, st.st_size, _("copy info file `%.255s'"), filename); #endif data= dataptr; endptr = dataptr + st.st_size; } else { data= dataptr= endptr= NULL; } pdone= 0; #define EOF_mmap(dataptr, endptr) (dataptr >= endptr) #define getc_mmap(dataptr) *dataptr++; #define ungetc_mmap(c, dataptr, data) dataptr--; for (;;) { /* loop per package */ memset(fieldencountered, 0, sizeof(fieldencountered)); blankpackage(&newpig); /* Skip adjacent new lines */ while(!EOF_mmap(dataptr, endptr)) { c= getc_mmap(dataptr); if (c!='\n' && c!=MSDOS_EOF_CHAR ) break; ps.lno++; } if (EOF_mmap(dataptr, endptr)) break; for (;;) { /* loop per field */ fieldstart= dataptr - 1; while (!EOF_mmap(dataptr, endptr) && !isspace(c) && c!=':' && c!=MSDOS_EOF_CHAR) c= getc_mmap(dataptr); fieldlen= dataptr - fieldstart - 1; while (!EOF_mmap(dataptr, endptr) && c != '\n' && isspace(c)) c= getc_mmap(dataptr); if (EOF_mmap(dataptr, endptr)) parse_error(&ps, &newpig, _("EOF after field name `%.*s'"), fieldlen, fieldstart); if (c == '\n') parse_error(&ps, &newpig, _("newline in field name `%.*s'"), fieldlen, fieldstart); if (c == MSDOS_EOF_CHAR) parse_error(&ps, &newpig, _("MSDOS EOF (^Z) in field name `%.*s'"), fieldlen, fieldstart); if (c != ':') parse_error(&ps, &newpig, _("field name `%.*s' must be followed by colon"), fieldlen, fieldstart); /* Skip space after ':' but before value and eol */ while(!EOF_mmap(dataptr, endptr)) { c= getc_mmap(dataptr); if (c == '\n' || !isspace(c)) break; } if (EOF_mmap(dataptr, endptr)) parse_error(&ps, &newpig, _("EOF before value of field `%.*s' (missing final newline)"), fieldlen,fieldstart); if (c == MSDOS_EOF_CHAR) parse_error(&ps, &newpig, _("MSDOS EOF char in value of field `%.*s' (missing newline?)"), fieldlen,fieldstart); valuestart= dataptr - 1; for (;;) { if (c == '\n' || c == MSDOS_EOF_CHAR) { ps.lno++; if (EOF_mmap(dataptr, endptr)) break; c= getc_mmap(dataptr); /* Found double eol, or start of new field */ if (EOF_mmap(dataptr, endptr) || c == '\n' || !isspace(c)) break; ungetc_mmap(c,dataptr, data); c= '\n'; } else if (EOF_mmap(dataptr, endptr)) { parse_error(&ps, &newpig, _("EOF during value of field `%.*s' (missing final newline)"), fieldlen,fieldstart); } c= getc_mmap(dataptr); } valuelen= dataptr - valuestart - 1; /* trim ending space on value */ while (valuelen && isspace(*(valuestart+valuelen-1))) valuelen--; for (nick = nicknames; nick->nick && (strncasecmp(nick->nick, fieldstart, fieldlen) || nick->nick[fieldlen] != '\0'); nick++) ; if (nick->nick) { fieldstart= nick->canon; fieldlen= strlen(fieldstart); } for (fip= fieldinfos, ip= fieldencountered; fip->name && strncasecmp(fieldstart,fip->name, fieldlen); fip++, ip++); if (fip->name) { value = m_realloc(value, valuelen + 1); memcpy(value,valuestart,valuelen); *(value + valuelen) = '\0'; if ((*ip)++) parse_error(&ps, &newpig, _("duplicate value for `%s' field"), fip->name); fip->rcall(&newpig, newpifp, &ps, value, fip); } else { if (fieldlen<2) parse_error(&ps, &newpig, _("user-defined field name `%.*s' too short"), fieldlen, fieldstart); larpp= &newpifp->arbs; while ((arp= *larpp) != NULL) { if (!strncasecmp(arp->name,fieldstart,fieldlen)) parse_error(&ps, &newpig, _("duplicate value for user-defined field `%.*s'"), fieldlen, fieldstart); larpp= &arp->next; } arp= nfmalloc(sizeof(struct arbitraryfield)); arp->name= nfstrnsave(fieldstart,fieldlen); arp->value= nfstrnsave(valuestart,valuelen); arp->next= NULL; *larpp= arp; } if (EOF_mmap(dataptr, endptr) || c == '\n' || c == MSDOS_EOF_CHAR) break; } /* loop per field */ if (pdone && donep) parse_error(&ps, &newpig, _("several package info entries found, only one allowed")); parse_must_have_field(&ps, &newpig, newpig.name, "package name"); if ((flags & pdb_recordavailable) || newpig.status != stat_notinstalled) { parse_ensure_have_field(&ps, &newpig, &newpifp->description, "description"); parse_ensure_have_field(&ps, &newpig, &newpifp->maintainer, "maintainer"); if (newpig.status != stat_halfinstalled) parse_must_have_field(&ps, &newpig, newpifp->version.version, "version"); } if (flags & pdb_recordavailable) parse_ensure_have_field(&ps, &newpig, &newpifp->architecture, "architecture"); /* 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.version) { if (newpig.status == stat_installed || newpig.status == stat_notinstalled) parse_error(&ps, &newpig, _("Configured-Version for package with inappropriate Status")); } else { if (newpig.status == stat_installed) newpig.configversion= newpifp->version; } } if (newpig.trigaw.head && (newpig.status <= stat_configfiles || newpig.status >= stat_triggerspending)) parse_error(&ps, &newpig, _("package has status %s but triggers are awaited"), statusinfos[newpig.status].name); else if (newpig.status == stat_triggersawaited && !newpig.trigaw.head) parse_error(&ps, &newpig, _("package has status triggers-awaited but no triggers " "awaited")); if (!(newpig.status == stat_triggerspending || newpig.status == stat_triggersawaited) && newpig.trigpend_head) parse_error(&ps, &newpig, _("package has status %s but triggers are pending"), statusinfos[newpig.status].name); else if (newpig.status == stat_triggerspending && !newpig.trigpend_head) parse_error(&ps, &newpig, _("package has status triggers-pending but no triggers " "pending")); /* FIXME: There was a bug that could make a not-installed package have * conffiles, so we check for them here and remove them (rather than * calling it an error, which will do at some point). */ if (!(flags & pdb_recordavailable) && newpig.status == stat_notinstalled && newpifp->conffiles) { parse_warn(&ps, &newpig, _("Package which in state not-installed has conffiles, " "forgetting them")); newpifp->conffiles= NULL; } /* XXX: Mark not-installed leftover packages for automatic removal on * next database dump. This code can be removed after dpkg 1.16.x, when * there's guarantee that no leftover is found on the status file on * major distributions. */ if (!(flags & pdb_recordavailable) && newpig.status == stat_notinstalled && newpig.eflag == eflag_ok && (newpig.want == want_purge || newpig.want == want_deinstall || newpig.want == want_hold)) { newpig.want = want_unknown; } pigp= findpackage(newpig.name); pifp= (flags & pdb_recordavailable) ? &pigp->available : &pigp->installed; if ((flags & pdb_ignoreolder) && versioncompare(&newpifp->version, &pifp->version) < 0) continue; /* 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->files= NULL; pigp->trigpend_head = newpig.trigpend_head; pigp->trigaw = newpig.trigaw; for (ta = pigp->trigaw.head; ta; ta = ta->sameaw.next) { assert(ta->aw == &newpig); ta->aw = pigp; /* ->othertrigaw_head is updated by trig_note_aw in *(findpackage()) * rather than in newpig */ } } else if (!(flags & pdb_ignorefiles)) { pigp->files= newpig.files; } if (donep) *donep= pigp; pdone++; if (EOF_mmap(dataptr, endptr)) break; if (c == '\n') ps.lno++; } if (data != NULL) { #ifdef USE_MMAP munmap(data, st.st_size); #else free(data); #endif } free(value); pop_cleanup(ehflag_normaltidy); if (close(fd)) ohshite(_("failed to close after read: `%.255s'"),filename); if (donep && !pdone) ohshit(_("no package information in `%.255s'"),filename); if (warncount) *warncount = ps.warncount; return pdone; }