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

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

Generated on Sun Oct 26 13:01:57 2003 for rpm by doxygen1.2.18