# parseSpec.c

Go to the documentation of this file.

    00001
    00006 #include "system.h"
    00007
    00008 /*@unchecked@*/
    00009 static int _debug = 0;
    00010
    00011 #include <rpmio_internal.h>
    00012 #include <rpmbuild.h>
    00013 #include "debug.h"
    00014
    00015 /*@access FD_t @*/      /* compared with NULL */
    00016
    00019 /*@unchecked@*/
    00020 static struct PartRec {
    00021     int part;
    00022     int len;
    00023 /*@observer@*/ /*@null@*/ const char * token;
    00024 } partList[] = {
    00025     { PART_PREAMBLE,      0, "%package"},
    00026     { PART_PREP,          0, "%prep"},
    00027     { PART_BUILD,         0, "%build"},
    00028     { PART_INSTALL,       0, "%install"},
    00029     { PART_CLEAN,         0, "%clean"},
    00030     { PART_PREUN,         0, "%preun"},
    00031     { PART_POSTUN,        0, "%postun"},
    00032     { PART_PRE,           0, "%pre"},
    00033     { PART_POST,          0, "%post"},
    00034     { PART_FILES,         0, "%files"},
    00035     { PART_CHANGELOG,     0, "%changelog"},
    00036     { PART_DESCRIPTION,   0, "%description"},
    00037     { PART_TRIGGERPOSTUN, 0, "%triggerpostun"},
    00038     { PART_TRIGGERUN,     0, "%triggerun"},
    00039     { PART_TRIGGERIN,     0, "%triggerin"},
    00040     { PART_TRIGGERIN,     0, "%trigger"},
    00041     { PART_VERIFYSCRIPT,  0, "%verifyscript"},
    00042     {0, 0, 0}
    00043 };
    00044
    00047 static inline void initParts(struct PartRec *p)
    00048         /*@modifies p->len @*/
    00049 {
    00050     for (; p->token != NULL; p++)
    00051         p->len = strlen(p->token);
    00052 }
    00053
    00054 rpmParseState isPart(const char *line)
    00055 {
    00056     struct PartRec *p;
    00057
    00058     if (partList[0].len == 0)
    00059         initParts(partList);
    00060
    00061     for (p = partList; p->token != NULL; p++) {
    00062         char c;
    00063         if (xstrncasecmp(line, p->token, p->len))
    00064             continue;
    00065         c = *(line + p->len);
    00066         if (c == '\0' || xisspace(c))
    00067             break;
    00068     }
    00069
    00070     return (p->token ? p->part : PART_NONE);
    00071 }
    00072
    00075 static int matchTok(const char *token, const char *line)
    00076         /*@*/
    00077 {
    00078     const char *b, *be = line;
    00079     size_t toklen = strlen(token);
    00080     int rc = 0;
    00081
    00082     while ( *(b = be) != '\0' ) {
    00083         SKIPSPACE(b);
    00084         be = b;
    00085         SKIPNONSPACE(be);
    00086         if (be == b)
    00087             break;
    00088         if (toklen != (be-b) || xstrncasecmp(token, b, (be-b)))
    00089             continue;
    00090         rc = 1;
    00091         break;
    00092     }
    00093
    00094     return rc;
    00095 }
    00096
    00097 void handleComments(char *s)
    00098 {
    00099     SKIPSPACE(s);
    00100     if (*s == '#')
    00101         *s = '\0';
    00102 }
    00103
    00106 static void forceIncludeFile(Spec spec, const char * fileName)
    00107         /*@modifies spec->fileStack @*/
    00108 {
    00109     OFI_t * ofi;
    00110
    00111     ofi = newOpenFileInfo();
    00112     ofi->fileName = xstrdup(fileName);
    00113     ofi->next = spec->fileStack;
    00114     spec->fileStack = ofi;
    00115 }
    00116
    00119 static int copyNextLine(Spec spec, OFI_t *ofi, int strip)
    00120         /*@globals rpmGlobalMacroContext,
    00121                 fileSystem @*/
    00122         /*@modifies spec->nextline, spec->nextpeekc, spec->lbuf, spec->line,
    00123                 ofi->readPtr,
    00124                 rpmGlobalMacroContext, fileSystem @*/
    00125 {
    00126     char *last;
    00127     char ch;
    00128
    00129     /* Restore 1st char in (possible) next line */
    00130     if (spec->nextline != NULL && spec->nextpeekc != '\0') {
    00131         *spec->nextline = spec->nextpeekc;
    00132         spec->nextpeekc = '\0';
    00133     }
    00134     /* Expand next line from file into line buffer */
    00135     if (!(spec->nextline && *spec->nextline)) {
    00136         char *from, *to;
    00137         to = last = spec->lbuf;
    00138         from = ofi->readPtr;
    00139         ch = ' ';
    00140         while (*from && ch != '\n')
    00141             ch = *to++ = *from++;
    00142         *to++ = '\0';
    00143         ofi->readPtr = from;
    00144
    00145         /* Don't expand macros (eg. %define) in false branch of %if clause */
    00146         if (spec->readStack->reading &&
    00147             expandMacros(spec, spec->macros, spec->lbuf, sizeof(spec->lbuf))) {
    00148                 rpmError(RPMERR_BADSPEC, _("line %d: %s\n"),
    00149                         spec->lineNum, spec->lbuf);
    00150                 return RPMERR_BADSPEC;
    00151         }
    00152         spec->nextline = spec->lbuf;
    00153     }
    00154
    00155     /* Find next line in expanded line buffer */
    00156     spec->line = last = spec->nextline;
    00157     ch = ' ';
    00158     while (*spec->nextline && ch != '\n') {
    00159         ch = *spec->nextline++;
    00160         if (!xisspace(ch))
    00161             last = spec->nextline;
    00162     }
    00163
    00164     /* Save 1st char of next line in order to terminate current line. */
    00165     if (*spec->nextline != '\0') {
    00166         spec->nextpeekc = *spec->nextline;
    00167         *spec->nextline = '\0';
    00168     }
    00169
    00170     if (strip & STRIP_COMMENTS)
    00171         handleComments(spec->line);
    00172
    00173     if (strip & STRIP_TRAILINGSPACE)
    00174         *last = '\0';
    00175
    00176     return 0;
    00177 }
    00178
    00179 int readLine(Spec spec, int strip)
    00180 {
    00181 #ifdef  DYING
    00182     const char *arch;
    00183     const char *os;
    00184 #endif
    00185     char  *s;
    00186     int match;
    00187     struct ReadLevelEntry *rl;
    00188     OFI_t *ofi = spec->fileStack;
    00189     int rc;
    00190
    00191 retry:
    00192     /* Make sure the current file is open */
    00193     /*@-branchstate@*/
    00194     if (ofi->fd == NULL) {
    00195         ofi->fd = Fopen(ofi->fileName, "r.fpio");
    00196         if (ofi->fd == NULL || Ferror(ofi->fd)) {
    00197             /* XXX Fstrerror */
    00198             rpmError(RPMERR_BADSPEC, _("Unable to open %s: %s\n"),
    00199                      ofi->fileName, Fstrerror(ofi->fd));
    00200             return RPMERR_BADSPEC;
    00201         }
    00202         spec->lineNum = ofi->lineNum = 0;
    00203     }
    00204     /*@=branchstate@*/
    00205
    00206     /* Make sure we have something in the read buffer */
    00207     if (!(ofi->readPtr && *(ofi->readPtr))) {
    00208         /*@-type@*/ /* FIX: cast? */
    00209         FILE * f = fdGetFp(ofi->fd);
    00210         /*@=type@*/
    00211         if (f == NULL || !fgets(ofi->readBuf, BUFSIZ, f)) {
    00212             /* EOF */
    00213             if (spec->readStack->next) {
    00214                 rpmError(RPMERR_UNMATCHEDIF, _("Unclosed %%if\n"));
    00215                 return RPMERR_UNMATCHEDIF;
    00216             }
    00217
    00218             /* remove this file from the stack */
    00219             spec->fileStack = ofi->next;
    00220             (void) Fclose(ofi->fd);
    00221             ofi->fileName = _free(ofi->fileName);
    00222             ofi = _free(ofi);
    00223
    00224             /* only on last file do we signal EOF to caller */
    00225             ofi = spec->fileStack;
    00226             if (ofi == NULL)
    00227                 return 1;
    00228
    00229             /* otherwise, go back and try the read again. */
    00230             goto retry;
    00231         }
    00232         ofi->readPtr = ofi->readBuf;
    00233         ofi->lineNum++;
    00234         spec->lineNum = ofi->lineNum;
    00235         if (spec->sl) {
    00236             speclines sl = spec->sl;
    00237             if (sl->sl_nlines == sl->sl_nalloc) {
    00238                 sl->sl_nalloc += 100;
    00239                 sl->sl_lines = (char **) xrealloc(sl->sl_lines,
    00240                         sl->sl_nalloc * sizeof(*(sl->sl_lines)));
    00241             }
    00242             sl->sl_lines[sl->sl_nlines++] = xstrdup(ofi->readBuf);
    00243         }
    00244     }
    00245
    00246 #ifdef  DYING
    00247     arch = NULL;
    00248     rpmGetArchInfo(&arch, NULL);
    00249     os = NULL;
    00250     rpmGetOsInfo(&os, NULL);
    00251 #endif
    00252
    00253     /* Copy next file line into the spec line buffer */
    00254     if ((rc = copyNextLine(spec, ofi, strip)) != 0)
    00255         return rc;
    00256
    00257     s = spec->line;
    00258     SKIPSPACE(s);
    00259
    00260     match = -1;
    00261     if (! strncmp("%ifarch", s, sizeof("%ifarch")-1)) {
    00262         const char *arch = rpmExpand("%{_target_cpu}", NULL);
    00263         s += 7;
    00264         match = matchTok(arch, s);
    00265         arch = _free(arch);
    00266     } else if (! strncmp("%ifnarch", s, sizeof("%ifnarch")-1)) {
    00267         const char *arch = rpmExpand("%{_target_cpu}", NULL);
    00268         s += 8;
    00269         match = !matchTok(arch, s);
    00270         arch = _free(arch);
    00271     } else if (! strncmp("%ifos", s, sizeof("%ifos")-1)) {
    00272         const char *os = rpmExpand("%{_target_os}", NULL);
    00273         s += 5;
    00274         match = matchTok(os, s);
    00275         os = _free(os);
    00276     } else if (! strncmp("%ifnos", s, sizeof("%ifnos")-1)) {
    00277         const char *os = rpmExpand("%{_target_os}", NULL);
    00278         s += 6;
    00279         match = !matchTok(os, s);
    00280         os = _free(os);
    00281     } else if (! strncmp("%if", s, sizeof("%if")-1)) {
    00282         s += 3;
    00283         match = parseExpressionBoolean(spec, s);
    00284         if (match < 0) {
    00285             rpmError(RPMERR_UNMATCHEDIF,
    00286                         _("%s:%d: parseExpressionBoolean returns %d\n"),
    00287                         ofi->fileName, ofi->lineNum, match);
    00288             return RPMERR_BADSPEC;
    00289         }
    00290     } else if (! strncmp("%else", s, sizeof("%else")-1)) {
    00291         s += 5;
    00292         if (! spec->readStack->next) {
    00293             /* Got an else with no %if ! */
    00294             rpmError(RPMERR_UNMATCHEDIF,
    00295                         _("%s:%d: Got a %%else with no %%if\n"),
    00296                         ofi->fileName, ofi->lineNum);
    00297             return RPMERR_UNMATCHEDIF;
    00298         }
    00299         spec->readStack->reading =
    00300             spec->readStack->next->reading && ! spec->readStack->reading;
    00301         spec->line[0] = '\0';
    00302     } else if (! strncmp("%endif", s, sizeof("%endif")-1)) {
    00303         s += 6;
    00304         if (! spec->readStack->next) {
    00305             /* Got an end with no %if ! */
    00306             rpmError(RPMERR_UNMATCHEDIF,
    00307                         _("%s:%d: Got a %%endif with no %%if\n"),
    00308                         ofi->fileName, ofi->lineNum);
    00309             return RPMERR_UNMATCHEDIF;
    00310         }
    00311         rl = spec->readStack;
    00312         spec->readStack = spec->readStack->next;
    00313         free(rl);
    00314         spec->line[0] = '\0';
    00315     } else if (! strncmp("%include", s, sizeof("%include")-1)) {
    00316         char *fileName, *endFileName, *p;
    00317
    00318         s += 8;
    00319         fileName = s;
    00320         if (! xisspace(*fileName)) {
    00321             rpmError(RPMERR_BADSPEC, _("malformed %%include statement\n"));
    00322             return RPMERR_BADSPEC;
    00323         }
    00324         SKIPSPACE(fileName);
    00325         endFileName = fileName;
    00326         SKIPNONSPACE(endFileName);
    00327         p = endFileName;
    00328         SKIPSPACE(p);
    00329         if (*p != '\0') {
    00330             rpmError(RPMERR_BADSPEC, _("malformed %%include statement\n"));
    00331             return RPMERR_BADSPEC;
    00332         }
    00333         *endFileName = '\0';
    00334
    00335         forceIncludeFile(spec, fileName);
    00336
    00337         ofi = spec->fileStack;
    00338         goto retry;
    00339     }
    00340
    00341     if (match != -1) {
    00342         rl = xmalloc(sizeof(*rl));
    00343         rl->reading = spec->readStack->reading && match;
    00344         rl->next = spec->readStack;
    00345         spec->readStack = rl;
    00346         spec->line[0] = '\0';
    00347     }
    00348
    00349     if (! spec->readStack->reading) {
    00350         spec->line[0] = '\0';
    00351     }
    00352
    00353     /*@-compmempass@*/ /* FIX: spec->readStack->next should be dependent */
    00354     return 0;
    00355     /*@=compmempass@*/
    00356 }
    00357
    00358 void closeSpec(Spec spec)
    00359 {
    00360     OFI_t *ofi;
    00361
    00362     while (spec->fileStack) {
    00363         ofi = spec->fileStack;
    00364         spec->fileStack = spec->fileStack->next;
    00365         if (ofi->fd) (void) Fclose(ofi->fd);
    00366         ofi->fileName = _free(ofi->fileName);
    00367         ofi = _free(ofi);
    00368     }
    00369 }
    00370
    00371 /*@-redecl@*/
    00372 /*@unchecked@*/
    00373 extern int noLang;              /* XXX FIXME: pass as arg */
    00374 /*@=redecl@*/
    00375
    00376 /*@todo Skip parse recursion if os is not compatible. @*/
    00377 int parseSpec(Spec *specp, const char *specFile, const char *rootURL,
    00378                 const char *buildRootURL, int recursing, const char *passPhrase,
    00379                 char *cookie, int anyarch, int force)
    00380 {
    00381     rpmParseState parsePart = PART_PREAMBLE;
    00382     int initialPackage = 1;
    00383 #ifdef  DYING
    00384     const char *saveArch;
    00385 #endif
    00386     Package pkg;
    00387     Spec spec;
    00388
    00389     /* Set up a new Spec structure with no packages. */
    00390     spec = newSpec();
    00391
    00392     /*
    00393      * Note: rpmGetPath should guarantee a "canonical" path. That means
    00394      * that the following pathologies should be weeded out:
    00395      *          //bin//sh
    00396      *          //usr//bin/
    00397      *          /.././../usr/../bin//./sh (XXX FIXME: dots not handled yet)
    00398      */
    00399     spec->specFile = rpmGetPath(specFile, NULL);
    00400     spec->fileStack = newOpenFileInfo();
    00401     spec->fileStack->fileName = xstrdup(spec->specFile);
    00402     if (buildRootURL) {
    00403         const char * buildRoot;
    00404         (void) urlPath(buildRootURL, &buildRoot);
    00405         /*@-branchstate@*/
    00406         if (*buildRoot == '\0') buildRoot = "/";
    00407         /*@=branchstate@*/
    00408         if (!strcmp(buildRoot, "/")) {
    00409             rpmError(RPMERR_BADSPEC,
    00410                      _("BuildRoot can not be \"/\": %s\n"), buildRootURL);
    00411             return RPMERR_BADSPEC;
    00412         }
    00413         spec->gotBuildRootURL = 1;
    00414         spec->buildRootURL = xstrdup(buildRootURL);
    00415         addMacro(spec->macros, "buildroot", NULL, buildRoot, RMIL_SPEC);
    00416 if (_debug)
    00417 fprintf(stderr, "*** PS buildRootURL(%s) %p macro set to %s\n", spec->buildRootURL, spec->buildRootURL, buildRoot);
    00418     }
    00419     addMacro(NULL, "_docdir", NULL, "%{_defaultdocdir}", RMIL_SPEC);
    00420     spec->recursing = recursing;
    00421     spec->anyarch = anyarch;
    00422     spec->force = force;
    00423
    00424     if (rootURL)
    00425         spec->rootURL = xstrdup(rootURL);
    00426     if (passPhrase)
    00427         spec->passPhrase = xstrdup(passPhrase);
    00428     if (cookie)
    00429         spec->cookie = xstrdup(cookie);
    00430
    00431     spec->timeCheck = rpmExpandNumeric("%{_timecheck}");
    00432
    00433     /* All the parse*() functions expect to have a line pre-read */
    00434     /* in the spec's line buffer.  Except for parsePreamble(),   */
    00435     /* which handles the initial entry into a spec file.         */
    00436
    00437     /*@-infloops@*/     /* LCL: parsePart is modified @*/
    00438     while (parsePart < PART_LAST && parsePart != PART_NONE) {
    00439         switch (parsePart) {
    00440         case PART_PREAMBLE:
    00441             parsePart = parsePreamble(spec, initialPackage);
    00442             initialPackage = 0;
    00443             /*@switchbreak@*/ break;
    00444         case PART_PREP:
    00445             parsePart = parsePrep(spec);
    00446             /*@switchbreak@*/ break;
    00447         case PART_BUILD:
    00448         case PART_INSTALL:
    00449         case PART_CLEAN:
    00450             parsePart = parseBuildInstallClean(spec, parsePart);
    00451             /*@switchbreak@*/ break;
    00452         case PART_CHANGELOG:
    00453             parsePart = parseChangelog(spec);
    00454             /*@switchbreak@*/ break;
    00455         case PART_DESCRIPTION:
    00456             parsePart = parseDescription(spec);
    00457             /*@switchbreak@*/ break;
    00458
    00459         case PART_PRE:
    00460         case PART_POST:
    00461         case PART_PREUN:
    00462         case PART_POSTUN:
    00463         case PART_VERIFYSCRIPT:
    00464         case PART_TRIGGERIN:
    00465         case PART_TRIGGERUN:
    00466         case PART_TRIGGERPOSTUN:
    00467             parsePart = parseScript(spec, parsePart);
    00468             /*@switchbreak@*/ break;
    00469
    00470         case PART_FILES:
    00471             parsePart = parseFiles(spec);
    00472             /*@switchbreak@*/ break;
    00473
    00474         case PART_NONE:         /* XXX avoid gcc whining */
    00475         case PART_LAST:
    00476         case PART_BUILDARCHITECTURES:
    00477             /*@switchbreak@*/ break;
    00478         }
    00479
    00480         if (parsePart >= PART_LAST) {
    00481             spec = freeSpec(spec);
    00482             return parsePart;
    00483         }
    00484
    00485         if (parsePart == PART_BUILDARCHITECTURES) {
    00486             int index;
    00487             int x;
    00488
    00489             closeSpec(spec);
    00490
    00491             /* LCL: sizeof(spec->BASpecs[0]) -nullderef whine here */
    00492             spec->BASpecs = xcalloc(spec->BACount, sizeof(*spec->BASpecs));
    00493             index = 0;
    00494             if (spec->BANames != NULL)
    00495             for (x = 0; x < spec->BACount; x++) {
    00496
    00497                 /* Skip if not arch is not compatible. */
    00498                 if (!rpmMachineScore(RPM_MACHTABLE_BUILDARCH, spec->BANames[x]))
    00499                     /*@innercontinue@*/ continue;
    00500 #ifdef  DYING
    00501                 rpmGetMachine(&saveArch, NULL);
    00502                 saveArch = xstrdup(saveArch);
    00503                 rpmSetMachine(spec->BANames[x], NULL);
    00504 #else
    00505                 addMacro(NULL, "_target_cpu", NULL, spec->BANames[x], RMIL_RPMRC);
    00506 #endif
    00507                 spec->BASpecs[index] = NULL;
    00508                 if (parseSpec(&(spec->BASpecs[index]),
    00509                                   specFile, spec->rootURL, buildRootURL, 1,
    00510                                   passPhrase, cookie, anyarch, force))
    00511                 {
    00512                         spec->BACount = index;
    00513                         spec = freeSpec(spec);
    00514                         return RPMERR_BADSPEC;
    00515                 }
    00516 #ifdef  DYING
    00517                 rpmSetMachine(saveArch, NULL);
    00518                 saveArch = _free(saveArch);
    00519 #else
    00520                 delMacro(NULL, "_target_cpu");
    00521 #endif
    00522                 index++;
    00523             }
    00524
    00525             spec->BACount = index;
    00526             if (! index) {
    00527                 spec = freeSpec(spec);
    00528                 rpmError(RPMERR_BADSPEC,
    00529                         _("No compatible architectures found for build\n"));
    00530                 return RPMERR_BADSPEC;
    00531             }
    00532
    00533             /*
    00534              * Return the 1st child's fully parsed Spec structure.
    00535              * The restart of the parse when encountering BuildArch
    00536              * causes problems for "rpm -q --specfile". This is
    00537              * still a hack because there may be more than 1 arch
    00538              * specified (unlikely but possible.) There's also the
    00539              * further problem that the macro context, particularly
    00540              * %{_target_cpu}, disagrees with the info in the header.
    00541              */
    00542             /*@-branchstate@*/
    00543             if (spec->BACount >= 1) {
    00544                 Spec nspec = spec->BASpecs[0];
    00545                 spec->BASpecs = _free(spec->BASpecs);
    00546                 spec = freeSpec(spec);
    00547                 spec = nspec;
    00548             }
    00549             /*@=branchstate@*/
    00550
    00551             *specp = spec;
    00552             return 0;
    00553         }
    00554     }
    00555     /*@=infloops@*/     /* LCL: parsePart is modified @*/
    00556
    00557     /* Check for description in each package and add arch and os */
    00558   {
    00559 #ifdef  DYING
    00560     const char *arch = NULL;
    00561     const char *os = NULL;
    00562     char *myos = NULL;
    00563
    00564     rpmGetArchInfo(&arch, NULL);
    00565     rpmGetOsInfo(&os, NULL);
    00566     /*
    00567      * XXX Capitalizing the 'L' is needed to insure that old
    00568      * XXX os-from-uname (e.g. "Linux") is compatible with the new
    00569      * XXX os-from-platform (e.g "linux" from "sparc-*-linux").
    00570      * XXX A copy of this string is embedded in headers.
    00571      */
    00572     if (!strcmp(os, "linux")) {
    00573         myos = xstrdup(os);
    00574         *myos = 'L';
    00575         os = myos;
    00576     }
    00577 #else
    00578     const char *platform = rpmExpand("%{_target_platform}", NULL);
    00579     const char *arch = rpmExpand("%{_target_cpu}", NULL);
    00580     const char *os = rpmExpand("%{_target_os}", NULL);
    00581 #endif
    00582
    00583     for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
    00584         if (!headerIsEntry(pkg->header, RPMTAG_DESCRIPTION)) {
    00585             const char * name;
    00586             (void) headerNVR(pkg->header, &name, NULL, NULL);
    00587             rpmError(RPMERR_BADSPEC, _("Package has no %%description: %s\n"),
    00588                         name);
    00589             spec = freeSpec(spec);
    00590             return RPMERR_BADSPEC;
    00591         }
    00592
    00593         (void) headerAddEntry(pkg->header, RPMTAG_OS, RPM_STRING_TYPE, os, 1);
    00594         (void) headerAddEntry(pkg->header, RPMTAG_ARCH,
    00595                 RPM_STRING_TYPE, arch, 1);
    00596         if (!headerIsEntry(pkg->header, RPMTAG_RHNPLATFORM))
    00597             (void) headerAddEntry(pkg->header, RPMTAG_RHNPLATFORM,
    00598                 RPM_STRING_TYPE, arch, 1);
    00599         (void) headerAddEntry(pkg->header, RPMTAG_PLATFORM,
    00600                 RPM_STRING_TYPE, platform, 1);
    00601     }
    00602
    00603 #ifdef  DYING
    00604     myos = _free(myos);
    00605 #else
    00606     platform = _free(platform);
    00607     arch = _free(arch);
    00608     os = _free(os);
    00609 #endif
    00610   }
    00611
    00612     closeSpec(spec);
    00613     *specp = spec;
    00614
    00615     return 0;
    00616 }