rpm  5.4.4
build/parsePreamble.c
Go to the documentation of this file.
00001 
00006 #include "system.h"
00007 
00008 #include <rpmio.h>
00009 #include <rpmiotypes.h>
00010 #include <rpmlog.h>
00011 #include <rpmurl.h>
00012 #include <argv.h>
00013 #include <mire.h>
00014 
00015 #define _RPMEVR_INTERNAL
00016 #define _RPMTAG_INTERNAL        /* XXX rpmTags->aTags */
00017 #include <rpmbuild.h>
00018 #include "debug.h"
00019 
00020 /*@access FD_t @*/      /* compared with NULL */
00021 /*@access headerTagIndices @*/  /* rpmTags->aTags */
00022 
00025 /*@observer@*/ /*@unchecked@*/
00026 static rpmTag copyTagsDuringParse[] = {
00027     RPMTAG_EPOCH,
00028     RPMTAG_VERSION,
00029     RPMTAG_RELEASE,
00030     RPMTAG_DISTEPOCH,
00031     RPMTAG_LICENSE,
00032     RPMTAG_GROUP,               /* XXX permissive. */
00033     RPMTAG_SUMMARY,             /* XXX permissive. */
00034     RPMTAG_DESCRIPTION,         /* XXX permissive. */
00035     RPMTAG_PACKAGER,
00036     RPMTAG_DISTRIBUTION,
00037     RPMTAG_DISTURL,
00038     RPMTAG_VENDOR,
00039     RPMTAG_ICON,
00040     RPMTAG_GIF,
00041     RPMTAG_XPM,
00042     RPMTAG_URL,
00043     RPMTAG_CHANGELOGTIME,
00044     RPMTAG_CHANGELOGNAME,
00045     RPMTAG_CHANGELOGTEXT,
00046     RPMTAG_PREFIXES,
00047     RPMTAG_DISTTAG,
00048     RPMTAG_BUGURL,
00049     RPMTAG_CVSID,
00050     RPMTAG_VARIANTS,
00051     RPMTAG_XMAJOR,
00052     RPMTAG_XMINOR,
00053     RPMTAG_REPOTAG,
00054     RPMTAG_KEYWORDS,
00055     0
00056 };
00057 
00060 /*@observer@*/ /*@unchecked@*/
00061 static rpmTag requiredTags[] = {
00062     RPMTAG_NAME,
00063     RPMTAG_VERSION,
00064     RPMTAG_RELEASE,
00065     RPMTAG_SUMMARY,
00066     RPMTAG_GROUP,
00067     RPMTAG_LICENSE,
00068     0
00069 };
00070 
00073 static void addOrAppendListEntry(Header h, rpmTag tag, char * line)
00074         /*@modifies h @*/
00075 {
00076     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
00077     int xx;
00078     int argc;
00079     const char **argv;
00080 
00081     xx = poptParseArgvString(line, &argc, &argv);
00082     if (argc) {
00083         he->tag = tag;
00084         he->t = RPM_STRING_ARRAY_TYPE;
00085         he->p.argv = argv;
00086         he->c = argc;
00087         he->append = 1;
00088         xx = headerPut(h, he, 0);
00089         he->append = 0;
00090     }
00091     argv = _free(argv);
00092 }
00093 
00094 /* Parse a simple part line that only take -n <pkg> or <pkg> */
00095 /* <pkg> is returned in name as a pointer into malloc'd storage. */
00096 
00099 static int parseSimplePart(Spec spec, /*@out@*/char ** Np,
00100                 /*@out@*/rpmParseState *flag)
00101         /*@globals internalState@*/
00102         /*@modifies *Np, *flag, internalState, spec->line @*/
00103 {
00104     char * s, * se;
00105     int rc = 0;         /* assume failure */
00106 
00107     if (Np)
00108         *Np = NULL;
00109 
00110     se = strchr(spec->line, '#');
00111     if (se) {
00112         *se = '\0';
00113         while (--se >= spec->line && strchr(" \t\n\r", *se) != NULL)
00114             *se = '\0';
00115     }
00116 
00117     s = xstrdup(spec->line);
00118     /* Throw away the first token (the %xxxx) */
00119     (void)strtok(s, " \t\n");
00120     
00121     if (!(se = strtok(NULL, " \t\n")))
00122         goto exit;
00123     
00124     if (!strcmp(se, "-n")) {
00125         if (!(se = strtok(NULL, " \t\n"))) {
00126             rc = 1;
00127             goto exit;
00128         }
00129         *flag = PART_NAME;
00130     } else
00131         *flag = PART_SUBNAME;
00132 
00133     if (Np)
00134         *Np = xstrdup(se);
00135 
00136     rc = (strtok(NULL, " \t\n") ? 1 : 0);
00137 
00138 exit:
00139     s = _free(s);
00140     return rc;
00141 }
00142 
00145 static inline int parseYesNo(const char * s)
00146         /*@*/
00147 {
00148     return ((!s || (s[0] == 'n' || s[0] == 'N' || s[0] == '0') ||
00149         !xstrcasecmp(s, "false") || !xstrcasecmp(s, "off"))
00150             ? 0 : 1);
00151 }
00152 
00153 typedef struct tokenBits_s {
00154 /*@observer@*/ /*@null@*/
00155     const char * name;
00156     rpmsenseFlags bits;
00157 } * tokenBits;
00158 
00161 /*@observer@*/ /*@unchecked@*/
00162 static struct tokenBits_s installScriptBits[] = {
00163     { "interp",         RPMSENSE_INTERP },
00164     { "preun",          RPMSENSE_SCRIPT_PREUN },
00165     { "pre",            RPMSENSE_SCRIPT_PRE },
00166     { "postun",         RPMSENSE_SCRIPT_POSTUN },
00167     { "post",           RPMSENSE_SCRIPT_POST },
00168     { "rpmlib",         RPMSENSE_RPMLIB },
00169     { "verify",         RPMSENSE_SCRIPT_VERIFY },
00170     { "hint",           RPMSENSE_MISSINGOK },
00171     { NULL, 0 }
00172 };
00173 
00176 /*@observer@*/ /*@unchecked@*/
00177 static struct tokenBits_s buildScriptBits[] = {
00178     { "prep",           RPMSENSE_SCRIPT_PREP },
00179     { "build",          RPMSENSE_SCRIPT_BUILD },
00180     { "install",        RPMSENSE_SCRIPT_INSTALL },
00181     { "clean",          RPMSENSE_SCRIPT_CLEAN },
00182     { "hint",           RPMSENSE_MISSINGOK },
00183     { NULL, 0 }
00184 };
00185 
00188 static int parseBits(const char * s, const tokenBits tokbits,
00189                 /*@out@*/ rpmsenseFlags * bp)
00190         /*@modifies *bp @*/
00191 {
00192     tokenBits tb;
00193     const char * se;
00194     rpmsenseFlags bits = RPMSENSE_ANY;
00195     int c = 0;
00196 
00197     if (s) {
00198         while (*s != '\0') {
00199             while ((c = *s) && xisspace(c)) s++;
00200             se = s;
00201             while ((c = *se) && xisalpha(c)) se++;
00202             if (s == se)
00203                 break;
00204             for (tb = tokbits; tb->name; tb++) {
00205                 if (tb->name != NULL &&
00206                     strlen(tb->name) == (size_t)(se-s) && !strncmp(tb->name, s, (se-s)))
00207                     /*@innerbreak@*/ break;
00208             }
00209             if (tb->name == NULL)
00210                 break;
00211             bits |= tb->bits;
00212             while ((c = *se) && xisspace(c)) se++;
00213             if (c != ',')
00214                 break;
00215             s = ++se;
00216         }
00217     }
00218     if (c == 0 && bp) *bp = bits;
00219     return (c ? RPMRC_FAIL : RPMRC_OK);
00220 }
00221 
00224 /*@null@*/
00225 static inline char * findLastChar(char * s)
00226         /*@modifies *s @*/
00227 {
00228     char *se = s + strlen(s);
00229 
00230     /* Right trim white space. */
00231     while (--se > s && strchr(" \t\n\r", *se) != NULL)
00232         *se = '\0';
00233     /* Truncate comments. */
00234     if ((se = strchr(s, '#')) != NULL) {
00235         *se = '\0';
00236         while (--se > s && strchr(" \t\n\r", *se) != NULL)
00237             *se = '\0';
00238     }
00239 /*@-temptrans -retalias @*/
00240     return se;
00241 /*@=temptrans =retalias @*/
00242 }
00243 
00246 static int isMemberInEntry(Header h, const char *name, rpmTag tag)
00247         /*@globals internalState @*/
00248         /*@modifies internalState @*/
00249 {
00250     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
00251     int rc = -1;
00252     int xx;
00253 
00254     he->tag = tag;
00255     xx = headerGet(h, he, 0);
00256     if (!xx)
00257         return rc;
00258     rc = 0;
00259     while (he->c) {
00260         he->c--;
00261         if (xstrcasecmp(he->p.argv[he->c], name))
00262             continue;
00263         rc = 1;
00264         break;
00265     }
00266     he->p.ptr = _free(he->p.ptr);
00267     return rc;
00268 }
00269 
00272 static int checkForValidArchitectures(Spec spec)
00273         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
00274         /*@modifies rpmGlobalMacroContext, internalState @*/
00275 {
00276     const char *arch = rpmExpand("%{_target_cpu}", NULL);
00277     const char *os = rpmExpand("%{_target_os}", NULL);
00278     int rc = RPMRC_FAIL;        /* assume failure. */
00279     
00280     if (isMemberInEntry(spec->sourceHeader, arch, RPMTAG_EXCLUDEARCH) == 1) {
00281         rpmlog(RPMLOG_ERR, _("Architecture is excluded: %s\n"), arch);
00282         goto exit;
00283     }
00284     if (isMemberInEntry(spec->sourceHeader, arch, RPMTAG_EXCLUSIVEARCH) == 0) {
00285         rpmlog(RPMLOG_ERR, _("Architecture is not included: %s\n"), arch);
00286         goto exit;
00287     }
00288     if (isMemberInEntry(spec->sourceHeader, os, RPMTAG_EXCLUDEOS) == 1) {
00289         rpmlog(RPMLOG_ERR, _("OS is excluded: %s\n"), os);
00290         goto exit;
00291     }
00292     if (isMemberInEntry(spec->sourceHeader, os, RPMTAG_EXCLUSIVEOS) == 0) {
00293         rpmlog(RPMLOG_ERR, _("OS is not included: %s\n"), os);
00294         goto exit;
00295     }
00296     rc = 0;
00297 exit:
00298     arch = _free(arch);
00299     os = _free(os);
00300     return rc;
00301 }
00302 
00309 static rpmRC checkForRequired(Header h, const char * NVR)
00310         /*@*/
00311 {
00312     rpmTag * p;
00313     rpmRC rc = RPMRC_OK;
00314 
00315     for (p = requiredTags; *p != 0; p++) {
00316         if (!headerIsEntry(h, *p)) {
00317             rpmlog(RPMLOG_ERR,
00318                         _("%s field must be present in package: %s\n"),
00319                         tagName(*p), NVR);
00320             rc = RPMRC_FAIL;
00321         }
00322     }
00323 
00324     return rc;
00325 }
00326 
00333 static rpmRC checkForDuplicates(Header h, const char * NVR)
00334         /*@globals internalState @*/
00335         /*@modifies h, internalState @*/
00336 {
00337     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
00338     HeaderIterator hi;
00339     rpmTag lastTag = 0;
00340     rpmRC rc = RPMRC_OK;
00341     
00342     for (hi = headerInit(h);
00343         headerNext(hi, he, 0);
00344         he->p.ptr = _free(he->p.ptr))
00345     {
00346         if (he->tag != lastTag) {
00347             lastTag = he->tag;
00348             continue;
00349         }
00350         rpmlog(RPMLOG_ERR, _("Duplicate %s entries in package: %s\n"),
00351                      tagName(he->tag), NVR);
00352         rc = RPMRC_FAIL;
00353     }
00354     hi = headerFini(hi);
00355 
00356     return rc;
00357 }
00358 
00361 /*@observer@*/ /*@unchecked@*/
00362 static struct optionalTag {
00363     rpmTag      ot_tag;
00364 /*@observer@*/ /*@null@*/
00365     const char * ot_mac;
00366 } optionalTags[] = {
00367     { RPMTAG_VENDOR,            "%{vendor}" },
00368     { RPMTAG_PACKAGER,          "%{packager}" },
00369     { RPMTAG_DISTEPOCH,         "%{distepoch}" },
00370     { RPMTAG_DISTRIBUTION,      "%{distribution}" },
00371     { RPMTAG_DISTTAG,           "%{disttag}" },
00372     { RPMTAG_DISTURL,           "%{disturl}" },
00373     { RPMTAG_BUGURL,            "%{bugurl}" },
00374     { 0xffffffff,               "%{class}" },
00375     { -1, NULL }
00376 };
00377 
00380 static void fillOutMainPackage(Header h)
00381         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
00382         /*@modifies h, rpmGlobalMacroContext, internalState @*/
00383 {
00384     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
00385     struct optionalTag *ot;
00386     int xx;
00387 
00388     for (ot = optionalTags; ot->ot_mac != NULL; ot++) {
00389         const char * val;
00390         rpmTag tag;
00391 
00392         tag = ot->ot_tag;
00393 
00394         /* Generate arbitrary tag (if necessary). */
00395         if (tag == 0xffffffff) {
00396             val = tagCanonicalize(ot->ot_mac + (sizeof("%{")-1));
00397             tag = tagGenerate(val);
00398             val = _free(val);
00399         }
00400 
00401         if (headerIsEntry(h, tag))
00402             continue;
00403         val = rpmExpand(ot->ot_mac, NULL);
00404         if (val && *val != '%') {
00405                 he->tag = tag;
00406                 he->t = RPM_STRING_TYPE;
00407                 he->p.str = val;
00408                 he->c = 1;
00409                 xx = headerPut(h, he, 0);
00410         }
00411         val = _free(val);
00412     }
00413 }
00414 
00417 static int doIcon(Spec spec, Header h)
00418         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
00419         /*@modifies h, rpmGlobalMacroContext, fileSystem, internalState  @*/
00420 {
00421     static size_t iconsize = 0;
00422     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
00423     const char *fn, *Lurlfn = NULL;
00424     struct Source *sp;
00425     size_t nb;
00426     rpmuint8_t * icon;
00427     FD_t fd = NULL;
00428     int rc = RPMRC_FAIL;        /* assume error */
00429     int urltype;
00430     int xx;
00431 
00432     if (iconsize == 0) {
00433         iconsize = rpmExpandNumeric("%{?_build_iconsize}");
00434         if (iconsize < 2048)
00435             iconsize = 2048;
00436     }
00437     icon = alloca(iconsize+1);
00438 
00439     for (sp = spec->sources; sp != NULL; sp = sp->next) {
00440         if (sp->flags & RPMFILE_ICON)
00441             break;
00442     }
00443     if (sp == NULL) {
00444         rpmlog(RPMLOG_ERR, _("No icon file in sources\n"));
00445         goto exit;
00446     }
00447 
00448 #if defined(RPM_VENDOR_OPENPKG) /* splitted-source-directory */
00449     /* support splitted source directories, i.e., source files which
00450        are alternatively placed into the .spec directory and picked
00451        up from there, too. */
00452     Lurlfn = rpmGenPath(NULL, "%{_specdir}/", sp->source);
00453     if (access(Lurlfn, F_OK) == -1) {
00454         Lurlfn = _free(Lurlfn);
00455         Lurlfn = rpmGenPath(NULL, "%{_icondir}/", sp->source);
00456     }
00457 #else
00458     Lurlfn = rpmGenPath(NULL, "%{_icondir}/", sp->source);
00459 #endif
00460 
00461     fn = NULL;
00462     urltype = urlPath(Lurlfn, &fn);
00463     switch (urltype) {  
00464     case URL_IS_HTTPS: 
00465     case URL_IS_HTTP:
00466     case URL_IS_FTP:
00467     case URL_IS_PATH:
00468     case URL_IS_UNKNOWN:
00469         break;
00470     case URL_IS_DASH:
00471     case URL_IS_HKP:
00472     case URL_IS_MONGO:  /* XXX FIXME */
00473         rpmlog(RPMLOG_ERR, _("Invalid icon URL: %s\n"), Lurlfn);
00474         goto exit;
00475         /*@notreached@*/ break;
00476     }
00477 
00478     fd = Fopen(fn, "r%{?_rpmgio}");
00479     if (fd == NULL || Ferror(fd)) {
00480         rpmlog(RPMLOG_ERR, _("Unable to open icon %s: %s\n"),
00481                 fn, Fstrerror(fd));
00482         rc = RPMRC_FAIL;
00483         goto exit;
00484     }
00485 
00486     *icon = '\0';
00487     nb = Fread(icon, sizeof(icon[0]), iconsize, fd);
00488     if (Ferror(fd) || nb == 0) {
00489         rpmlog(RPMLOG_ERR, _("Unable to read icon %s: %s\n"),
00490                 fn, Fstrerror(fd));
00491         goto exit;
00492     }
00493     if (nb >= iconsize) {
00494         rpmlog(RPMLOG_ERR, _("Icon %s is too big (max. %d bytes)\n"),
00495                 fn, (int)iconsize);
00496         goto exit;
00497     }
00498 
00499     if (icon[0] == 'G' && icon[1] == 'I' && icon[2] == 'F')
00500         he->tag = RPMTAG_GIF;
00501     else
00502     if (icon[0] == '/' && icon[1] == '*' && icon[2] == ' '
00503      && icon[3] == 'X' && icon[4] == 'P' && icon[5] == 'M')
00504         he->tag = RPMTAG_XPM;
00505     else
00506         he->tag = tagValue("Icon");
00507     he->t = RPM_BIN_TYPE;
00508     he->p.ui8p = icon;
00509     he->c = (rpmTagCount)nb;
00510     xx = headerPut(h, he, 0);
00511     rc = 0;
00512     
00513 exit:
00514     if (fd) {
00515         (void) Fclose(fd);
00516         fd = NULL;
00517     }
00518     Lurlfn = _free(Lurlfn);
00519     return rc;
00520 }
00521 
00522 spectag stashSt(Spec spec, Header h, rpmTag tag, const char * lang)
00523 {
00524     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
00525     spectag t = NULL;
00526     int xx;
00527 
00528     if (spec->st) {
00529         spectags st = spec->st;
00530         if (st->st_ntags == st->st_nalloc) {
00531             st->st_nalloc += 10;
00532             st->st_t = xrealloc(st->st_t, st->st_nalloc * sizeof(*(st->st_t)));
00533         }
00534         t = st->st_t + st->st_ntags++;
00535         t->t_tag = tag;
00536         t->t_startx = spec->lineNum - 1;
00537         t->t_nlines = 1;
00538         t->t_lang = xstrdup(lang);
00539         t->t_msgid = NULL;
00540         if (!(t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG))) {
00541             he->tag = RPMTAG_NAME;
00542             xx = headerGet(h, he, 0);
00543             if (xx) {
00544                 char buf[1024];
00545                 sprintf(buf, "%s(%s)", he->p.str, tagName(tag));
00546                 t->t_msgid = xstrdup(buf);
00547             }
00548             he->p.ptr = _free(he->p.ptr);
00549         }
00550     }
00551     /*@-usereleased -compdef@*/
00552     return t;
00553     /*@=usereleased =compdef@*/
00554 }
00555 
00556 #define SINGLE_TOKEN_ONLY \
00557 if (multiToken) { \
00558     rpmlog(RPMLOG_ERR, _("line %d: Tag takes single token only: %s\n"), \
00559              spec->lineNum, spec->line); \
00560     return RPMRC_FAIL; \
00561 }
00562 
00563 /*@-redecl@*/
00564 extern int noLang;
00565 /*@=redecl@*/
00566 
00567 static rpmRC tagValidate(Spec spec, rpmTag tag, const char * value)
00568         /*@*/
00569 {
00570     const char * tagN = tagName(tag);
00571     const char * pattern = rpmExpand("%{?pattern_", tagN, "}", NULL);
00572     rpmRC ec = RPMRC_OK;
00573 
00574     if (pattern && *pattern) {
00575         miRE mire;
00576         int xx;
00577 
00578         mire = mireNew(RPMMIRE_REGEX, tag);
00579         xx = mireSetCOptions(mire, RPMMIRE_REGEX, 0, 0, NULL);
00580         if (!xx)
00581             xx = mireRegcomp(mire, pattern);
00582         if (!xx)
00583             xx = mireRegexec(mire, value, strlen(value));
00584         if (!xx)
00585             ec = RPMRC_OK;
00586         else {
00587             rpmlog(RPMLOG_ERR, _("line %d: invalid tag value(\"%s\") %s: %s\n"),
00588                     spec->lineNum, pattern, tagN, spec->line);
00589             ec = RPMRC_FAIL;
00590         }
00591 
00592         mire = mireFree(mire);
00593     }
00594 
00595     pattern = _free(pattern);
00596 
00597     return ec;
00598 }
00599 
00602 static rpmRC handlePreambleTag(Spec spec, Package pkg, rpmTag tag,
00603                 const char *macro, const char *lang)
00604         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
00605         /*@modifies spec->macros, spec->st,
00606                 spec->sources, spec->numSources, spec->noSource,
00607                 spec->sourceHeader, spec->BANames, spec->BACount,
00608                 spec->line,
00609                 pkg->header, pkg->autoProv, pkg->autoReq, pkg->noarch,
00610                 rpmGlobalMacroContext, fileSystem, internalState @*/
00611 {
00612     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
00613     char * field = spec->line;
00614     char * end;
00615     int multiToken = 0;
00616     rpmsenseFlags tagflags;
00617     int len;
00618     rpmuint32_t num;
00619     int rc;
00620     int xx;
00621     
00622     if (field == NULL) return RPMRC_FAIL;       /* XXX can't happen */
00623     /* Find the start of the "field" and strip trailing space */
00624     while ((*field) && (*field != ':'))
00625         field++;
00626     if (*field != ':') {
00627         rpmlog(RPMLOG_ERR, _("line %d: Malformed tag: %s\n"),
00628                  spec->lineNum, spec->line);
00629         return RPMRC_FAIL;
00630     }
00631     field++;
00632     SKIPSPACE(field);
00633     if (!*field) {
00634         /* Empty field */
00635         rpmlog(RPMLOG_ERR, _("line %d: Empty tag: %s\n"),
00636                  spec->lineNum, spec->line);
00637         return RPMRC_FAIL;
00638     }
00639     end = findLastChar(field);
00640 
00641     /* Validate tag data content. */
00642     if (tagValidate(spec, tag, field) != RPMRC_OK)
00643         return RPMRC_FAIL;
00644 
00645     /* See if this is multi-token */
00646     end = field;
00647     SKIPNONSPACE(end);
00648     if (*end != '\0')
00649         multiToken = 1;
00650 
00651     switch (tag) {
00652     case RPMTAG_NAME:
00653     case RPMTAG_VERSION:
00654     case RPMTAG_RELEASE:
00655     case RPMTAG_DISTEPOCH:
00656     case RPMTAG_URL:
00657     case RPMTAG_DISTTAG:
00658     case RPMTAG_REPOTAG:
00659     case RPMTAG_CVSID:
00660     case RPMTAG_BUGURL:
00661         SINGLE_TOKEN_ONLY;
00662         /* These macros are for backward compatibility */
00663         if (tag == RPMTAG_VERSION) {
00664             if (strchr(field, '-') != NULL) {
00665                 rpmlog(RPMLOG_ERR, _("line %d: Illegal char '-' in %s: %s\n"),
00666                     spec->lineNum, "version", spec->line);
00667                 return RPMRC_FAIL;
00668             }
00669             addMacro(spec->macros, "PACKAGE_VERSION", NULL, field, RMIL_OLDSPEC);
00670         } else if (tag == RPMTAG_RELEASE) {
00671             if (strchr(field, '-') != NULL) {
00672                 rpmlog(RPMLOG_ERR, _("line %d: Illegal char '-' in %s: %s\n"),
00673                     spec->lineNum, "release", spec->line);
00674                 return RPMRC_FAIL;
00675             }
00676             addMacro(spec->macros, "PACKAGE_RELEASE", NULL, field, RMIL_OLDSPEC-1);
00677         }
00678         he->tag = tag;
00679         he->t = RPM_STRING_TYPE;
00680         he->p.str = field;
00681         he->c = 1;
00682         xx = headerPut(pkg->header, he, 0);
00683         break;
00684     case RPMTAG_GROUP:
00685     case RPMTAG_SUMMARY:
00686 #if defined(RPM_VENDOR_OPENPKG) /* make-class-available-as-macro */
00687     case RPMTAG_CLASS:
00688 #endif
00689         (void) stashSt(spec, pkg->header, tag, lang);
00690         /*@fallthrough@*/
00691     case RPMTAG_DISTRIBUTION:
00692     case RPMTAG_VENDOR:
00693     case RPMTAG_LICENSE:
00694     case RPMTAG_PACKAGER:
00695         if (!*lang) {
00696             he->tag = tag;
00697             he->t = RPM_STRING_TYPE;
00698             he->p.str = field;
00699             he->c = 1;
00700             xx = headerPut(pkg->header, he, 0);
00701         } else if (!(noLang && strcmp(lang, RPMBUILD_DEFAULT_LANG))) {
00702             (void) headerAddI18NString(pkg->header, tag, field, lang);
00703         }
00704         break;
00705     /* XXX silently ignore BuildRoot: */
00706     case RPMTAG_BUILDROOT:
00707         SINGLE_TOKEN_ONLY;
00708         macro = NULL;
00709 #ifdef  DYING
00710         buildRootURL = rpmGenPath(spec->rootURL, "%{?buildroot}", NULL);
00711         (void) urlPath(buildRootURL, &buildRoot);
00712         if (*buildRoot == '\0') buildRoot = "/";
00713         if (!strcmp(buildRoot, "/")) {
00714             rpmlog(RPMLOG_ERR,
00715                      _("BuildRoot can not be \"/\": %s\n"), spec->buildRootURL);
00716             buildRootURL = _free(buildRootURL);
00717             return RPMRC_FAIL;
00718         }
00719         buildRootURL = _free(buildRootURL);
00720 #endif
00721         break;
00722     case RPMTAG_KEYWORDS:
00723     case RPMTAG_VARIANTS:
00724     case RPMTAG_PREFIXES:
00725         addOrAppendListEntry(pkg->header, tag, field);
00726         he->tag = tag;
00727         xx = headerGet(pkg->header, he, 0);
00728         if (tag == RPMTAG_PREFIXES)
00729         while (he->c--) {
00730             if (he->p.argv[he->c][0] != '/') {
00731                 rpmlog(RPMLOG_ERR,
00732                          _("line %d: Prefixes must begin with \"/\": %s\n"),
00733                          spec->lineNum, spec->line);
00734                 he->p.ptr = _free(he->p.ptr);
00735                 return RPMRC_FAIL;
00736             }
00737             len = (int)strlen(he->p.argv[he->c]);
00738             if (he->p.argv[he->c][len - 1] == '/' && len > 1) {
00739                 rpmlog(RPMLOG_ERR,
00740                          _("line %d: Prefixes must not end with \"/\": %s\n"),
00741                          spec->lineNum, spec->line);
00742                 he->p.ptr = _free(he->p.ptr);
00743                 return RPMRC_FAIL;
00744             }
00745         }
00746         he->p.ptr = _free(he->p.ptr);
00747         break;
00748     case RPMTAG_DOCDIR:
00749         SINGLE_TOKEN_ONLY;
00750         if (field[0] != '/') {
00751             rpmlog(RPMLOG_ERR,
00752                      _("line %d: Docdir must begin with '/': %s\n"),
00753                      spec->lineNum, spec->line);
00754             return RPMRC_FAIL;
00755         }
00756         macro = NULL;
00757         delMacro(NULL, "_docdir");
00758         addMacro(NULL, "_docdir", NULL, field, RMIL_SPEC);
00759         break;
00760     case RPMTAG_XMAJOR:
00761     case RPMTAG_XMINOR:
00762     case RPMTAG_EPOCH:
00763         SINGLE_TOKEN_ONLY;
00764         if (parseNum(field, &num)) {
00765             rpmlog(RPMLOG_ERR,
00766                      _("line %d: %s takes an integer value: %s\n"),
00767                      spec->lineNum, tagName(tag), spec->line);
00768             return RPMRC_FAIL;
00769         }
00770         he->tag = tag;
00771         he->t = RPM_UINT32_TYPE;
00772         he->p.ui32p = &num;
00773         he->c = 1;
00774         xx = headerPut(pkg->header, he, 0);
00775         break;
00776     case RPMTAG_AUTOREQPROV:
00777         pkg->autoReq = parseYesNo(field);
00778         pkg->autoProv = pkg->autoReq;
00779         break;
00780     case RPMTAG_AUTOREQ:
00781         pkg->autoReq = parseYesNo(field);
00782         break;
00783     case RPMTAG_AUTOPROV:
00784         pkg->autoProv = parseYesNo(field);
00785         break;
00786     case RPMTAG_SOURCE:
00787     case RPMTAG_PATCH:
00788         SINGLE_TOKEN_ONLY;
00789         macro = NULL;
00790         if ((rc = addSource(spec, pkg, field, tag)))
00791             return rc;
00792         break;
00793     case RPMTAG_ICON:
00794         SINGLE_TOKEN_ONLY;
00795         macro = NULL;
00796         if ((rc = addSource(spec, pkg, field, tag)))
00797             return rc;
00798         /* XXX the fetch/load of icon needs to be elsewhere. */
00799         if ((rc = doIcon(spec, pkg->header)))
00800             return rc;
00801         break;
00802     case RPMTAG_NOSOURCE:
00803     case RPMTAG_NOPATCH:
00804         spec->noSource = 1;
00805         if ((rc = parseNoSource(spec, field, tag)))
00806             return rc;
00807         break;
00808     case RPMTAG_BUILDPREREQ:
00809     case RPMTAG_BUILDREQUIRES:
00810         if ((rc = parseBits(lang, buildScriptBits, &tagflags))) {
00811             rpmlog(RPMLOG_ERR,
00812                      _("line %d: Bad %s: qualifiers: %s\n"),
00813                      spec->lineNum, tagName(tag), spec->line);
00814             return rc;
00815         }
00816         if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
00817             return rc;
00818         break;
00819     case RPMTAG_PREREQ:
00820     case RPMTAG_REQUIREFLAGS:
00821         if ((rc = parseBits(lang, installScriptBits, &tagflags))) {
00822             rpmlog(RPMLOG_ERR,
00823                      _("line %d: Bad %s: qualifiers: %s\n"),
00824                      spec->lineNum, tagName(tag), spec->line);
00825             return rc;
00826         }
00827         if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
00828             return rc;
00829         break;
00830     /* Aliases for BuildRequires(hint): */
00831     case RPMTAG_BUILDSUGGESTS:
00832     case RPMTAG_BUILDENHANCES:
00833         tagflags = RPMSENSE_MISSINGOK;
00834         if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
00835             return rc;
00836         break;
00837     /* Aliases for Requires(hint): */
00838     case RPMTAG_SUGGESTSFLAGS:
00839     case RPMTAG_ENHANCESFLAGS:
00840         tag = RPMTAG_REQUIREFLAGS;
00841         tagflags = RPMSENSE_MISSINGOK;
00842         if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
00843             return rc;
00844         break;
00845     case RPMTAG_BUILDOBSOLETES:
00846     case RPMTAG_BUILDPROVIDES:
00847     case RPMTAG_BUILDCONFLICTS:
00848     case RPMTAG_CONFLICTFLAGS:
00849     case RPMTAG_OBSOLETEFLAGS:
00850     case RPMTAG_PROVIDEFLAGS:
00851         tagflags = RPMSENSE_ANY;
00852         if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
00853             return rc;
00854         break;
00855     case RPMTAG_BUILDPLATFORMS:         /* XXX needs pattern parsing */
00856     case RPMTAG_EXCLUDEARCH:
00857     case RPMTAG_EXCLUSIVEARCH:
00858     case RPMTAG_EXCLUDEOS:
00859     case RPMTAG_EXCLUSIVEOS:
00860         addOrAppendListEntry(spec->sourceHeader, tag, field);
00861         break;
00862 
00863     case RPMTAG_BUILDARCHS:
00864     {   const char ** BANames = NULL;
00865         int BACount = 0;
00866         if ((rc = poptParseArgvString(field, &BACount, &BANames))) {
00867             rpmlog(RPMLOG_ERR,
00868                      _("line %d: Bad BuildArchitecture format: %s\n"),
00869                      spec->lineNum, spec->line);
00870             return RPMRC_FAIL;
00871         }
00872         if (spec->toplevel) {
00873             if (BACount > 0 && BANames != NULL) {
00874                 spec->BACount = BACount;
00875                 spec->BANames = BANames;
00876                 BANames = NULL;         /* XXX don't free. */
00877             }
00878         } else {
00879             if (BACount != 1 || strcmp(BANames[0], "noarch")) {
00880                 rpmlog(RPMLOG_ERR,
00881                      _("line %d: Only \"noarch\" sub-packages are supported: %s\n"),
00882                      spec->lineNum, spec->line);
00883                 BANames = _free(BANames);
00884                 return RPMRC_FAIL;
00885             }
00886             pkg->noarch = 1;
00887         }
00888         BANames = _free(BANames);
00889     }   break;
00890 
00891     default:
00892         macro = NULL;
00893         he->tag = tag;
00894         he->t = RPM_STRING_ARRAY_TYPE;
00895         he->p.argv= (const char **) &field;     /* XXX NOCAST */
00896         he->c = 1;
00897         he->append = 1;
00898         xx = headerPut(pkg->header, he, 0);
00899         he->append = 0;
00900         break;
00901     }
00902 
00903 /*@-usereleased@*/
00904     if (macro)
00905         addMacro(spec->macros, macro, NULL, field, RMIL_SPEC);
00906 /*@=usereleased@*/
00907     
00908     return RPMRC_OK;
00909 }
00910 
00911 /* This table has to be in a peculiar order.  If one tag is the */
00912 /* same as another, plus a few letters, it must come first.     */
00913 
00916 typedef struct PreambleRec_s {
00917     rpmTag tag;
00918     int multiLang;
00919     int obsolete;
00920 /*@observer@*/ /*@null@*/
00921     const char * token;
00922 } * PreambleRec;
00923 
00924 /*@unchecked@*/
00925 static struct PreambleRec_s preambleList[] = {
00926     {RPMTAG_NAME,               0, 0, "name"},
00927     {RPMTAG_VERSION,            0, 0, "version"},
00928     {RPMTAG_RELEASE,            0, 0, "release"},
00929     {RPMTAG_DISTEPOCH,          0, 0, "distepoch"},
00930     {RPMTAG_EPOCH,              0, 0, "epoch"},
00931     {RPMTAG_EPOCH,              0, 1, "serial"},
00932     {RPMTAG_SUMMARY,            1, 0, "summary"},
00933     {RPMTAG_LICENSE,            0, 0, "copyright"},
00934     {RPMTAG_LICENSE,            0, 0, "license"},
00935     {RPMTAG_DISTRIBUTION,       0, 0, "distribution"},
00936     {RPMTAG_DISTURL,            0, 0, "disturl"},
00937     {RPMTAG_VENDOR,             0, 0, "vendor"},
00938     {RPMTAG_GROUP,              1, 0, "group"},
00939     {RPMTAG_PACKAGER,           0, 0, "packager"},
00940     {RPMTAG_URL,                0, 0, "url"},
00941     {RPMTAG_SOURCE,             0, 0, "source"},
00942     {RPMTAG_PATCH,              0, 0, "patch"},
00943     {RPMTAG_NOSOURCE,           0, 0, "nosource"},
00944     {RPMTAG_NOPATCH,            0, 0, "nopatch"},
00945     {RPMTAG_EXCLUDEARCH,        0, 0, "excludearch"},
00946     {RPMTAG_EXCLUSIVEARCH,      0, 0, "exclusivearch"},
00947     {RPMTAG_EXCLUDEOS,          0, 0, "excludeos"},
00948     {RPMTAG_EXCLUSIVEOS,        0, 0, "exclusiveos"},
00949     {RPMTAG_ICON,               0, 0, "icon"},
00950     {RPMTAG_PROVIDEFLAGS,       0, 0, "provides"},
00951     {RPMTAG_REQUIREFLAGS,       1, 0, "requires"},
00952     {RPMTAG_PREREQ,             1, 0, "prereq"},
00953     {RPMTAG_CONFLICTFLAGS,      0, 0, "conflicts"},
00954     {RPMTAG_OBSOLETEFLAGS,      0, 0, "obsoletes"},
00955     {RPMTAG_PREFIXES,           0, 0, "prefixes"},
00956     {RPMTAG_PREFIXES,           0, 0, "prefix"},
00957     {RPMTAG_BUILDROOT,          0, 0, "buildroot"},
00958     {RPMTAG_BUILDARCHS,         0, 0, "buildarchitectures"},
00959     {RPMTAG_BUILDARCHS,         0, 0, "buildarch"},
00960     {RPMTAG_BUILDCONFLICTS,     0, 0, "buildconflicts"},
00961     {RPMTAG_BUILDOBSOLETES,     0, 0, "buildobsoletes"},
00962     {RPMTAG_BUILDPREREQ,        1, 0, "buildprereq"},
00963     {RPMTAG_BUILDPROVIDES,      0, 0, "buildprovides"},
00964     {RPMTAG_BUILDREQUIRES,      1, 0, "buildrequires"},
00965     {RPMTAG_AUTOREQPROV,        0, 0, "autoreqprov"},
00966     {RPMTAG_AUTOREQ,            0, 0, "autoreq"},
00967     {RPMTAG_AUTOPROV,           0, 0, "autoprov"},
00968     {RPMTAG_DOCDIR,             0, 0, "docdir"},
00969     {RPMTAG_DISTTAG,            0, 0, "disttag"},
00970     {RPMTAG_BUGURL,             0, 0, "bugurl"},
00971     {RPMTAG_CVSID,              0, 0, "cvsid"},
00972     {RPMTAG_SVNID,              0, 0, "svnid"},
00973     {RPMTAG_SUGGESTSFLAGS,      0, 0, "suggests"},
00974     {RPMTAG_ENHANCESFLAGS,      0, 0, "enhances"},
00975     {RPMTAG_BUILDSUGGESTS,      0, 0, "buildsuggests"},
00976     {RPMTAG_BUILDENHANCES,      0, 0, "buildenhances"},
00977     {RPMTAG_VARIANTS,           0, 0, "variants"},
00978     {RPMTAG_VARIANTS,           0, 0, "variant"},
00979     {RPMTAG_XMAJOR,             0, 0, "xmajor"},
00980     {RPMTAG_XMINOR,             0, 0, "xminor"},
00981     {RPMTAG_REPOTAG,            0, 0, "repotag"},
00982     {RPMTAG_KEYWORDS,           0, 0, "keywords"},
00983     {RPMTAG_KEYWORDS,           0, 0, "keyword"},
00984     {RPMTAG_BUILDPLATFORMS,     0, 0, "buildplatforms"},
00985 #if defined(RPM_VENDOR_OPENPKG) /* make-class-available-as-macro */
00986     {RPMTAG_CLASS,              0, 0, "class"},
00987 #endif
00988     /*@-nullassign@*/   /* LCL: can't add null annotation */
00989     {0, 0, 0, 0}
00990     /*@=nullassign@*/
00991 };
00992 
00995 static int findPreambleTag(Spec spec, /*@out@*/rpmTag * tagp,
00996                 /*@null@*/ /*@out@*/ const char ** macro, /*@out@*/ char * lang)
00997         /*@modifies *tagp, *macro, *lang @*/
00998 {
00999     PreambleRec p;
01000     char *s;
01001     size_t len = 0;
01002 
01003     /* Search for defined tags. */
01004     for (p = preambleList; p->token != NULL; p++) {
01005         len = strlen(p->token);
01006         if (!(p->token && !xstrncasecmp(spec->line, p->token, len)))
01007             continue;
01008         if (p->obsolete) {
01009             rpmlog(RPMLOG_ERR, _("Legacy syntax is unsupported: %s\n"),
01010                         p->token);
01011             p = NULL;
01012         }
01013         break;
01014     }
01015     if (p == NULL)
01016         return 1;
01017 
01018     /* Search for arbitrary tags. */
01019     if (tagp && p->token == NULL) {
01020         ARGV_t aTags = NULL;
01021         int rc = 1;     /* assume failure */
01022 
01023 /*@-noeffect@*/
01024         (void) tagName(0); /* XXX force arbitrary tags to be initialized. */
01025 /*@=noeffect@*/
01026         aTags = rpmTags->aTags;
01027         if (aTags != NULL && aTags[0] != NULL) {
01028             ARGV_t av;
01029             s = tagCanonicalize(spec->line);
01030 #if defined(RPM_VENDOR_OPENPKG) /* wildcard-matching-arbitrary-tagnames */
01031             av = argvSearchLinear(aTags, s, argvFnmatchCasefold);
01032 #else
01033             av = argvSearch(aTags, s, argvStrcasecmp);
01034 #endif
01035             if (av != NULL) {
01036                 *tagp = tagGenerate(s);
01037                 rc = 0;
01038             }
01039             s = _free(s);
01040         }
01041         return rc;
01042     }
01043 
01044     s = spec->line + len;
01045     SKIPSPACE(s);
01046 
01047     switch (p->multiLang) {
01048     default:
01049     case 0:
01050         /* Unless this is a source or a patch, a ':' better be next */
01051         if (p->tag != RPMTAG_SOURCE && p->tag != RPMTAG_PATCH) {
01052             if (*s != ':') return 1;
01053         }
01054         *lang = '\0';
01055         break;
01056     case 1:     /* Parse optional ( <token> ). */
01057         if (*s == ':') {
01058             strcpy(lang, RPMBUILD_DEFAULT_LANG);
01059             break;
01060         }
01061         if (*s != '(') return 1;
01062         s++;
01063         SKIPSPACE(s);
01064         while (!xisspace(*s) && *s != ')')
01065             *lang++ = *s++;
01066         *lang = '\0';
01067         SKIPSPACE(s);
01068         if (*s != ')') return 1;
01069         s++;
01070         SKIPSPACE(s);
01071         if (*s != ':') return 1;
01072         break;
01073     }
01074 
01075     if (tagp)
01076         *tagp = p->tag;
01077     if (macro)
01078         /*@-onlytrans -observertrans -dependenttrans@*/ /* FIX: double indirection. */
01079         *macro = p->token;
01080         /*@=onlytrans =observertrans =dependenttrans@*/
01081     return 0;
01082 }
01083 
01084 /* XXX should return rpmParseState, but RPMRC_FAIL forces int return. */
01085 int parsePreamble(Spec spec, int initialPackage)
01086 {
01087     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
01088     rpmParseState nextPart;
01089     int xx;
01090     char *linep;
01091     Package pkg;
01092     char NVR[BUFSIZ];
01093     char lang[BUFSIZ];
01094     rpmRC rc;
01095 
01096     strcpy(NVR, "(main package)");
01097 
01098     pkg = newPackage(spec);
01099     if (spec->packages == NULL) {
01100         spec->packages = pkg;
01101 assert(initialPackage);
01102     } else if (! initialPackage) {
01103         char *name = NULL;
01104         rpmParseState flag;
01105         Package lastpkg;
01106 
01107         /* There is one option to %package: <pkg> or -n <pkg> */
01108         flag = PART_NONE;
01109         if (parseSimplePart(spec, &name, &flag)) {
01110             rpmlog(RPMLOG_ERR, _("Bad package specification: %s\n"),
01111                         spec->line);
01112             pkg = freePackages(pkg);
01113             return RPMRC_FAIL;
01114         }
01115         
01116         lastpkg = NULL;
01117         if (lookupPackage(spec, name, flag, &lastpkg) == RPMRC_OK) {
01118             pkg->next = lastpkg->next;
01119         } else {
01120             /* Add package to end of list */
01121             for (lastpkg = spec->packages; lastpkg->next != NULL; lastpkg = lastpkg->next)
01122             {};
01123         }
01124 assert(lastpkg != NULL);
01125         lastpkg->next = pkg;
01126         
01127         /* Construct the package */
01128         if (flag == PART_SUBNAME) {
01129             he->tag = RPMTAG_NAME;
01130             xx = headerGet(spec->packages->header, he, 0);
01131             sprintf(NVR, "%s-%s", he->p.str, name);
01132             he->p.ptr = _free(he->p.ptr);
01133         } else
01134             strcpy(NVR, name);
01135         name = _free(name);
01136         he->tag = RPMTAG_NAME;
01137         he->t = RPM_STRING_TYPE;
01138         he->p.str = NVR;
01139         he->c = 1;
01140         xx = headerPut(pkg->header, he, 0);
01141     }
01142 
01143     if ((rc = readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
01144         nextPart = PART_NONE;
01145     } else {
01146         if (rc)
01147             return rc;
01148         while ((nextPart = isPart(spec)) == PART_NONE) {
01149             const char * macro = NULL;
01150             rpmTag tag = 0;
01151 
01152             /* Skip blank lines */
01153             linep = spec->line;
01154             SKIPSPACE(linep);
01155             if (*linep != '\0') {
01156                 if (findPreambleTag(spec, &tag, &macro, lang)) {
01157                     rpmlog(RPMLOG_ERR, _("line %d: Unknown tag: %s\n"),
01158                                 spec->lineNum, spec->line);
01159                     return RPMRC_FAIL;
01160                 }
01161                 if (handlePreambleTag(spec, pkg, tag, macro, lang))
01162                     return RPMRC_FAIL;
01163                 if (spec->BANames && !spec->recursing && spec->toplevel)
01164                     return PART_BUILDARCHITECTURES;
01165             }
01166             if ((rc =
01167                  readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
01168                 nextPart = PART_NONE;
01169                 break;
01170             }
01171             if (rc)
01172                 return rc;
01173         }
01174     }
01175 
01176     /* Do some final processing on the header */
01177 
01178     /* 
01179      * Expand buildroot one more time to get %{version} and the like
01180      * from the main package.
01181      */
01182     if (initialPackage) {
01183         const char *s = rpmExpand("%{?buildroot}", NULL);
01184         if (s && *s) 
01185             (void) addMacro(NULL, "buildroot", NULL, s, -1);
01186         s = _free(s);
01187     }
01188     
01189     /* XXX Skip valid arch check if not building binary package */
01190     if (!spec->anyarch && checkForValidArchitectures(spec))
01191         return RPMRC_FAIL;
01192 
01193     if (pkg == spec->packages)
01194         fillOutMainPackage(pkg->header);
01195 
01196     if (checkForDuplicates(pkg->header, NVR) != RPMRC_OK)
01197         return RPMRC_FAIL;
01198 
01199     if (pkg != spec->packages)
01200         headerCopyTags(spec->packages->header, pkg->header,
01201                         (void *)copyTagsDuringParse);
01202 
01203 #ifdef RPM_VENDOR_PLD /* rpm-epoch0 */
01204     /* Add Epoch: 0 to package header if it was not set by spec */
01205     he->tag = RPMTAG_NAME;
01206     if (headerGet(spec->packages->header, he, 0) == 0) {
01207         rpmuint32_t num = 0;
01208 
01209         he->tag = RPMTAG_EPOCH;
01210         he->t = RPM_UINT32_TYPE;
01211         he->p.ui32p = &num;
01212         he->c = 1;
01213         xx = headerPut(pkg->header, he, 0);
01214 
01215         /* also declare %{epoch} to be same */
01216         addMacro(spec->macros, "epoch", NULL, "0", RMIL_SPEC);
01217     }
01218 #endif /* RPM_VENDOR_PLD rpm-epoch0 */
01219 
01220     if (checkForRequired(pkg->header, NVR) != RPMRC_OK)
01221         return RPMRC_FAIL;
01222 
01223     return nextPart;
01224 }