# build/parsePreamble.c

Go to the documentation of this file.

    00001
    00006 #include "system.h"
    00007
    00008 #include <rpmio_internal.h>
    00009 #include <rpmbuild.h>
    00010 #include "debug.h"
    00011
    00012 /*@access FD_t @*/      /* compared with NULL */
    00013
    00016 /*@observer@*/ /*@unchecked@*/
    00017 static rpmTag copyTagsDuringParse[] = {
    00018     RPMTAG_EPOCH,
    00019     RPMTAG_VERSION,
    00020     RPMTAG_RELEASE,
    00021     RPMTAG_LICENSE,
    00022     RPMTAG_PACKAGER,
    00023     RPMTAG_DISTRIBUTION,
    00024     RPMTAG_DISTURL,
    00025     RPMTAG_VENDOR,
    00026     RPMTAG_ICON,
    00027     RPMTAG_URL,
    00028     RPMTAG_CHANGELOGTIME,
    00029     RPMTAG_CHANGELOGNAME,
    00030     RPMTAG_CHANGELOGTEXT,
    00031     RPMTAG_PREFIXES,
    00032     RPMTAG_RHNPLATFORM,
    00033     0
    00034 };
    00035
    00038 /*@observer@*/ /*@unchecked@*/
    00039 static rpmTag requiredTags[] = {
    00040     RPMTAG_NAME,
    00041     RPMTAG_VERSION,
    00042     RPMTAG_RELEASE,
    00043     RPMTAG_SUMMARY,
    00044     RPMTAG_GROUP,
    00045     RPMTAG_LICENSE,
    00046     0
    00047 };
    00048
    00051 static void addOrAppendListEntry(Header h, int_32 tag, char * line)
    00052         /*@modifies h @*/
    00053 {
    00054     int xx;
    00055     int argc;
    00056     const char **argv;
    00057
    00058     xx = poptParseArgvString(line, &argc, &argv);
    00059     if (argc)
    00060         xx = headerAddOrAppendEntry(h, tag, RPM_STRING_ARRAY_TYPE, argv, argc);
    00061     argv = _free(argv);
    00062 }
    00063
    00064 /* Parse a simple part line that only take -n <pkg> or <pkg> */
    00065 /* <pkg> is return in name as a pointer into a static buffer */
    00066
    00069 static int parseSimplePart(char *line, /*@out@*/char **name, /*@out@*/int *flag)
    00070         /*@globals internalState@*/
    00071         /*@modifies *name, *flag, internalState @*/
    00072 {
    00073     char *tok;
    00074     char linebuf[BUFSIZ];
    00075     static char buf[BUFSIZ];
    00076
    00077     strcpy(linebuf, line);
    00078
    00079     /* Throw away the first token (the %xxxx) */
    00080     (void)strtok(linebuf, " \t\n");
    00081
    00082     if (!(tok = strtok(NULL, " \t\n"))) {
    00083         *name = NULL;
    00084         return 0;
    00085     }
    00086
    00087     if (!strcmp(tok, "-n")) {
    00088         if (!(tok = strtok(NULL, " \t\n")))
    00089             return 1;
    00090         *flag = PART_NAME;
    00091     } else {
    00092         *flag = PART_SUBNAME;
    00093     }
    00094     strcpy(buf, tok);
    00095     *name = buf;
    00096
    00097     return (strtok(NULL, " \t\n")) ? 1 : 0;
    00098 }
    00099
    00102 static inline int parseYesNo(const char * s)
    00103         /*@*/
    00104 {
    00105     return ((!s || (s[0] == 'n' || s[0] == 'N' || s[0] == '0') ||
    00106         !xstrcasecmp(s, "false") || !xstrcasecmp(s, "off"))
    00107             ? 0 : 1);
    00108 }
    00109
    00110 typedef struct tokenBits_s {
    00111 /*@observer@*/ /*@null@*/ const char * name;
    00112     rpmsenseFlags bits;
    00113 } * tokenBits;
    00114
    00117 /*@observer@*/ /*@unchecked@*/
    00118 static struct tokenBits_s installScriptBits[] = {
    00119     { "interp",         RPMSENSE_INTERP },
    00120     { "prereq",         RPMSENSE_PREREQ },
    00121     { "preun",          RPMSENSE_SCRIPT_PREUN },
    00122     { "pre",            RPMSENSE_SCRIPT_PRE },
    00123     { "postun",         RPMSENSE_SCRIPT_POSTUN },
    00124     { "post",           RPMSENSE_SCRIPT_POST },
    00125     { "rpmlib",         RPMSENSE_RPMLIB },
    00126     { "verify",         RPMSENSE_SCRIPT_VERIFY },
    00127     { NULL, 0 }
    00128 };
    00129
    00132 /*@observer@*/ /*@unchecked@*/
    00133 static struct tokenBits_s buildScriptBits[] = {
    00134     { "prep",           RPMSENSE_SCRIPT_PREP },
    00135     { "build",          RPMSENSE_SCRIPT_BUILD },
    00136     { "install",        RPMSENSE_SCRIPT_INSTALL },
    00137     { "clean",          RPMSENSE_SCRIPT_CLEAN },
    00138     { NULL, 0 }
    00139 };
    00140
    00143 static int parseBits(const char * s, const tokenBits tokbits,
    00144                 /*@out@*/ rpmsenseFlags * bp)
    00145         /*@modifies *bp @*/
    00146 {
    00147     tokenBits tb;
    00148     const char * se;
    00149     rpmsenseFlags bits = RPMSENSE_ANY;
    00150     int c = 0;
    00151
    00152     if (s) {
    00153         while (*s != '\0') {
    00154             while ((c = *s) && xisspace(c)) s++;
    00155             se = s;
    00156             while ((c = *se) && xisalpha(c)) se++;
    00157             if (s == se)
    00158                 break;
    00159             for (tb = tokbits; tb->name; tb++) {
    00160                 if (tb->name != NULL &&
    00161                     strlen(tb->name) == (se-s) && !strncmp(tb->name, s, (se-s)))
    00162                     /*@innerbreak@*/ break;
    00163             }
    00164             if (tb->name == NULL)
    00165                 break;
    00166             bits |= tb->bits;
    00167             while ((c = *se) && xisspace(c)) se++;
    00168             if (c != ',')
    00169                 break;
    00170             s = ++se;
    00171         }
    00172     }
    00173     if (c == 0 && bp) *bp = bits;
    00174     return (c ? RPMERR_BADSPEC : 0);
    00175 }
    00176
    00179 static inline char * findLastChar(char * s)
    00180         /*@*/
    00181 {
    00182     char *res = s;
    00183
    00184     while (*s != '\0') {
    00185         if (! xisspace(*s))
    00186             res = s;
    00187         s++;
    00188     }
    00189
    00190     /*@-temptrans -retalias@*/
    00191     return res;
    00192     /*@=temptrans =retalias@*/
    00193 }
    00194
    00197 static int isMemberInEntry(Header h, const char *name, rpmTag tag)
    00198         /*@*/
    00199 {
    00200     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
    00201     HFD_t hfd = headerFreeData;
    00202     const char ** names;
    00203     rpmTagType type;
    00204     int count;
    00205
    00206     if (!hge(h, tag, &type, (void **)&names, &count))
    00207         return -1;
    00208     while (count--) {
    00209         if (!xstrcasecmp(names[count], name))
    00210             break;
    00211     }
    00212     names = hfd(names, type);
    00213     return (count >= 0 ? 1 : 0);
    00214 }
    00215
    00218 static int checkForValidArchitectures(Spec spec)
    00219         /*@*/
    00220 {
    00221 #ifndef DYING
    00222     const char *arch = NULL;
    00223     const char *os = NULL;
    00224
    00225     rpmGetArchInfo(&arch, NULL);
    00226     rpmGetOsInfo(&os, NULL);
    00227 #else
    00228     const char *arch = rpmExpand("%{_target_cpu}", NULL);
    00229     const char *os = rpmExpand("%{_target_os}", NULL);
    00230 #endif
    00231
    00232     if (isMemberInEntry(spec->buildRestrictions,
    00233                         arch, RPMTAG_EXCLUDEARCH) == 1) {
    00234         rpmError(RPMERR_BADSPEC, _("Architecture is excluded: %s\n"), arch);
    00235         return RPMERR_BADSPEC;
    00236     }
    00237     if (isMemberInEntry(spec->buildRestrictions,
    00238                         arch, RPMTAG_EXCLUSIVEARCH) == 0) {
    00239         rpmError(RPMERR_BADSPEC, _("Architecture is not included: %s\n"), arch);
    00240         return RPMERR_BADSPEC;
    00241     }
    00242     if (isMemberInEntry(spec->buildRestrictions,
    00243                         os, RPMTAG_EXCLUDEOS) == 1) {
    00244         rpmError(RPMERR_BADSPEC, _("OS is excluded: %s\n"), os);
    00245         return RPMERR_BADSPEC;
    00246     }
    00247     if (isMemberInEntry(spec->buildRestrictions,
    00248                         os, RPMTAG_EXCLUSIVEOS) == 0) {
    00249         rpmError(RPMERR_BADSPEC, _("OS is not included: %s\n"), os);
    00250         return RPMERR_BADSPEC;
    00251     }
    00252
    00253     return 0;
    00254 }
    00255
    00262 static int checkForRequired(Header h, const char * NVR)
    00263         /*@modifies h @*/ /* LCL: parse error here with modifies */
    00264 {
    00265     int res = 0;
    00266     rpmTag * p;
    00267
    00268     for (p = requiredTags; *p != 0; p++) {
    00269         if (!headerIsEntry(h, *p)) {
    00270             rpmError(RPMERR_BADSPEC,
    00271                         _("%s field must be present in package: %s\n"),
    00272                         tagName(*p), NVR);
    00273             res = 1;
    00274         }
    00275     }
    00276
    00277     return res;
    00278 }
    00279
    00286 static int checkForDuplicates(Header h, const char * NVR)
    00287         /*@modifies h @*/
    00288 {
    00289     int res = 0;
    00290     int lastTag, tag;
    00291     HeaderIterator hi;
    00292
    00293     for (hi = headerInitIterator(h), lastTag = 0;
    00294         headerNextIterator(hi, &tag, NULL, NULL, NULL);
    00295         lastTag = tag)
    00296     {
    00297         if (tag != lastTag)
    00298             continue;
    00299         rpmError(RPMERR_BADSPEC, _("Duplicate %s entries in package: %s\n"),
    00300                      tagName(tag), NVR);
    00301         res = 1;
    00302     }
    00303     hi = headerFreeIterator(hi);
    00304
    00305     return res;
    00306 }
    00307
    00310 /*@observer@*/ /*@unchecked@*/
    00311 static struct optionalTag {
    00312     rpmTag      ot_tag;
    00313 /*@observer@*/ /*@null@*/ const char * ot_mac;
    00314 } optionalTags[] = {
    00315     { RPMTAG_VENDOR,            "%{vendor}" },
    00316     { RPMTAG_PACKAGER,          "%{packager}" },
    00317     { RPMTAG_DISTRIBUTION,      "%{distribution}" },
    00318     { RPMTAG_DISTURL,           "%{disturl}" },
    00319     { -1, NULL }
    00320 };
    00321
    00324 static void fillOutMainPackage(Header h)
    00325         /*@globals rpmGlobalMacroContext @*/
    00326         /*@modifies h, rpmGlobalMacroContext @*/
    00327 {
    00328     struct optionalTag *ot;
    00329
    00330     for (ot = optionalTags; ot->ot_mac != NULL; ot++) {
    00331         if (!headerIsEntry(h, ot->ot_tag)) {
    00332             const char *val = rpmExpand(ot->ot_mac, NULL);
    00333             if (val && *val != '%')
    00334                 (void) headerAddEntry(h, ot->ot_tag, RPM_STRING_TYPE, (void *)val, 1);
    00335             val = _free(val);
    00336         }
    00337     }
    00338 }
    00339
    00342 static int readIcon(Header h, const char * file)
    00343         /*@globals rpmGlobalMacroContext,
    00344                 fileSystem@*/
    00345         /*@modifies h, rpmGlobalMacroContext, fileSystem @*/
    00346 {
    00347     const char *fn = NULL;
    00348     char *icon;
    00349     FD_t fd;
    00350     int rc = 0;
    00351     off_t size;
    00352     size_t nb, iconsize;
    00353
    00354     /* XXX use rpmGenPath(rootdir, "%{_sourcedir}/", file) for icon path. */
    00355     fn = rpmGetPath("%{_sourcedir}/", file, NULL);
    00356
    00357     fd = Fopen(fn, "r.ufdio");
    00358     if (fd == NULL || Ferror(fd)) {
    00359         rpmError(RPMERR_BADSPEC, _("Unable to open icon %s: %s\n"),
    00360                 fn, Fstrerror(fd));
    00361         rc = RPMERR_BADSPEC;
    00362         goto exit;
    00363     }
    00364     size = fdSize(fd);
    00365     iconsize = (size >= 0 ? size : (8 * BUFSIZ));
    00366     if (iconsize == 0) {
    00367         (void) Fclose(fd);
    00368         rc = 0;
    00369         goto exit;
    00370     }
    00371
    00372     icon = xmalloc(iconsize + 1);
    00373     *icon = '\0';
    00374
    00375     nb = Fread(icon, sizeof(icon[0]), iconsize, fd);
    00376     if (Ferror(fd) || (size >= 0 && nb != size)) {
    00377         rpmError(RPMERR_BADSPEC, _("Unable to read icon %s: %s\n"),
    00378                 fn, Fstrerror(fd));
    00379         rc = RPMERR_BADSPEC;
    00380     }
    00381     (void) Fclose(fd);
    00382     if (rc)
    00383         goto exit;
    00384
    00385     if (! strncmp(icon, "GIF", sizeof("GIF")-1)) {
    00386         (void) headerAddEntry(h, RPMTAG_GIF, RPM_BIN_TYPE, icon, iconsize);
    00387     } else if (! strncmp(icon, "/* XPM", sizeof("/* XPM")-1)) {
    00388         (void) headerAddEntry(h, RPMTAG_XPM, RPM_BIN_TYPE, icon, iconsize);
    00389     } else {
    00390         rpmError(RPMERR_BADSPEC, _("Unknown icon type: %s\n"), file);
    00391         rc = RPMERR_BADSPEC;
    00392         goto exit;
    00393     }
    00394     icon = _free(icon);
    00395
    00396 exit:
    00397     fn = _free(fn);
    00398     return rc;
    00399 }
    00400
    00401 spectag stashSt(Spec spec, Header h, int tag, const char * lang)
    00402 {
    00403     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
    00404     spectag t = NULL;
    00405
    00406     if (spec->st) {
    00407         spectags st = spec->st;
    00408         if (st->st_ntags == st->st_nalloc) {
    00409             st->st_nalloc += 10;
    00410             st->st_t = xrealloc(st->st_t, st->st_nalloc * sizeof(*(st->st_t)));
    00411         }
    00412         t = st->st_t + st->st_ntags++;
    00413         t->t_tag = tag;
    00414         t->t_startx = spec->lineNum - 1;
    00415         t->t_nlines = 1;
    00416         t->t_lang = xstrdup(lang);
    00417         t->t_msgid = NULL;
    00418         if (!(t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG))) {
    00419             char *n;
    00420             if (hge(h, RPMTAG_NAME, NULL, (void **) &n, NULL)) {
    00421                 char buf[1024];
    00422                 sprintf(buf, "%s(%s)", n, tagName(tag));
    00423                 t->t_msgid = xstrdup(buf);
    00424             }
    00425         }
    00426     }
    00427     /*@-usereleased -compdef@*/
    00428     return t;
    00429     /*@=usereleased =compdef@*/
    00430 }
    00431
    00432 #define SINGLE_TOKEN_ONLY \
    00433 if (multiToken) { \
    00434     rpmError(RPMERR_BADSPEC, _("line %d: Tag takes single token only: %s\n"), \
    00435              spec->lineNum, spec->line); \
    00436     return RPMERR_BADSPEC; \
    00437 }
    00438
    00439 /*@-redecl@*/
    00440 extern int noLang;
    00441 /*@=redecl@*/
    00442
    00445 static int handlePreambleTag(Spec spec, Package pkg, int tag, const char *macro,
    00446                              const char *lang)
    00447         /*@globals rpmGlobalMacroContext,
    00448                 fileSystem @*/
    00449         /*@modifies spec->macros, spec->st, spec->buildRootURL,
    00450                 spec->sources, spec->numSources, spec->noSource,
    00451                 spec->buildRestrictions, spec->BANames, spec->BACount,
    00452                 spec->line, spec->gotBuildRootURL,
    00453                 pkg->header, pkg->autoProv, pkg->autoReq, pkg->icon,
    00454                 rpmGlobalMacroContext, fileSystem @*/
    00455 {
    00456     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
    00457     HFD_t hfd = headerFreeData;
    00458     char * field = spec->line;
    00459     char * end;
    00460     char ** array;
    00461     int multiToken = 0;
    00462     rpmsenseFlags tagflags;
    00463     rpmTagType type;
    00464     int len;
    00465     int num;
    00466     int rc;
    00467     int xx;
    00468
    00469     if (field == NULL) return RPMERR_BADSPEC;   /* XXX can't happen */
    00470     /* Find the start of the "field" and strip trailing space */
    00471     while ((*field) && (*field != ':'))
    00472         field++;
    00473     if (*field != ':') {
    00474         rpmError(RPMERR_BADSPEC, _("line %d: Malformed tag: %s\n"),
    00475                  spec->lineNum, spec->line);
    00476         return RPMERR_BADSPEC;
    00477     }
    00478     field++;
    00479     SKIPSPACE(field);
    00480     if (!*field) {
    00481         /* Empty field */
    00482         rpmError(RPMERR_BADSPEC, _("line %d: Empty tag: %s\n"),
    00483                  spec->lineNum, spec->line);
    00484         return RPMERR_BADSPEC;
    00485     }
    00486     end = findLastChar(field);
    00487     *(end+1) = '\0';
    00488
    00489     /* See if this is multi-token */
    00490     end = field;
    00491     SKIPNONSPACE(end);
    00492     if (*end != '\0')
    00493         multiToken = 1;
    00494
    00495     switch (tag) {
    00496       case RPMTAG_NAME:
    00497       case RPMTAG_VERSION:
    00498       case RPMTAG_RELEASE:
    00499       case RPMTAG_URL:
    00500       case RPMTAG_RHNPLATFORM:
    00501         SINGLE_TOKEN_ONLY;
    00502         /* These macros are for backward compatibility */
    00503         if (tag == RPMTAG_VERSION) {
    00504             if (strchr(field, '-') != NULL) {
    00505                 rpmError(RPMERR_BADSPEC, _("line %d: Illegal char '-' in %s: %s\n"),
    00506                     spec->lineNum, "version", spec->line);
    00507                 return RPMERR_BADSPEC;
    00508             }
    00509             addMacro(spec->macros, "PACKAGE_VERSION", NULL, field, RMIL_OLDSPEC);
    00510         } else if (tag == RPMTAG_RELEASE) {
    00511             if (strchr(field, '-') != NULL) {
    00512                 rpmError(RPMERR_BADSPEC, _("line %d: Illegal char '-' in %s: %s\n"),
    00513                     spec->lineNum, "release", spec->line);
    00514                 return RPMERR_BADSPEC;
    00515             }
    00516             addMacro(spec->macros, "PACKAGE_RELEASE", NULL, field, RMIL_OLDSPEC-1);
    00517         }
    00518         (void) headerAddEntry(pkg->header, tag, RPM_STRING_TYPE, field, 1);
    00519         break;
    00520       case RPMTAG_GROUP:
    00521       case RPMTAG_SUMMARY:
    00522         (void) stashSt(spec, pkg->header, tag, lang);
    00523         /*@fallthrough@*/
    00524       case RPMTAG_DISTRIBUTION:
    00525       case RPMTAG_VENDOR:
    00526       case RPMTAG_LICENSE:
    00527       case RPMTAG_PACKAGER:
    00528         if (!*lang)
    00529             (void) headerAddEntry(pkg->header, tag, RPM_STRING_TYPE, field, 1);
    00530         else if (!(noLang && strcmp(lang, RPMBUILD_DEFAULT_LANG)))
    00531             (void) headerAddI18NString(pkg->header, tag, field, lang);
    00532         break;
    00533       case RPMTAG_BUILDROOT:
    00534         SINGLE_TOKEN_ONLY;
    00535       { const char * buildRoot = NULL;
    00536         const char * buildRootURL = spec->buildRootURL;
    00537
    00538         /*
    00539          * Note: rpmGenPath should guarantee a "canonical" path. That means
    00540          * that the following pathologies should be weeded out:
    00541          *          //bin//sh
    00542          *          //usr//bin/
    00543          *          /.././../usr/../bin//./sh
    00544          */
    00545         if (buildRootURL == NULL) {
    00546             buildRootURL = rpmGenPath(NULL, "%{?buildroot:%{buildroot}}", NULL);
    00547             if (strcmp(buildRootURL, "/")) {
    00548                 spec->buildRootURL = buildRootURL;
    00549                 macro = NULL;
    00550             } else {
    00551                 const char * specURL = field;
    00552
    00553                 buildRootURL = _free(buildRootURL);
    00554                 (void) urlPath(specURL, (const char **)&field);
    00555                 /*@-branchstate@*/
    00556                 if (*field == '\0') field = "/";
    00557                 /*@=branchstate@*/
    00558                 buildRootURL = rpmGenPath(spec->rootURL, field, NULL);
    00559                 spec->buildRootURL = buildRootURL;
    00560                 field = (char *) buildRootURL;
    00561             }
    00562             spec->gotBuildRootURL = 1;
    00563         } else {
    00564             macro = NULL;
    00565         }
    00566         buildRootURL = rpmGenPath(NULL, spec->buildRootURL, NULL);
    00567         (void) urlPath(buildRootURL, &buildRoot);
    00568         /*@-branchstate@*/
    00569         if (*buildRoot == '\0') buildRoot = "/";
    00570         /*@=branchstate@*/
    00571         if (!strcmp(buildRoot, "/")) {
    00572             rpmError(RPMERR_BADSPEC,
    00573                      _("BuildRoot can not be \"/\": %s\n"), spec->buildRootURL);
    00574             buildRootURL = _free(buildRootURL);
    00575             return RPMERR_BADSPEC;
    00576         }
    00577         buildRootURL = _free(buildRootURL);
    00578       } break;
    00579       case RPMTAG_PREFIXES:
    00580         addOrAppendListEntry(pkg->header, tag, field);
    00581         xx = hge(pkg->header, tag, &type, (void **)&array, &num);
    00582         while (num--) {
    00583             len = strlen(array[num]);
    00584             if (array[num][len - 1] == '/' && len > 1) {
    00585                 rpmError(RPMERR_BADSPEC,
    00586                          _("line %d: Prefixes must not end with \"/\": %s\n"),
    00587                          spec->lineNum, spec->line);
    00588                 array = hfd(array, type);
    00589                 return RPMERR_BADSPEC;
    00590             }
    00591         }
    00592         array = hfd(array, type);
    00593         break;
    00594       case RPMTAG_DOCDIR:
    00595         SINGLE_TOKEN_ONLY;
    00596         if (field[0] != '/') {
    00597             rpmError(RPMERR_BADSPEC,
    00598                      _("line %d: Docdir must begin with '/': %s\n"),
    00599                      spec->lineNum, spec->line);
    00600             return RPMERR_BADSPEC;
    00601         }
    00602         macro = NULL;
    00603         delMacro(NULL, "_docdir");
    00604         addMacro(NULL, "_docdir", NULL, field, RMIL_SPEC);
    00605         break;
    00606       case RPMTAG_EPOCH:
    00607         SINGLE_TOKEN_ONLY;
    00608         if (parseNum(field, &num)) {
    00609             rpmError(RPMERR_BADSPEC,
    00610                      _("line %d: Epoch/Serial field must be a number: %s\n"),
    00611                      spec->lineNum, spec->line);
    00612             return RPMERR_BADSPEC;
    00613         }
    00614         xx = headerAddEntry(pkg->header, tag, RPM_INT32_TYPE, &num, 1);
    00615         break;
    00616       case RPMTAG_AUTOREQPROV:
    00617         pkg->autoReq = parseYesNo(field);
    00618         pkg->autoProv = pkg->autoReq;
    00619         break;
    00620       case RPMTAG_AUTOREQ:
    00621         pkg->autoReq = parseYesNo(field);
    00622         break;
    00623       case RPMTAG_AUTOPROV:
    00624         pkg->autoProv = parseYesNo(field);
    00625         break;
    00626       case RPMTAG_SOURCE:
    00627       case RPMTAG_PATCH:
    00628         SINGLE_TOKEN_ONLY;
    00629         macro = NULL;
    00630         if ((rc = addSource(spec, pkg, field, tag)))
    00631             return rc;
    00632         break;
    00633       case RPMTAG_ICON:
    00634         SINGLE_TOKEN_ONLY;
    00635         if ((rc = addSource(spec, pkg, field, tag)))
    00636             return rc;
    00637         if ((rc = readIcon(pkg->header, field)))
    00638             return RPMERR_BADSPEC;
    00639         break;
    00640       case RPMTAG_NOSOURCE:
    00641       case RPMTAG_NOPATCH:
    00642         spec->noSource = 1;
    00643         if ((rc = parseNoSource(spec, field, tag)))
    00644             return rc;
    00645         break;
    00646       case RPMTAG_BUILDPREREQ:
    00647       case RPMTAG_BUILDREQUIRES:
    00648         if ((rc = parseBits(lang, buildScriptBits, &tagflags))) {
    00649             rpmError(RPMERR_BADSPEC,
    00650                      _("line %d: Bad %s: qualifiers: %s\n"),
    00651                      spec->lineNum, tagName(tag), spec->line);
    00652             return rc;
    00653         }
    00654         if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
    00655             return rc;
    00656         break;
    00657       case RPMTAG_REQUIREFLAGS:
    00658       case RPMTAG_PREREQ:
    00659         if ((rc = parseBits(lang, installScriptBits, &tagflags))) {
    00660             rpmError(RPMERR_BADSPEC,
    00661                      _("line %d: Bad %s: qualifiers: %s\n"),
    00662                      spec->lineNum, tagName(tag), spec->line);
    00663             return rc;
    00664         }
    00665         if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
    00666             return rc;
    00667         break;
    00668       case RPMTAG_BUILDCONFLICTS:
    00669       case RPMTAG_CONFLICTFLAGS:
    00670       case RPMTAG_OBSOLETEFLAGS:
    00671       case RPMTAG_PROVIDEFLAGS:
    00672         tagflags = RPMSENSE_ANY;
    00673         if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
    00674             return rc;
    00675         break;
    00676       case RPMTAG_EXCLUDEARCH:
    00677       case RPMTAG_EXCLUSIVEARCH:
    00678       case RPMTAG_EXCLUDEOS:
    00679       case RPMTAG_EXCLUSIVEOS:
    00680         addOrAppendListEntry(spec->buildRestrictions, tag, field);
    00681         break;
    00682       case RPMTAG_BUILDARCHS:
    00683         if ((rc = poptParseArgvString(field,
    00684                                       &(spec->BACount),
    00685                                       &(spec->BANames)))) {
    00686             rpmError(RPMERR_BADSPEC,
    00687                      _("line %d: Bad BuildArchitecture format: %s\n"),
    00688                      spec->lineNum, spec->line);
    00689             return RPMERR_BADSPEC;
    00690         }
    00691         if (!spec->BACount)
    00692             spec->BANames = _free(spec->BANames);
    00693         break;
    00694
    00695       default:
    00696         rpmError(RPMERR_INTERNAL, _("Internal error: Bogus tag %d\n"), tag);
    00697         return RPMERR_INTERNAL;
    00698     }
    00699
    00700     if (macro)
    00701         addMacro(spec->macros, macro, NULL, field, RMIL_SPEC);
    00702
    00703     return 0;
    00704 }
    00705
    00706 /* This table has to be in a peculiar order.  If one tag is the */
    00707 /* same as another, plus a few letters, it must come first.     */
    00708
    00711 typedef struct PreambleRec_s {
    00712     rpmTag tag;
    00713     int len;
    00714     int multiLang;
    00715 /*@observer@*/ /*@null@*/ const char * token;
    00716 } * PreambleRec;
    00717
    00718 /*@unchecked@*/
    00719 static struct PreambleRec_s preambleList[] = {
    00720     {RPMTAG_NAME,               0, 0, "name"},
    00721     {RPMTAG_VERSION,            0, 0, "version"},
    00722     {RPMTAG_RELEASE,            0, 0, "release"},
    00723     {RPMTAG_EPOCH,              0, 0, "epoch"},
    00724     {RPMTAG_EPOCH,              0, 0, "serial"},
    00725     {RPMTAG_SUMMARY,            0, 1, "summary"},
    00726     {RPMTAG_LICENSE,            0, 0, "copyright"},
    00727     {RPMTAG_LICENSE,            0, 0, "license"},
    00728     {RPMTAG_DISTRIBUTION,       0, 0, "distribution"},
    00729     {RPMTAG_DISTURL,            0, 0, "disturl"},
    00730     {RPMTAG_VENDOR,             0, 0, "vendor"},
    00731     {RPMTAG_GROUP,              0, 1, "group"},
    00732     {RPMTAG_PACKAGER,           0, 0, "packager"},
    00733     {RPMTAG_URL,                0, 0, "url"},
    00734     {RPMTAG_SOURCE,             0, 0, "source"},
    00735     {RPMTAG_PATCH,              0, 0, "patch"},
    00736     {RPMTAG_NOSOURCE,           0, 0, "nosource"},
    00737     {RPMTAG_NOPATCH,            0, 0, "nopatch"},
    00738     {RPMTAG_EXCLUDEARCH,        0, 0, "excludearch"},
    00739     {RPMTAG_EXCLUSIVEARCH,      0, 0, "exclusivearch"},
    00740     {RPMTAG_EXCLUDEOS,          0, 0, "excludeos"},
    00741     {RPMTAG_EXCLUSIVEOS,        0, 0, "exclusiveos"},
    00742     {RPMTAG_ICON,               0, 0, "icon"},
    00743     {RPMTAG_PROVIDEFLAGS,       0, 0, "provides"},
    00744     {RPMTAG_REQUIREFLAGS,       0, 1, "requires"},
    00745     {RPMTAG_PREREQ,             0, 1, "prereq"},
    00746     {RPMTAG_CONFLICTFLAGS,      0, 0, "conflicts"},
    00747     {RPMTAG_OBSOLETEFLAGS,      0, 0, "obsoletes"},
    00748     {RPMTAG_PREFIXES,           0, 0, "prefixes"},
    00749     {RPMTAG_PREFIXES,           0, 0, "prefix"},
    00750     {RPMTAG_BUILDROOT,          0, 0, "buildroot"},
    00751     {RPMTAG_BUILDARCHS,         0, 0, "buildarchitectures"},
    00752     {RPMTAG_BUILDARCHS,         0, 0, "buildarch"},
    00753     {RPMTAG_BUILDCONFLICTS,     0, 0, "buildconflicts"},
    00754     {RPMTAG_BUILDPREREQ,        0, 1, "buildprereq"},
    00755     {RPMTAG_BUILDREQUIRES,      0, 1, "buildrequires"},
    00756     {RPMTAG_AUTOREQPROV,        0, 0, "autoreqprov"},
    00757     {RPMTAG_AUTOREQ,            0, 0, "autoreq"},
    00758     {RPMTAG_AUTOPROV,           0, 0, "autoprov"},
    00759     {RPMTAG_DOCDIR,             0, 0, "docdir"},
    00760     {RPMTAG_RHNPLATFORM,        0, 0, "rhnplatform"},
    00761     /*@-nullassign@*/   /* LCL: can't add null annotation */
    00762     {0, 0, 0, 0}
    00763     /*@=nullassign@*/
    00764 };
    00765
    00768 static inline void initPreambleList(void)
    00769         /*@globals preambleList @*/
    00770         /*@modifies preambleList @*/
    00771 {
    00772     PreambleRec p;
    00773     for (p = preambleList; p->token != NULL; p++)
    00774         if (p->token) p->len = strlen(p->token);
    00775 }
    00776
    00779 static int findPreambleTag(Spec spec, /*@out@*/int * tag,
    00780                 /*@null@*/ /*@out@*/ const char ** macro, /*@out@*/ char * lang)
    00781         /*@modifies *tag, *macro, *lang @*/
    00782 {
    00783     PreambleRec p;
    00784     char *s;
    00785
    00786     if (preambleList[0].len == 0)
    00787         initPreambleList();
    00788
    00789     for (p = preambleList; p->token != NULL; p++) {
    00790         if (p->token && !xstrncasecmp(spec->line, p->token, p->len))
    00791             break;
    00792     }
    00793     if (p->token == NULL)
    00794         return 1;
    00795
    00796     s = spec->line + p->len;
    00797     SKIPSPACE(s);
    00798
    00799     switch (p->multiLang) {
    00800     default:
    00801     case 0:
    00802         /* Unless this is a source or a patch, a ':' better be next */
    00803         if (p->tag != RPMTAG_SOURCE && p->tag != RPMTAG_PATCH) {
    00804             if (*s != ':') return 1;
    00805         }
    00806         *lang = '\0';
    00807         break;
    00808     case 1:     /* Parse optional ( <token> ). */
    00809         if (*s == ':') {
    00810             strcpy(lang, RPMBUILD_DEFAULT_LANG);
    00811             break;
    00812         }
    00813         if (*s != '(') return 1;
    00814         s++;
    00815         SKIPSPACE(s);
    00816         while (!xisspace(*s) && *s != ')')
    00817             *lang++ = *s++;
    00818         *lang = '\0';
    00819         SKIPSPACE(s);
    00820         if (*s != ')') return 1;
    00821         s++;
    00822         SKIPSPACE(s);
    00823         if (*s != ':') return 1;
    00824         break;
    00825     }
    00826
    00827     *tag = p->tag;
    00828     if (macro)
    00829         /*@-onlytrans -observertrans -dependenttrans@*/ /* FIX: double indirection. */
    00830         *macro = p->token;
    00831         /*@=onlytrans =observertrans =dependenttrans@*/
    00832     return 0;
    00833 }
    00834
    00835 int parsePreamble(Spec spec, int initialPackage)
    00836 {
    00837     int nextPart;
    00838     int tag, rc, xx;
    00839     char *name, *linep;
    00840     int flag;
    00841     Package pkg;
    00842     char NVR[BUFSIZ];
    00843     char lang[BUFSIZ];
    00844
    00845     strcpy(NVR, "(main package)");
    00846
    00847     pkg = newPackage(spec);
    00848
    00849     if (! initialPackage) {
    00850         /* There is one option to %package: <pkg> or -n <pkg> */
    00851         if (parseSimplePart(spec->line, &name, &flag)) {
    00852             rpmError(RPMERR_BADSPEC, _("Bad package specification: %s\n"),
    00853                         spec->line);
    00854             return RPMERR_BADSPEC;
    00855         }
    00856
    00857         if (!lookupPackage(spec, name, flag, NULL)) {
    00858             rpmError(RPMERR_BADSPEC, _("Package already exists: %s\n"),
    00859                         spec->line);
    00860             return RPMERR_BADSPEC;
    00861         }
    00862
    00863         /* Construct the package */
    00864         if (flag == PART_SUBNAME) {
    00865             const char * mainName;
    00866             xx = headerNVR(spec->packages->header, &mainName, NULL, NULL);
    00867             sprintf(NVR, "%s-%s", mainName, name);
    00868         } else
    00869             strcpy(NVR, name);
    00870         xx = headerAddEntry(pkg->header, RPMTAG_NAME, RPM_STRING_TYPE, NVR, 1);
    00871     }
    00872
    00873     if ((rc = readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
    00874         nextPart = PART_NONE;
    00875     } else {
    00876         if (rc)
    00877             return rc;
    00878         while (! (nextPart = isPart(spec->line))) {
    00879             const char * macro;
    00880             /* Skip blank lines */
    00881             linep = spec->line;
    00882             SKIPSPACE(linep);
    00883             if (*linep != '\0') {
    00884                 if (findPreambleTag(spec, &tag, &macro, lang)) {
    00885                     rpmError(RPMERR_BADSPEC, _("line %d: Unknown tag: %s\n"),
    00886                                 spec->lineNum, spec->line);
    00887                     return RPMERR_BADSPEC;
    00888                 }
    00889                 if (handlePreambleTag(spec, pkg, tag, macro, lang))
    00890                     return RPMERR_BADSPEC;
    00891                 if (spec->BANames && !spec->recursing)
    00892                     return PART_BUILDARCHITECTURES;
    00893             }
    00894             if ((rc =
    00895                  readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
    00896                 nextPart = PART_NONE;
    00897                 break;
    00898             }
    00899             if (rc)
    00900                 return rc;
    00901         }
    00902     }
    00903
    00904     /* Do some final processing on the header */
    00905
    00906     if (!spec->gotBuildRootURL && spec->buildRootURL) {
    00907         rpmError(RPMERR_BADSPEC, _("Spec file can't use BuildRoot\n"));
    00908         return RPMERR_BADSPEC;
    00909     }
    00910
    00911     /* XXX Skip valid arch check if not building binary package */
    00912     if (!spec->anyarch && checkForValidArchitectures(spec))
    00913         return RPMERR_BADSPEC;
    00914
    00915     if (pkg == spec->packages)
    00916         fillOutMainPackage(pkg->header);
    00917
    00918     if (checkForDuplicates(pkg->header, NVR))
    00919         return RPMERR_BADSPEC;
    00920
    00921     if (pkg != spec->packages)
    00922         headerCopyTags(spec->packages->header, pkg->header,
    00923                         (int_32 *)copyTagsDuringParse);
    00924
    00925     if (checkForRequired(pkg->header, NVR))
    00926         return RPMERR_BADSPEC;
    00927
    00928     return nextPart;
    00929 }