Main Page | Modules | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

parseSpec.c

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

Generated on Wed Mar 15 14:02:43 2006 for rpm by  doxygen 1.3.9.1