build/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 }

Generated on Thu Jan 3 08:05:02 2008 for rpm by  doxygen 1.5.2