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

Generated on Sun Feb 2 23:31:59 2003 for rpm by doxygen1.2.18