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

Generated at Thu Sep 6 11:25:40 2001 for rpm by doxygen1.2.8.1 written by Dimitri van Heesch, © 1997-2001