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

Generated at Mon May 21 08:53:38 2001 for rpm by doxygen1.2.6 written by Dimitri van Heesch, © 1997-2001