rpm 5.3.7

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         rpmlog(RPMLOG_ERR, _("Invalid icon URL: %s\n"), Lurlfn);
00473         goto exit;
00474         /*@notreached@*/ break;
00475     }
00476 
00477     fd = Fopen(fn, "r%{?_rpmgio}");
00478     if (fd == NULL || Ferror(fd)) {
00479         rpmlog(RPMLOG_ERR, _("Unable to open icon %s: %s\n"),
00480                 fn, Fstrerror(fd));
00481         rc = RPMRC_FAIL;
00482         goto exit;
00483     }
00484 
00485     *icon = '\0';
00486     nb = Fread(icon, sizeof(icon[0]), iconsize, fd);
00487     if (Ferror(fd) || nb == 0) {
00488         rpmlog(RPMLOG_ERR, _("Unable to read icon %s: %s\n"),
00489                 fn, Fstrerror(fd));
00490         goto exit;
00491     }
00492     if (nb >= iconsize) {
00493         rpmlog(RPMLOG_ERR, _("Icon %s is too big (max. %d bytes)\n"),
00494                 fn, (int)iconsize);
00495         goto exit;
00496     }
00497 
00498     if (icon[0] == 'G' && icon[1] == 'I' && icon[2] == 'F')
00499         he->tag = RPMTAG_GIF;
00500     else
00501     if (icon[0] == '/' && icon[1] == '*' && icon[2] == ' '
00502      && icon[3] == 'X' && icon[4] == 'P' && icon[5] == 'M')
00503         he->tag = RPMTAG_XPM;
00504     else
00505         he->tag = tagValue("Icon");
00506     he->t = RPM_BIN_TYPE;
00507     he->p.ui8p = icon;
00508     he->c = (rpmTagCount)nb;
00509     xx = headerPut(h, he, 0);
00510     rc = 0;
00511     
00512 exit:
00513     if (fd) {
00514         (void) Fclose(fd);
00515         fd = NULL;
00516     }
00517     Lurlfn = _free(Lurlfn);
00518     return rc;
00519 }
00520 
00521 spectag stashSt(Spec spec, Header h, rpmTag tag, const char * lang)
00522 {
00523     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
00524     spectag t = NULL;
00525     int xx;
00526 
00527     if (spec->st) {
00528         spectags st = spec->st;
00529         if (st->st_ntags == st->st_nalloc) {
00530             st->st_nalloc += 10;
00531             st->st_t = xrealloc(st->st_t, st->st_nalloc * sizeof(*(st->st_t)));
00532         }
00533         t = st->st_t + st->st_ntags++;
00534         t->t_tag = tag;
00535         t->t_startx = spec->lineNum - 1;
00536         t->t_nlines = 1;
00537         t->t_lang = xstrdup(lang);
00538         t->t_msgid = NULL;
00539         if (!(t->t_lang && strcmp(t->t_lang, RPMBUILD_DEFAULT_LANG))) {
00540             he->tag = RPMTAG_NAME;
00541             xx = headerGet(h, he, 0);
00542             if (xx) {
00543                 char buf[1024];
00544                 sprintf(buf, "%s(%s)", he->p.str, tagName(tag));
00545                 t->t_msgid = xstrdup(buf);
00546             }
00547             he->p.ptr = _free(he->p.ptr);
00548         }
00549     }
00550     /*@-usereleased -compdef@*/
00551     return t;
00552     /*@=usereleased =compdef@*/
00553 }
00554 
00555 #define SINGLE_TOKEN_ONLY \
00556 if (multiToken) { \
00557     rpmlog(RPMLOG_ERR, _("line %d: Tag takes single token only: %s\n"), \
00558              spec->lineNum, spec->line); \
00559     return RPMRC_FAIL; \
00560 }
00561 
00562 /*@-redecl@*/
00563 extern int noLang;
00564 /*@=redecl@*/
00565 
00566 static rpmRC tagValidate(Spec spec, rpmTag tag, const char * value)
00567         /*@*/
00568 {
00569     const char * tagN = tagName(tag);
00570     const char * pattern = rpmExpand("%{?pattern_", tagN, "}", NULL);
00571     rpmRC ec = RPMRC_OK;
00572 
00573     if (pattern && *pattern) {
00574         miRE mire;
00575         int xx;
00576 
00577         mire = mireNew(RPMMIRE_REGEX, tag);
00578         xx = mireSetCOptions(mire, RPMMIRE_REGEX, 0, 0, NULL);
00579         if (!xx)
00580             xx = mireRegcomp(mire, pattern);
00581         if (!xx)
00582             xx = mireRegexec(mire, value, strlen(value));
00583         if (!xx)
00584             ec = RPMRC_OK;
00585         else {
00586             rpmlog(RPMLOG_ERR, _("line %d: invalid tag value(\"%s\") %s: %s\n"),
00587                     spec->lineNum, pattern, tagN, spec->line);
00588             ec = RPMRC_FAIL;
00589         }
00590 
00591         mire = mireFree(mire);
00592     }
00593 
00594     pattern = _free(pattern);
00595 
00596     return ec;
00597 }
00598 
00601 static rpmRC handlePreambleTag(Spec spec, Package pkg, rpmTag tag,
00602                 const char *macro, const char *lang)
00603         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
00604         /*@modifies spec->macros, spec->st,
00605                 spec->sources, spec->numSources, spec->noSource,
00606                 spec->sourceHeader, spec->BANames, spec->BACount,
00607                 spec->line,
00608                 pkg->header, pkg->autoProv, pkg->autoReq, pkg->noarch,
00609                 rpmGlobalMacroContext, fileSystem, internalState @*/
00610 {
00611     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
00612     char * field = spec->line;
00613     char * end;
00614     int multiToken = 0;
00615     rpmsenseFlags tagflags;
00616     int len;
00617     rpmuint32_t num;
00618     int rc;
00619     int xx;
00620     
00621     if (field == NULL) return RPMRC_FAIL;       /* XXX can't happen */
00622     /* Find the start of the "field" and strip trailing space */
00623     while ((*field) && (*field != ':'))
00624         field++;
00625     if (*field != ':') {
00626         rpmlog(RPMLOG_ERR, _("line %d: Malformed tag: %s\n"),
00627                  spec->lineNum, spec->line);
00628         return RPMRC_FAIL;
00629     }
00630     field++;
00631     SKIPSPACE(field);
00632     if (!*field) {
00633         /* Empty field */
00634         rpmlog(RPMLOG_ERR, _("line %d: Empty tag: %s\n"),
00635                  spec->lineNum, spec->line);
00636         return RPMRC_FAIL;
00637     }
00638     end = findLastChar(field);
00639 
00640     /* Validate tag data content. */
00641     if (tagValidate(spec, tag, field) != RPMRC_OK)
00642         return RPMRC_FAIL;
00643 
00644     /* See if this is multi-token */
00645     end = field;
00646     SKIPNONSPACE(end);
00647     if (*end != '\0')
00648         multiToken = 1;
00649 
00650     switch (tag) {
00651     case RPMTAG_NAME:
00652     case RPMTAG_VERSION:
00653     case RPMTAG_RELEASE:
00654     case RPMTAG_DISTEPOCH:
00655     case RPMTAG_URL:
00656     case RPMTAG_DISTTAG:
00657     case RPMTAG_REPOTAG:
00658     case RPMTAG_CVSID:
00659     case RPMTAG_BUGURL:
00660         SINGLE_TOKEN_ONLY;
00661         /* These macros are for backward compatibility */
00662         if (tag == RPMTAG_VERSION) {
00663             if (strchr(field, '-') != NULL) {
00664                 rpmlog(RPMLOG_ERR, _("line %d: Illegal char '-' in %s: %s\n"),
00665                     spec->lineNum, "version", spec->line);
00666                 return RPMRC_FAIL;
00667             }
00668             addMacro(spec->macros, "PACKAGE_VERSION", NULL, field, RMIL_OLDSPEC);
00669         } else if (tag == RPMTAG_RELEASE) {
00670             if (strchr(field, '-') != NULL) {
00671                 rpmlog(RPMLOG_ERR, _("line %d: Illegal char '-' in %s: %s\n"),
00672                     spec->lineNum, "release", spec->line);
00673                 return RPMRC_FAIL;
00674             }
00675             addMacro(spec->macros, "PACKAGE_RELEASE", NULL, field, RMIL_OLDSPEC-1);
00676         }
00677         he->tag = tag;
00678         he->t = RPM_STRING_TYPE;
00679         he->p.str = field;
00680         he->c = 1;
00681         xx = headerPut(pkg->header, he, 0);
00682         break;
00683     case RPMTAG_GROUP:
00684     case RPMTAG_SUMMARY:
00685 #if defined(RPM_VENDOR_OPENPKG) /* make-class-available-as-macro */
00686     case RPMTAG_CLASS:
00687 #endif
00688         (void) stashSt(spec, pkg->header, tag, lang);
00689         /*@fallthrough@*/
00690     case RPMTAG_DISTRIBUTION:
00691     case RPMTAG_VENDOR:
00692     case RPMTAG_LICENSE:
00693     case RPMTAG_PACKAGER:
00694         if (!*lang) {
00695             he->tag = tag;
00696             he->t = RPM_STRING_TYPE;
00697             he->p.str = field;
00698             he->c = 1;
00699             xx = headerPut(pkg->header, he, 0);
00700         } else if (!(noLang && strcmp(lang, RPMBUILD_DEFAULT_LANG))) {
00701             (void) headerAddI18NString(pkg->header, tag, field, lang);
00702         }
00703         break;
00704     /* XXX silently ignore BuildRoot: */
00705     case RPMTAG_BUILDROOT:
00706         SINGLE_TOKEN_ONLY;
00707         macro = NULL;
00708 #ifdef  DYING
00709         buildRootURL = rpmGenPath(spec->rootURL, "%{?buildroot}", NULL);
00710         (void) urlPath(buildRootURL, &buildRoot);
00711         if (*buildRoot == '\0') buildRoot = "/";
00712         if (!strcmp(buildRoot, "/")) {
00713             rpmlog(RPMLOG_ERR,
00714                      _("BuildRoot can not be \"/\": %s\n"), spec->buildRootURL);
00715             buildRootURL = _free(buildRootURL);
00716             return RPMRC_FAIL;
00717         }
00718         buildRootURL = _free(buildRootURL);
00719 #endif
00720         break;
00721     case RPMTAG_KEYWORDS:
00722     case RPMTAG_VARIANTS:
00723     case RPMTAG_PREFIXES:
00724         addOrAppendListEntry(pkg->header, tag, field);
00725         he->tag = tag;
00726         xx = headerGet(pkg->header, he, 0);
00727         if (tag == RPMTAG_PREFIXES)
00728         while (he->c--) {
00729             if (he->p.argv[he->c][0] != '/') {
00730                 rpmlog(RPMLOG_ERR,
00731                          _("line %d: Prefixes must begin with \"/\": %s\n"),
00732                          spec->lineNum, spec->line);
00733                 he->p.ptr = _free(he->p.ptr);
00734                 return RPMRC_FAIL;
00735             }
00736             len = (int)strlen(he->p.argv[he->c]);
00737             if (he->p.argv[he->c][len - 1] == '/' && len > 1) {
00738                 rpmlog(RPMLOG_ERR,
00739                          _("line %d: Prefixes must not end with \"/\": %s\n"),
00740                          spec->lineNum, spec->line);
00741                 he->p.ptr = _free(he->p.ptr);
00742                 return RPMRC_FAIL;
00743             }
00744         }
00745         he->p.ptr = _free(he->p.ptr);
00746         break;
00747     case RPMTAG_DOCDIR:
00748         SINGLE_TOKEN_ONLY;
00749         if (field[0] != '/') {
00750             rpmlog(RPMLOG_ERR,
00751                      _("line %d: Docdir must begin with '/': %s\n"),
00752                      spec->lineNum, spec->line);
00753             return RPMRC_FAIL;
00754         }
00755         macro = NULL;
00756         delMacro(NULL, "_docdir");
00757         addMacro(NULL, "_docdir", NULL, field, RMIL_SPEC);
00758         break;
00759     case RPMTAG_XMAJOR:
00760     case RPMTAG_XMINOR:
00761     case RPMTAG_EPOCH:
00762         SINGLE_TOKEN_ONLY;
00763         if (parseNum(field, &num)) {
00764             rpmlog(RPMLOG_ERR,
00765                      _("line %d: %s takes an integer value: %s\n"),
00766                      spec->lineNum, tagName(tag), spec->line);
00767             return RPMRC_FAIL;
00768         }
00769         he->tag = tag;
00770         he->t = RPM_UINT32_TYPE;
00771         he->p.ui32p = &num;
00772         he->c = 1;
00773         xx = headerPut(pkg->header, he, 0);
00774         break;
00775     case RPMTAG_AUTOREQPROV:
00776         pkg->autoReq = parseYesNo(field);
00777         pkg->autoProv = pkg->autoReq;
00778         break;
00779     case RPMTAG_AUTOREQ:
00780         pkg->autoReq = parseYesNo(field);
00781         break;
00782     case RPMTAG_AUTOPROV:
00783         pkg->autoProv = parseYesNo(field);
00784         break;
00785     case RPMTAG_SOURCE:
00786     case RPMTAG_PATCH:
00787         SINGLE_TOKEN_ONLY;
00788         macro = NULL;
00789         if ((rc = addSource(spec, pkg, field, tag)))
00790             return rc;
00791         break;
00792     case RPMTAG_ICON:
00793         SINGLE_TOKEN_ONLY;
00794         macro = NULL;
00795         if ((rc = addSource(spec, pkg, field, tag)))
00796             return rc;
00797         /* XXX the fetch/load of icon needs to be elsewhere. */
00798         if ((rc = doIcon(spec, pkg->header)))
00799             return rc;
00800         break;
00801     case RPMTAG_NOSOURCE:
00802     case RPMTAG_NOPATCH:
00803         spec->noSource = 1;
00804         if ((rc = parseNoSource(spec, field, tag)))
00805             return rc;
00806         break;
00807     case RPMTAG_BUILDPREREQ:
00808     case RPMTAG_BUILDREQUIRES:
00809         if ((rc = parseBits(lang, buildScriptBits, &tagflags))) {
00810             rpmlog(RPMLOG_ERR,
00811                      _("line %d: Bad %s: qualifiers: %s\n"),
00812                      spec->lineNum, tagName(tag), spec->line);
00813             return rc;
00814         }
00815         if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
00816             return rc;
00817         break;
00818     case RPMTAG_PREREQ:
00819     case RPMTAG_REQUIREFLAGS:
00820         if ((rc = parseBits(lang, installScriptBits, &tagflags))) {
00821             rpmlog(RPMLOG_ERR,
00822                      _("line %d: Bad %s: qualifiers: %s\n"),
00823                      spec->lineNum, tagName(tag), spec->line);
00824             return rc;
00825         }
00826         if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
00827             return rc;
00828         break;
00829     /* Aliases for BuildRequires(hint): */
00830     case RPMTAG_BUILDSUGGESTS:
00831     case RPMTAG_BUILDENHANCES:
00832         tagflags = RPMSENSE_MISSINGOK;
00833         if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
00834             return rc;
00835         break;
00836     /* Aliases for Requires(hint): */
00837     case RPMTAG_SUGGESTSFLAGS:
00838     case RPMTAG_ENHANCESFLAGS:
00839         tag = RPMTAG_REQUIREFLAGS;
00840         tagflags = RPMSENSE_MISSINGOK;
00841         if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
00842             return rc;
00843         break;
00844     case RPMTAG_BUILDOBSOLETES:
00845     case RPMTAG_BUILDPROVIDES:
00846     case RPMTAG_BUILDCONFLICTS:
00847     case RPMTAG_CONFLICTFLAGS:
00848     case RPMTAG_OBSOLETEFLAGS:
00849     case RPMTAG_PROVIDEFLAGS:
00850         tagflags = RPMSENSE_ANY;
00851         if ((rc = parseRCPOT(spec, pkg, field, tag, 0, tagflags)))
00852             return rc;
00853         break;
00854     case RPMTAG_BUILDPLATFORMS:         /* XXX needs pattern parsing */
00855     case RPMTAG_EXCLUDEARCH:
00856     case RPMTAG_EXCLUSIVEARCH:
00857     case RPMTAG_EXCLUDEOS:
00858     case RPMTAG_EXCLUSIVEOS:
00859         addOrAppendListEntry(spec->sourceHeader, tag, field);
00860         break;
00861 
00862     case RPMTAG_BUILDARCHS:
00863     {   const char ** BANames = NULL;
00864         int BACount = 0;
00865         if ((rc = poptParseArgvString(field, &BACount, &BANames))) {
00866             rpmlog(RPMLOG_ERR,
00867                      _("line %d: Bad BuildArchitecture format: %s\n"),
00868                      spec->lineNum, spec->line);
00869             return RPMRC_FAIL;
00870         }
00871         if (spec->toplevel) {
00872             if (BACount > 0 && BANames != NULL) {
00873                 spec->BACount = BACount;
00874                 spec->BANames = BANames;
00875                 BANames = NULL;         /* XXX don't free. */
00876             }
00877         } else {
00878             if (BACount != 1 || strcmp(BANames[0], "noarch")) {
00879                 rpmlog(RPMLOG_ERR,
00880                      _("line %d: Only \"noarch\" sub-packages are supported: %s\n"),
00881                      spec->lineNum, spec->line);
00882                 BANames = _free(BANames);
00883                 return RPMRC_FAIL;
00884             }
00885             pkg->noarch = 1;
00886         }
00887         BANames = _free(BANames);
00888     }   break;
00889 
00890     default:
00891         macro = NULL;
00892         he->tag = tag;
00893         he->t = RPM_STRING_ARRAY_TYPE;
00894         he->p.argv= (const char **) &field;     /* XXX NOCAST */
00895         he->c = 1;
00896         he->append = 1;
00897         xx = headerPut(pkg->header, he, 0);
00898         he->append = 0;
00899         break;
00900     }
00901 
00902 /*@-usereleased@*/
00903     if (macro)
00904         addMacro(spec->macros, macro, NULL, field, RMIL_SPEC);
00905 /*@=usereleased@*/
00906     
00907     return RPMRC_OK;
00908 }
00909 
00910 /* This table has to be in a peculiar order.  If one tag is the */
00911 /* same as another, plus a few letters, it must come first.     */
00912 
00915 typedef struct PreambleRec_s {
00916     rpmTag tag;
00917     int multiLang;
00918     int obsolete;
00919 /*@observer@*/ /*@null@*/
00920     const char * token;
00921 } * PreambleRec;
00922 
00923 /*@unchecked@*/
00924 static struct PreambleRec_s preambleList[] = {
00925     {RPMTAG_NAME,               0, 0, "name"},
00926     {RPMTAG_VERSION,            0, 0, "version"},
00927     {RPMTAG_RELEASE,            0, 0, "release"},
00928     {RPMTAG_DISTEPOCH,          0, 0, "distepoch"},
00929     {RPMTAG_EPOCH,              0, 0, "epoch"},
00930     {RPMTAG_EPOCH,              0, 1, "serial"},
00931     {RPMTAG_SUMMARY,            1, 0, "summary"},
00932     {RPMTAG_LICENSE,            0, 0, "copyright"},
00933     {RPMTAG_LICENSE,            0, 0, "license"},
00934     {RPMTAG_DISTRIBUTION,       0, 0, "distribution"},
00935     {RPMTAG_DISTURL,            0, 0, "disturl"},
00936     {RPMTAG_VENDOR,             0, 0, "vendor"},
00937     {RPMTAG_GROUP,              1, 0, "group"},
00938     {RPMTAG_PACKAGER,           0, 0, "packager"},
00939     {RPMTAG_URL,                0, 0, "url"},
00940     {RPMTAG_SOURCE,             0, 0, "source"},
00941     {RPMTAG_PATCH,              0, 0, "patch"},
00942     {RPMTAG_NOSOURCE,           0, 0, "nosource"},
00943     {RPMTAG_NOPATCH,            0, 0, "nopatch"},
00944     {RPMTAG_EXCLUDEARCH,        0, 0, "excludearch"},
00945     {RPMTAG_EXCLUSIVEARCH,      0, 0, "exclusivearch"},
00946     {RPMTAG_EXCLUDEOS,          0, 0, "excludeos"},
00947     {RPMTAG_EXCLUSIVEOS,        0, 0, "exclusiveos"},
00948     {RPMTAG_ICON,               0, 0, "icon"},
00949     {RPMTAG_PROVIDEFLAGS,       0, 0, "provides"},
00950     {RPMTAG_REQUIREFLAGS,       1, 0, "requires"},
00951     {RPMTAG_PREREQ,             1, 0, "prereq"},
00952     {RPMTAG_CONFLICTFLAGS,      0, 0, "conflicts"},
00953     {RPMTAG_OBSOLETEFLAGS,      0, 0, "obsoletes"},
00954     {RPMTAG_PREFIXES,           0, 0, "prefixes"},
00955     {RPMTAG_PREFIXES,           0, 0, "prefix"},
00956     {RPMTAG_BUILDROOT,          0, 0, "buildroot"},
00957     {RPMTAG_BUILDARCHS,         0, 0, "buildarchitectures"},
00958     {RPMTAG_BUILDARCHS,         0, 0, "buildarch"},
00959     {RPMTAG_BUILDCONFLICTS,     0, 0, "buildconflicts"},
00960     {RPMTAG_BUILDOBSOLETES,     0, 0, "buildobsoletes"},
00961     {RPMTAG_BUILDPREREQ,        1, 0, "buildprereq"},
00962     {RPMTAG_BUILDPROVIDES,      0, 0, "buildprovides"},
00963     {RPMTAG_BUILDREQUIRES,      1, 0, "buildrequires"},
00964     {RPMTAG_AUTOREQPROV,        0, 0, "autoreqprov"},
00965     {RPMTAG_AUTOREQ,            0, 0, "autoreq"},
00966     {RPMTAG_AUTOPROV,           0, 0, "autoprov"},
00967     {RPMTAG_DOCDIR,             0, 0, "docdir"},
00968     {RPMTAG_DISTTAG,            0, 0, "disttag"},
00969     {RPMTAG_BUGURL,             0, 0, "bugurl"},
00970     {RPMTAG_CVSID,              0, 0, "cvsid"},
00971     {RPMTAG_SVNID,              0, 0, "svnid"},
00972     {RPMTAG_SUGGESTSFLAGS,      0, 0, "suggests"},
00973     {RPMTAG_ENHANCESFLAGS,      0, 0, "enhances"},
00974     {RPMTAG_BUILDSUGGESTS,      0, 0, "buildsuggests"},
00975     {RPMTAG_BUILDENHANCES,      0, 0, "buildenhances"},
00976     {RPMTAG_VARIANTS,           0, 0, "variants"},
00977     {RPMTAG_VARIANTS,           0, 0, "variant"},
00978     {RPMTAG_XMAJOR,             0, 0, "xmajor"},
00979     {RPMTAG_XMINOR,             0, 0, "xminor"},
00980     {RPMTAG_REPOTAG,            0, 0, "repotag"},
00981     {RPMTAG_KEYWORDS,           0, 0, "keywords"},
00982     {RPMTAG_KEYWORDS,           0, 0, "keyword"},
00983     {RPMTAG_BUILDPLATFORMS,     0, 0, "buildplatforms"},
00984 #if defined(RPM_VENDOR_OPENPKG) /* make-class-available-as-macro */
00985     {RPMTAG_CLASS,              0, 0, "class"},
00986 #endif
00987     /*@-nullassign@*/   /* LCL: can't add null annotation */
00988     {0, 0, 0, 0}
00989     /*@=nullassign@*/
00990 };
00991 
00994 static int findPreambleTag(Spec spec, /*@out@*/rpmTag * tagp,
00995                 /*@null@*/ /*@out@*/ const char ** macro, /*@out@*/ char * lang)
00996         /*@modifies *tagp, *macro, *lang @*/
00997 {
00998     PreambleRec p;
00999     char *s;
01000     size_t len = 0;
01001 
01002     /* Search for defined tags. */
01003     for (p = preambleList; p->token != NULL; p++) {
01004         len = strlen(p->token);
01005         if (!(p->token && !xstrncasecmp(spec->line, p->token, len)))
01006             continue;
01007         if (p->obsolete) {
01008             rpmlog(RPMLOG_ERR, _("Legacy syntax is unsupported: %s\n"),
01009                         p->token);
01010             p = NULL;
01011         }
01012         break;
01013     }
01014     if (p == NULL)
01015         return 1;
01016 
01017     /* Search for arbitrary tags. */
01018     if (tagp && p->token == NULL) {
01019         ARGV_t aTags = NULL;
01020         int rc = 1;     /* assume failure */
01021 
01022 /*@-noeffect@*/
01023         (void) tagName(0); /* XXX force arbitrary tags to be initialized. */
01024 /*@=noeffect@*/
01025         aTags = rpmTags->aTags;
01026         if (aTags != NULL && aTags[0] != NULL) {
01027             ARGV_t av;
01028             s = tagCanonicalize(spec->line);
01029 #if defined(RPM_VENDOR_OPENPKG) /* wildcard-matching-arbitrary-tagnames */
01030             av = argvSearchLinear(aTags, s, argvFnmatchCasefold);
01031 #else
01032             av = argvSearch(aTags, s, argvStrcasecmp);
01033 #endif
01034             if (av != NULL) {
01035                 *tagp = tagGenerate(s);
01036                 rc = 0;
01037             }
01038             s = _free(s);
01039         }
01040         return rc;
01041     }
01042 
01043     s = spec->line + len;
01044     SKIPSPACE(s);
01045 
01046     switch (p->multiLang) {
01047     default:
01048     case 0:
01049         /* Unless this is a source or a patch, a ':' better be next */
01050         if (p->tag != RPMTAG_SOURCE && p->tag != RPMTAG_PATCH) {
01051             if (*s != ':') return 1;
01052         }
01053         *lang = '\0';
01054         break;
01055     case 1:     /* Parse optional ( <token> ). */
01056         if (*s == ':') {
01057             strcpy(lang, RPMBUILD_DEFAULT_LANG);
01058             break;
01059         }
01060         if (*s != '(') return 1;
01061         s++;
01062         SKIPSPACE(s);
01063         while (!xisspace(*s) && *s != ')')
01064             *lang++ = *s++;
01065         *lang = '\0';
01066         SKIPSPACE(s);
01067         if (*s != ')') return 1;
01068         s++;
01069         SKIPSPACE(s);
01070         if (*s != ':') return 1;
01071         break;
01072     }
01073 
01074     if (tagp)
01075         *tagp = p->tag;
01076     if (macro)
01077         /*@-onlytrans -observertrans -dependenttrans@*/ /* FIX: double indirection. */
01078         *macro = p->token;
01079         /*@=onlytrans =observertrans =dependenttrans@*/
01080     return 0;
01081 }
01082 
01083 /* XXX should return rpmParseState, but RPMRC_FAIL forces int return. */
01084 int parsePreamble(Spec spec, int initialPackage)
01085 {
01086     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
01087     rpmParseState nextPart;
01088     int xx;
01089     char *linep;
01090     Package pkg;
01091     char NVR[BUFSIZ];
01092     char lang[BUFSIZ];
01093     rpmRC rc;
01094 
01095     strcpy(NVR, "(main package)");
01096 
01097     pkg = newPackage(spec);
01098     if (spec->packages == NULL) {
01099         spec->packages = pkg;
01100 assert(initialPackage);
01101     } else if (! initialPackage) {
01102         char *name = NULL;
01103         rpmParseState flag;
01104         Package lastpkg;
01105 
01106         /* There is one option to %package: <pkg> or -n <pkg> */
01107         flag = PART_NONE;
01108         if (parseSimplePart(spec, &name, &flag)) {
01109             rpmlog(RPMLOG_ERR, _("Bad package specification: %s\n"),
01110                         spec->line);
01111             return RPMRC_FAIL;
01112         }
01113         
01114         lastpkg = NULL;
01115         if (lookupPackage(spec, name, flag, &lastpkg) == RPMRC_OK) {
01116             pkg->next = lastpkg->next;
01117         } else {
01118             /* Add package to end of list */
01119             for (lastpkg = spec->packages; lastpkg->next != NULL; lastpkg = lastpkg->next)
01120             {};
01121         }
01122 assert(lastpkg != NULL);
01123         lastpkg->next = pkg;
01124         
01125         /* Construct the package */
01126         if (flag == PART_SUBNAME) {
01127             he->tag = RPMTAG_NAME;
01128             xx = headerGet(spec->packages->header, he, 0);
01129             sprintf(NVR, "%s-%s", he->p.str, name);
01130             he->p.ptr = _free(he->p.ptr);
01131         } else
01132             strcpy(NVR, name);
01133         name = _free(name);
01134         he->tag = RPMTAG_NAME;
01135         he->t = RPM_STRING_TYPE;
01136         he->p.str = NVR;
01137         he->c = 1;
01138         xx = headerPut(pkg->header, he, 0);
01139     }
01140 
01141     if ((rc = readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
01142         nextPart = PART_NONE;
01143     } else {
01144         if (rc)
01145             return rc;
01146         while ((nextPart = isPart(spec)) == PART_NONE) {
01147             const char * macro = NULL;
01148             rpmTag tag = 0;
01149 
01150             /* Skip blank lines */
01151             linep = spec->line;
01152             SKIPSPACE(linep);
01153             if (*linep != '\0') {
01154                 if (findPreambleTag(spec, &tag, &macro, lang)) {
01155                     rpmlog(RPMLOG_ERR, _("line %d: Unknown tag: %s\n"),
01156                                 spec->lineNum, spec->line);
01157                     return RPMRC_FAIL;
01158                 }
01159                 if (handlePreambleTag(spec, pkg, tag, macro, lang))
01160                     return RPMRC_FAIL;
01161                 if (spec->BANames && !spec->recursing && spec->toplevel)
01162                     return PART_BUILDARCHITECTURES;
01163             }
01164             if ((rc =
01165                  readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
01166                 nextPart = PART_NONE;
01167                 break;
01168             }
01169             if (rc)
01170                 return rc;
01171         }
01172     }
01173 
01174     /* Do some final processing on the header */
01175     
01176     /* 
01177      * Expand buildroot one more time to get %{version} and the like
01178      * from the main package.
01179      */
01180     if (initialPackage) {
01181         const char *s = rpmExpand("%{?buildroot}", NULL);
01182         if (s && *s) 
01183             (void) addMacro(NULL, "buildroot", NULL, s, -1);
01184         s = _free(s);
01185     }    
01186     
01187     /* XXX Skip valid arch check if not building binary package */
01188     if (!spec->anyarch && checkForValidArchitectures(spec))
01189         return RPMRC_FAIL;
01190 
01191     if (pkg == spec->packages)
01192         fillOutMainPackage(pkg->header);
01193 
01194     if (checkForDuplicates(pkg->header, NVR) != RPMRC_OK)
01195         return RPMRC_FAIL;
01196 
01197     if (pkg != spec->packages)
01198         headerCopyTags(spec->packages->header, pkg->header,
01199                         (void *)copyTagsDuringParse);
01200 
01201 #ifdef RPM_VENDOR_PLD /* rpm-epoch0 */
01202     /* Add Epoch: 0 to package header if it was not set by spec */
01203     he->tag = RPMTAG_NAME;
01204     if (headerGet(spec->packages->header, he, 0) == 0) {
01205         rpmuint32_t num = 0;
01206 
01207         he->tag = RPMTAG_EPOCH;
01208         he->t = RPM_UINT32_TYPE;
01209         he->p.ui32p = &num;
01210         he->c = 1;
01211         xx = headerPut(pkg->header, he, 0);
01212 
01213         /* also declare %{epoch} to be same */
01214         addMacro(spec->macros, "epoch", NULL, "0", RMIL_SPEC);
01215     }
01216 #endif /* RPM_VENDOR_PLD rpm-epoch0 */
01217 
01218     if (checkForRequired(pkg->header, NVR) != RPMRC_OK)
01219         return RPMRC_FAIL;
01220 
01221     return nextPart;
01222 }