build/files.c

Go to the documentation of this file.
00001 
00007 #include "system.h"
00008 
00009 #define MYALLPERMS      07777
00010 
00011 #include <regex.h>
00012 
00013 #include <rpmio_internal.h>
00014 #include <fts.h>
00015 
00016 #include <rpmbuild.h>
00017 
00018 #include "cpio.h"
00019 
00020 #include "argv.h"
00021 #include "rpmfc.h"
00022 
00023 #define _RPMFI_INTERNAL
00024 #include "rpmfi.h"
00025 
00026 #include "rpmsx.h"
00027 
00028 #define _RPMTE_INTERNAL
00029 #include "rpmte.h"
00030 
00031 #include "buildio.h"
00032 
00033 #include "legacy.h"     /* XXX domd5, expandFileList, compressFileList */
00034 #include "misc.h"
00035 #include "debug.h"
00036 
00037 /*@access Header @*/
00038 /*@access rpmfi @*/
00039 /*@access rpmte @*/
00040 /*@access FD_t @*/
00041 /*@access StringBuf @*/         /* compared with NULL */
00042 
00043 #define SKIPWHITE(_x)   {while(*(_x) && (xisspace(*_x) || *(_x) == ',')) (_x)++;}
00044 #define SKIPNONWHITE(_x){while(*(_x) &&!(xisspace(*_x) || *(_x) == ',')) (_x)++;}
00045 
00046 #define MAXDOCDIR 1024
00047 
00050 typedef enum specdFlags_e {
00051     SPECD_DEFFILEMODE   = (1 << 0),
00052     SPECD_DEFDIRMODE    = (1 << 1),
00053     SPECD_DEFUID        = (1 << 2),
00054     SPECD_DEFGID        = (1 << 3),
00055     SPECD_DEFVERIFY     = (1 << 4),
00056 
00057     SPECD_FILEMODE      = (1 << 8),
00058     SPECD_DIRMODE       = (1 << 9),
00059     SPECD_UID           = (1 << 10),
00060     SPECD_GID           = (1 << 11),
00061     SPECD_VERIFY        = (1 << 12)
00062 } specdFlags;
00063 
00066 typedef struct FileListRec_s {
00067     struct stat fl_st;
00068 #define fl_dev  fl_st.st_dev
00069 #define fl_ino  fl_st.st_ino
00070 #define fl_mode fl_st.st_mode
00071 #define fl_nlink fl_st.st_nlink
00072 #define fl_uid  fl_st.st_uid
00073 #define fl_gid  fl_st.st_gid
00074 #define fl_rdev fl_st.st_rdev
00075 #define fl_size fl_st.st_size
00076 #define fl_mtime fl_st.st_mtime
00077 
00078 /*@only@*/
00079     const char *diskURL;        /* get file from here       */
00080 /*@only@*/
00081     const char *fileURL;        /* filename in cpio archive */
00082 /*@observer@*/
00083     const char *uname;
00084 /*@observer@*/
00085     const char *gname;
00086     unsigned    flags;
00087     specdFlags  specdFlags;     /* which attributes have been explicitly specified. */
00088     unsigned    verifyFlags;
00089 /*@only@*/
00090     const char *langs;          /* XXX locales separated with | */
00091 } * FileListRec;
00092 
00095 typedef struct AttrRec_s {
00096 /*@null@*/
00097     const char *ar_fmodestr;
00098 /*@null@*/
00099     const char *ar_dmodestr;
00100 /*@null@*/
00101     const char *ar_user;
00102 /*@null@*/
00103     const char *ar_group;
00104     mode_t      ar_fmode;
00105     mode_t      ar_dmode;
00106 } * AttrRec;
00107 
00108 /*@-readonlytrans@*/
00109 /*@unchecked@*/ /*@observer@*/
00110 static struct AttrRec_s root_ar = { NULL, NULL, "root", "root", 0, 0 };
00111 /*@=readonlytrans@*/
00112 
00113 /* list of files */
00114 /*@unchecked@*/ /*@only@*/ /*@null@*/
00115 static StringBuf check_fileList = NULL;
00116 
00120 typedef struct FileList_s {
00121 /*@only@*/
00122     const char * buildRootURL;
00123 /*@only@*/
00124     const char * prefix;
00125 
00126     int fileCount;
00127     int totalFileSize;
00128     int processingFailed;
00129 
00130     int passedSpecialDoc;
00131     int isSpecialDoc;
00132 
00133     int noGlob;
00134     unsigned devtype;
00135     unsigned devmajor;
00136     int devminor;
00137     
00138     int isDir;
00139     int inFtw;
00140     int currentFlags;
00141     specdFlags currentSpecdFlags;
00142     int currentVerifyFlags;
00143     struct AttrRec_s cur_ar;
00144     struct AttrRec_s def_ar;
00145     specdFlags defSpecdFlags;
00146     int defVerifyFlags;
00147     int nLangs;
00148 /*@only@*/ /*@null@*/
00149     const char ** currentLangs;
00150 
00151     /* Hard coded limit of MAXDOCDIR docdirs.         */
00152     /* If you break it you are doing something wrong. */
00153     const char * docDirs[MAXDOCDIR];
00154     int docDirCount;
00155     
00156 /*@only@*/
00157     FileListRec fileList;
00158     int fileListRecsAlloced;
00159     int fileListRecsUsed;
00160 } * FileList;
00161 
00164 static void nullAttrRec(/*@out@*/ AttrRec ar)   /*@modifies ar @*/
00165 {
00166     ar->ar_fmodestr = NULL;
00167     ar->ar_dmodestr = NULL;
00168     ar->ar_user = NULL;
00169     ar->ar_group = NULL;
00170     ar->ar_fmode = 0;
00171     ar->ar_dmode = 0;
00172 }
00173 
00176 static void freeAttrRec(AttrRec ar)     /*@modifies ar @*/
00177 {
00178     ar->ar_fmodestr = _free(ar->ar_fmodestr);
00179     ar->ar_dmodestr = _free(ar->ar_dmodestr);
00180     ar->ar_user = _free(ar->ar_user);
00181     ar->ar_group = _free(ar->ar_group);
00182     /* XXX doesn't free ar (yet) */
00183     /*@-nullstate@*/
00184     return;
00185     /*@=nullstate@*/
00186 }
00187 
00190 static void dupAttrRec(const AttrRec oar, /*@in@*/ /*@out@*/ AttrRec nar)
00191         /*@modifies nar @*/
00192 {
00193     if (oar == nar)
00194         return;
00195     freeAttrRec(nar);
00196     nar->ar_fmodestr = (oar->ar_fmodestr ? xstrdup(oar->ar_fmodestr) : NULL);
00197     nar->ar_dmodestr = (oar->ar_dmodestr ? xstrdup(oar->ar_dmodestr) : NULL);
00198     nar->ar_user = (oar->ar_user ? xstrdup(oar->ar_user) : NULL);
00199     nar->ar_group = (oar->ar_group ? xstrdup(oar->ar_group) : NULL);
00200     nar->ar_fmode = oar->ar_fmode;
00201     nar->ar_dmode = oar->ar_dmode;
00202 }
00203 
00204 #if 0
00205 
00207 static void dumpAttrRec(const char * msg, AttrRec ar)
00208         /*@globals fileSystem@*/
00209         /*@modifies fileSystem @*/
00210 {
00211     if (msg)
00212         fprintf(stderr, "%s:\t", msg);
00213     fprintf(stderr, "(%s, %s, %s, %s)\n",
00214         ar->ar_fmodestr,
00215         ar->ar_user,
00216         ar->ar_group,
00217         ar->ar_dmodestr);
00218 }
00219 #endif
00220 
00225 /*@-boundswrite@*/
00226 /*@null@*/
00227 static char *strtokWithQuotes(/*@null@*/ char *s, char *delim)
00228         /*@modifies *s @*/
00229 {
00230     static char *olds = NULL;
00231     char *token;
00232 
00233     if (s == NULL)
00234         s = olds;
00235     if (s == NULL)
00236         return NULL;
00237 
00238     /* Skip leading delimiters */
00239     s += strspn(s, delim);
00240     if (*s == '\0')
00241         return NULL;
00242 
00243     /* Find the end of the token.  */
00244     token = s;
00245     if (*token == '"') {
00246         token++;
00247         /* Find next " char */
00248         s = strchr(token, '"');
00249     } else {
00250         s = strpbrk(token, delim);
00251     }
00252 
00253     /* Terminate it */
00254     if (s == NULL) {
00255         /* This token finishes the string */
00256         olds = strchr(token, '\0');
00257     } else {
00258         /* Terminate the token and make olds point past it */
00259         *s = '\0';
00260         olds = s+1;
00261     }
00262 
00263     /*@-retalias -temptrans @*/
00264     return token;
00265     /*@=retalias =temptrans @*/
00266 }
00267 /*@=boundswrite@*/
00268 
00271 static void timeCheck(int tc, Header h)
00272         /*@globals internalState @*/
00273         /*@modifies internalState @*/
00274 {
00275     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
00276     HFD_t hfd = headerFreeData;
00277     int * mtime;
00278     const char ** files;
00279     rpmTagType fnt;
00280     int count, x;
00281     time_t currentTime = time(NULL);
00282 
00283     x = hge(h, RPMTAG_OLDFILENAMES, &fnt, (void **) &files, &count);
00284     x = hge(h, RPMTAG_FILEMTIMES, NULL, (void **) &mtime, NULL);
00285     
00286 /*@-boundsread@*/
00287     for (x = 0; x < count; x++) {
00288         if ((currentTime - mtime[x]) > tc)
00289             rpmMessage(RPMMESS_WARNING, _("TIMECHECK failure: %s\n"), files[x]);
00290     }
00291     files = hfd(files, fnt);
00292 /*@=boundsread@*/
00293 }
00294 
00297 typedef struct VFA {
00298 /*@observer@*/ /*@null@*/ const char * attribute;
00299     int not;
00300     int flag;
00301 } VFA_t;
00302 
00305 /*@-exportlocal -exportheadervar@*/
00306 /*@unchecked@*/
00307 VFA_t verifyAttrs[] = {
00308     { "md5",    0,      RPMVERIFY_MD5 },
00309     { "size",   0,      RPMVERIFY_FILESIZE },
00310     { "link",   0,      RPMVERIFY_LINKTO },
00311     { "user",   0,      RPMVERIFY_USER },
00312     { "group",  0,      RPMVERIFY_GROUP },
00313     { "mtime",  0,      RPMVERIFY_MTIME },
00314     { "mode",   0,      RPMVERIFY_MODE },
00315     { "rdev",   0,      RPMVERIFY_RDEV },
00316     { NULL, 0,  0 }
00317 };
00318 /*@=exportlocal =exportheadervar@*/
00319 
00326 /*@-boundswrite@*/
00327 static int parseForVerify(char * buf, FileList fl)
00328         /*@modifies buf, fl->processingFailed,
00329                 fl->currentVerifyFlags, fl->defVerifyFlags,
00330                 fl->currentSpecdFlags, fl->defSpecdFlags @*/
00331 {
00332     char *p, *pe, *q;
00333     const char *name;
00334     int *resultVerify;
00335     int negated;
00336     int verifyFlags;
00337     specdFlags * specdFlags;
00338 
00339     if ((p = strstr(buf, (name = "%verify"))) != NULL) {
00340         resultVerify = &(fl->currentVerifyFlags);
00341         specdFlags = &fl->currentSpecdFlags;
00342     } else if ((p = strstr(buf, (name = "%defverify"))) != NULL) {
00343         resultVerify = &(fl->defVerifyFlags);
00344         specdFlags = &fl->defSpecdFlags;
00345     } else
00346         return 0;
00347 
00348     for (pe = p; (pe-p) < strlen(name); pe++)
00349         *pe = ' ';
00350 
00351     SKIPSPACE(pe);
00352 
00353     if (*pe != '(') {
00354         rpmError(RPMERR_BADSPEC, _("Missing '(' in %s %s\n"), name, pe);
00355         fl->processingFailed = 1;
00356         return RPMERR_BADSPEC;
00357     }
00358 
00359     /* Bracket %*verify args */
00360     *pe++ = ' ';
00361     for (p = pe; *pe && *pe != ')'; pe++)
00362         {};
00363 
00364     if (*pe == '\0') {
00365         rpmError(RPMERR_BADSPEC, _("Missing ')' in %s(%s\n"), name, p);
00366         fl->processingFailed = 1;
00367         return RPMERR_BADSPEC;
00368     }
00369 
00370     /* Localize. Erase parsed string */
00371     q = alloca((pe-p) + 1);
00372     strncpy(q, p, pe-p);
00373     q[pe-p] = '\0';
00374     while (p <= pe)
00375         *p++ = ' ';
00376 
00377     negated = 0;
00378     verifyFlags = RPMVERIFY_NONE;
00379 
00380     for (p = q; *p != '\0'; p = pe) {
00381         SKIPWHITE(p);
00382         if (*p == '\0')
00383             break;
00384         pe = p;
00385         SKIPNONWHITE(pe);
00386         if (*pe != '\0')
00387             *pe++ = '\0';
00388 
00389         {   VFA_t *vfa;
00390             for (vfa = verifyAttrs; vfa->attribute != NULL; vfa++) {
00391                 if (strcmp(p, vfa->attribute))
00392                     /*@innercontinue@*/ continue;
00393                 verifyFlags |= vfa->flag;
00394                 /*@innerbreak@*/ break;
00395             }
00396             if (vfa->attribute)
00397                 continue;
00398         }
00399 
00400         if (!strcmp(p, "not")) {
00401             negated ^= 1;
00402         } else {
00403             rpmError(RPMERR_BADSPEC, _("Invalid %s token: %s\n"), name, p);
00404             fl->processingFailed = 1;
00405             return RPMERR_BADSPEC;
00406         }
00407     }
00408 
00409     *resultVerify = negated ? ~(verifyFlags) : verifyFlags;
00410     *specdFlags |= SPECD_VERIFY;
00411 
00412     return 0;
00413 }
00414 /*@=boundswrite@*/
00415 
00416 #define isAttrDefault(_ars)     ((_ars)[0] == '-' && (_ars)[1] == '\0')
00417 
00424 /*@-boundswrite@*/
00425 static int parseForDev(char * buf, FileList fl)
00426         /*@modifies buf, fl->processingFailed,
00427                 fl->noGlob, fl->devtype, fl->devmajor, fl->devminor @*/
00428 {
00429     const char * name;
00430     const char * errstr = NULL;
00431     char *p, *pe, *q;
00432     int rc = RPMERR_BADSPEC;    /* assume error */
00433 
00434     if ((p = strstr(buf, (name = "%dev"))) == NULL)
00435         return 0;
00436 
00437     for (pe = p; (pe-p) < strlen(name); pe++)
00438         *pe = ' ';
00439     SKIPSPACE(pe);
00440 
00441     if (*pe != '(') {
00442         errstr = "'('";
00443         goto exit;
00444     }
00445 
00446     /* Bracket %dev args */
00447     *pe++ = ' ';
00448     for (p = pe; *pe && *pe != ')'; pe++)
00449         {};
00450     if (*pe != ')') {
00451         errstr = "')'";
00452         goto exit;
00453     }
00454 
00455     /* Localize. Erase parsed string */
00456     q = alloca((pe-p) + 1);
00457     strncpy(q, p, pe-p);
00458     q[pe-p] = '\0';
00459     while (p <= pe)
00460         *p++ = ' ';
00461 
00462     p = q; SKIPWHITE(p);
00463     pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
00464     if (*p == 'b')
00465         fl->devtype = 'b';
00466     else if (*p == 'c')
00467         fl->devtype = 'c';
00468     else {
00469         errstr = "devtype";
00470         goto exit;
00471     }
00472 
00473     p = pe; SKIPWHITE(p);
00474     pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
00475     for (pe = p; *pe && xisdigit(*pe); pe++)
00476         {} ;
00477     if (*pe == '\0') {
00478         fl->devmajor = atoi(p);
00479         /*@-unsignedcompare @*/ /* LCL: ge is ok */
00480         if (!(fl->devmajor >= 0 && fl->devmajor < 256)) {
00481             errstr = "devmajor";
00482             goto exit;
00483         }
00484         /*@=unsignedcompare @*/
00485         pe++;
00486     } else {
00487         errstr = "devmajor";
00488         goto exit;
00489     }
00490 
00491     p = pe; SKIPWHITE(p);
00492     pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
00493     for (pe = p; *pe && xisdigit(*pe); pe++)
00494         {} ;
00495     if (*pe == '\0') {
00496         fl->devminor = atoi(p);
00497         if (!(fl->devminor >= 0 && fl->devminor < 256)) {
00498             errstr = "devminor";
00499             goto exit;
00500         }
00501         pe++;
00502     } else {
00503         errstr = "devminor";
00504         goto exit;
00505     }
00506 
00507     fl->noGlob = 1;
00508 
00509     rc = 0;
00510 
00511 exit:
00512     if (rc) {
00513         rpmError(RPMERR_BADSPEC, _("Missing %s in %s %s\n"), errstr, name, p);
00514         fl->processingFailed = 1;
00515     }
00516     return rc;
00517 }
00518 /*@=boundswrite@*/
00519 
00526 /*@-boundswrite@*/
00527 static int parseForAttr(char * buf, FileList fl)
00528         /*@modifies buf, fl->processingFailed,
00529                 fl->cur_ar, fl->def_ar,
00530                 fl->currentSpecdFlags, fl->defSpecdFlags @*/
00531 {
00532     const char *name;
00533     char *p, *pe, *q;
00534     int x;
00535     struct AttrRec_s arbuf;
00536     AttrRec ar = &arbuf, ret_ar;
00537     specdFlags * specdFlags;
00538 
00539     if ((p = strstr(buf, (name = "%attr"))) != NULL) {
00540         ret_ar = &(fl->cur_ar);
00541         specdFlags = &fl->currentSpecdFlags;
00542     } else if ((p = strstr(buf, (name = "%defattr"))) != NULL) {
00543         ret_ar = &(fl->def_ar);
00544         specdFlags = &fl->defSpecdFlags;
00545     } else
00546         return 0;
00547 
00548     for (pe = p; (pe-p) < strlen(name); pe++)
00549         *pe = ' ';
00550 
00551     SKIPSPACE(pe);
00552 
00553     if (*pe != '(') {
00554         rpmError(RPMERR_BADSPEC, _("Missing '(' in %s %s\n"), name, pe);
00555         fl->processingFailed = 1;
00556         return RPMERR_BADSPEC;
00557     }
00558 
00559     /* Bracket %*attr args */
00560     *pe++ = ' ';
00561     for (p = pe; *pe && *pe != ')'; pe++)
00562         {};
00563 
00564     if (ret_ar == &(fl->def_ar)) {      /* %defattr */
00565         q = pe;
00566         q++;
00567         SKIPSPACE(q);
00568         if (*q != '\0') {
00569             rpmError(RPMERR_BADSPEC,
00570                      _("Non-white space follows %s(): %s\n"), name, q);
00571             fl->processingFailed = 1;
00572             return RPMERR_BADSPEC;
00573         }
00574     }
00575 
00576     /* Localize. Erase parsed string */
00577     q = alloca((pe-p) + 1);
00578     strncpy(q, p, pe-p);
00579     q[pe-p] = '\0';
00580     while (p <= pe)
00581         *p++ = ' ';
00582 
00583     nullAttrRec(ar);
00584 
00585     p = q; SKIPWHITE(p);
00586     if (*p != '\0') {
00587         pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
00588         ar->ar_fmodestr = p;
00589         p = pe; SKIPWHITE(p);
00590     }
00591     if (*p != '\0') {
00592         pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
00593         ar->ar_user = p;
00594         p = pe; SKIPWHITE(p);
00595     }
00596     if (*p != '\0') {
00597         pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
00598         ar->ar_group = p;
00599         p = pe; SKIPWHITE(p);
00600     }
00601     if (*p != '\0' && ret_ar == &(fl->def_ar)) {        /* %defattr */
00602         pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
00603         ar->ar_dmodestr = p;
00604         p = pe; SKIPWHITE(p);
00605     }
00606 
00607     if (!(ar->ar_fmodestr && ar->ar_user && ar->ar_group) || *p != '\0') {
00608         rpmError(RPMERR_BADSPEC, _("Bad syntax: %s(%s)\n"), name, q);
00609         fl->processingFailed = 1;
00610         return RPMERR_BADSPEC;
00611     }
00612 
00613     /* Do a quick test on the mode argument and adjust for "-" */
00614     if (ar->ar_fmodestr && !isAttrDefault(ar->ar_fmodestr)) {
00615         unsigned int ui;
00616         x = sscanf(ar->ar_fmodestr, "%o", &ui);
00617         if ((x == 0) || (ar->ar_fmode & ~MYALLPERMS)) {
00618             rpmError(RPMERR_BADSPEC, _("Bad mode spec: %s(%s)\n"), name, q);
00619             fl->processingFailed = 1;
00620             return RPMERR_BADSPEC;
00621         }
00622         ar->ar_fmode = ui;
00623     } else
00624         ar->ar_fmodestr = NULL;
00625 
00626     if (ar->ar_dmodestr && !isAttrDefault(ar->ar_dmodestr)) {
00627         unsigned int ui;
00628         x = sscanf(ar->ar_dmodestr, "%o", &ui);
00629         if ((x == 0) || (ar->ar_dmode & ~MYALLPERMS)) {
00630             rpmError(RPMERR_BADSPEC, _("Bad dirmode spec: %s(%s)\n"), name, q);
00631             fl->processingFailed = 1;
00632             return RPMERR_BADSPEC;
00633         }
00634         ar->ar_dmode = ui;
00635     } else
00636         ar->ar_dmodestr = NULL;
00637 
00638     if (!(ar->ar_user && !isAttrDefault(ar->ar_user)))
00639         ar->ar_user = NULL;
00640 
00641     if (!(ar->ar_group && !isAttrDefault(ar->ar_group)))
00642         ar->ar_group = NULL;
00643 
00644     dupAttrRec(ar, ret_ar);
00645 
00646     /* XXX fix all this */
00647     *specdFlags |= SPECD_UID | SPECD_GID | SPECD_FILEMODE | SPECD_DIRMODE;
00648     
00649     return 0;
00650 }
00651 /*@=boundswrite@*/
00652 
00659 /*@-boundswrite@*/
00660 static int parseForConfig(char * buf, FileList fl)
00661         /*@modifies buf, fl->processingFailed, fl->currentFlags @*/
00662 {
00663     char *p, *pe, *q;
00664     const char *name;
00665 
00666     if ((p = strstr(buf, (name = "%config"))) == NULL)
00667         return 0;
00668 
00669     fl->currentFlags |= RPMFILE_CONFIG;
00670 
00671     /* Erase "%config" token. */
00672     for (pe = p; (pe-p) < strlen(name); pe++)
00673         *pe = ' ';
00674     SKIPSPACE(pe);
00675     if (*pe != '(')
00676         return 0;
00677 
00678     /* Bracket %config args */
00679     *pe++ = ' ';
00680     for (p = pe; *pe && *pe != ')'; pe++)
00681         {};
00682 
00683     if (*pe == '\0') {
00684         rpmError(RPMERR_BADSPEC, _("Missing ')' in %s(%s\n"), name, p);
00685         fl->processingFailed = 1;
00686         return RPMERR_BADSPEC;
00687     }
00688 
00689     /* Localize. Erase parsed string. */
00690     q = alloca((pe-p) + 1);
00691     strncpy(q, p, pe-p);
00692     q[pe-p] = '\0';
00693     while (p <= pe)
00694         *p++ = ' ';
00695 
00696     for (p = q; *p != '\0'; p = pe) {
00697         SKIPWHITE(p);
00698         if (*p == '\0')
00699             break;
00700         pe = p;
00701         SKIPNONWHITE(pe);
00702         if (*pe != '\0')
00703             *pe++ = '\0';
00704         if (!strcmp(p, "missingok")) {
00705             fl->currentFlags |= RPMFILE_MISSINGOK;
00706         } else if (!strcmp(p, "noreplace")) {
00707             fl->currentFlags |= RPMFILE_NOREPLACE;
00708         } else {
00709             rpmError(RPMERR_BADSPEC, _("Invalid %s token: %s\n"), name, p);
00710             fl->processingFailed = 1;
00711             return RPMERR_BADSPEC;
00712         }
00713     }
00714 
00715     return 0;
00716 }
00717 /*@=boundswrite@*/
00718 
00721 static int langCmp(const void * ap, const void * bp)
00722         /*@*/
00723 {
00724 /*@-boundsread@*/
00725     return strcmp(*(const char **)ap, *(const char **)bp);
00726 /*@=boundsread@*/
00727 }
00728 
00735 /*@-bounds@*/
00736 static int parseForLang(char * buf, FileList fl)
00737         /*@modifies buf, fl->processingFailed,
00738                 fl->currentLangs, fl->nLangs @*/
00739 {
00740     char *p, *pe, *q;
00741     const char *name;
00742 
00743   while ((p = strstr(buf, (name = "%lang"))) != NULL) {
00744 
00745     for (pe = p; (pe-p) < strlen(name); pe++)
00746         *pe = ' ';
00747     SKIPSPACE(pe);
00748 
00749     if (*pe != '(') {
00750         rpmError(RPMERR_BADSPEC, _("Missing '(' in %s %s\n"), name, pe);
00751         fl->processingFailed = 1;
00752         return RPMERR_BADSPEC;
00753     }
00754 
00755     /* Bracket %lang args */
00756     *pe++ = ' ';
00757     for (pe = p; *pe && *pe != ')'; pe++)
00758         {};
00759 
00760     if (*pe == '\0') {
00761         rpmError(RPMERR_BADSPEC, _("Missing ')' in %s(%s\n"), name, p);
00762         fl->processingFailed = 1;
00763         return RPMERR_BADSPEC;
00764     }
00765 
00766     /* Localize. Erase parsed string. */
00767     q = alloca((pe-p) + 1);
00768     strncpy(q, p, pe-p);
00769     q[pe-p] = '\0';
00770     while (p <= pe)
00771         *p++ = ' ';
00772 
00773     /* Parse multiple arguments from %lang */
00774     for (p = q; *p != '\0'; p = pe) {
00775         char *newp;
00776         size_t np;
00777         int i;
00778 
00779         SKIPWHITE(p);
00780         pe = p;
00781         SKIPNONWHITE(pe);
00782 
00783         np = pe - p;
00784         
00785         /* Sanity check on locale lengths */
00786         if (np < 1 || (np == 1 && *p != 'C') || np >= 32) {
00787             rpmError(RPMERR_BADSPEC,
00788                 _("Unusual locale length: \"%.*s\" in %%lang(%s)\n"),
00789                 (int)np, p, q);
00790             fl->processingFailed = 1;
00791             return RPMERR_BADSPEC;
00792         }
00793 
00794         /* Check for duplicate locales */
00795         if (fl->currentLangs != NULL)
00796         for (i = 0; i < fl->nLangs; i++) {
00797             if (strncmp(fl->currentLangs[i], p, np))
00798                 /*@innercontinue@*/ continue;
00799             rpmError(RPMERR_BADSPEC, _("Duplicate locale %.*s in %%lang(%s)\n"),
00800                 (int)np, p, q);
00801             fl->processingFailed = 1;
00802             return RPMERR_BADSPEC;
00803         }
00804 
00805         /* Add new locale */
00806         fl->currentLangs = xrealloc(fl->currentLangs,
00807                                 (fl->nLangs + 1) * sizeof(*fl->currentLangs));
00808         newp = xmalloc( np+1 );
00809         strncpy(newp, p, np);
00810         newp[np] = '\0';
00811         fl->currentLangs[fl->nLangs++] = newp;
00812         if (*pe == ',') pe++;   /* skip , if present */
00813     }
00814   }
00815 
00816     /* Insure that locales are sorted. */
00817     if (fl->currentLangs)
00818         qsort(fl->currentLangs, fl->nLangs, sizeof(*fl->currentLangs), langCmp);
00819 
00820     return 0;
00821 }
00822 /*@=bounds@*/
00823 
00826 /*@-boundswrite@*/
00827 static int parseForRegexLang(const char * fileName, /*@out@*/ char ** lang)
00828         /*@globals rpmGlobalMacroContext, h_errno @*/
00829         /*@modifies *lang, rpmGlobalMacroContext @*/
00830 {
00831     static int initialized = 0;
00832     static int hasRegex = 0;
00833     static regex_t compiledPatt;
00834     static char buf[BUFSIZ];
00835     int x;
00836     regmatch_t matches[2];
00837     const char *s;
00838 
00839     if (! initialized) {
00840         const char *patt = rpmExpand("%{?_langpatt}", NULL);
00841         int rc = 0;
00842         if (!(patt && *patt != '\0'))
00843             rc = 1;
00844         else if (regcomp(&compiledPatt, patt, REG_EXTENDED))
00845             rc = -1;
00846         patt = _free(patt);
00847         if (rc)
00848             return rc;
00849         hasRegex = 1;
00850         initialized = 1;
00851     }
00852     
00853     memset(matches, 0, sizeof(matches));
00854     if (! hasRegex || regexec(&compiledPatt, fileName, 2, matches, REG_NOTEOL))
00855         return 1;
00856 
00857     /* Got match */
00858     s = fileName + matches[1].rm_eo - 1;
00859     x = matches[1].rm_eo - matches[1].rm_so;
00860     buf[x] = '\0';
00861     while (x) {
00862         buf[--x] = *s--;
00863     }
00864     if (lang)
00865         *lang = buf;
00866     return 0;
00867 }
00868 /*@=boundswrite@*/
00869 
00872 /*@-exportlocal -exportheadervar@*/
00873 /*@unchecked@*/
00874 VFA_t virtualFileAttributes[] = {
00875         { "%dir",       0,      0 },    /* XXX why not RPMFILE_DIR? */
00876         { "%doc",       0,      RPMFILE_DOC },
00877         { "%ghost",     0,      RPMFILE_GHOST },
00878         { "%exclude",   0,      RPMFILE_EXCLUDE },
00879         { "%readme",    0,      RPMFILE_README },
00880         { "%license",   0,      RPMFILE_LICENSE },
00881         { "%pubkey",    0,      RPMFILE_PUBKEY },
00882         { "%policy",    0,      RPMFILE_POLICY },
00883         { "%optional",  0,      RPMFILE_OPTIONAL },
00884 
00885 #if WHY_NOT
00886         { "%icon",      0,      RPMFILE_ICON },
00887         { "%spec",      0,      RPMFILE_SPEC },
00888         { "%config",    0,      RPMFILE_CONFIG },
00889         { "%missingok", 0,      RPMFILE_CONFIG|RPMFILE_MISSINGOK },
00890         { "%noreplace", 0,      RPMFILE_CONFIG|RPMFILE_NOREPLACE },
00891 #endif
00892 
00893         { NULL, 0, 0 }
00894 };
00895 /*@=exportlocal =exportheadervar@*/
00896 
00906 /*@-boundswrite@*/
00907 static int parseForSimple(/*@unused@*/Spec spec, Package pkg, char * buf,
00908                           FileList fl, /*@out@*/ const char ** fileName)
00909         /*@globals rpmGlobalMacroContext, h_errno @*/
00910         /*@modifies buf, fl->processingFailed, *fileName,
00911                 fl->currentFlags,
00912                 fl->docDirs, fl->docDirCount, fl->isDir,
00913                 fl->passedSpecialDoc, fl->isSpecialDoc,
00914                 pkg->specialDoc, rpmGlobalMacroContext @*/
00915 {
00916     char *s, *t;
00917     int res, specialDoc = 0;
00918     char specialDocBuf[BUFSIZ];
00919 
00920     specialDocBuf[0] = '\0';
00921     *fileName = NULL;
00922     res = 0;
00923 
00924     t = buf;
00925     while ((s = strtokWithQuotes(t, " \t\n")) != NULL) {
00926         t = NULL;
00927         if (!strcmp(s, "%docdir")) {
00928             s = strtokWithQuotes(NULL, " \t\n");
00929             if (fl->docDirCount == MAXDOCDIR) {
00930                 rpmError(RPMERR_INTERNAL, _("Hit limit for %%docdir\n"));
00931                 fl->processingFailed = 1;
00932                 res = 1;
00933             }
00934         
00935             if (s != NULL)
00936                 fl->docDirs[fl->docDirCount++] = xstrdup(s);
00937             if (s == NULL || strtokWithQuotes(NULL, " \t\n")) {
00938                 rpmError(RPMERR_INTERNAL, _("Only one arg for %%docdir\n"));
00939                 fl->processingFailed = 1;
00940                 res = 1;
00941             }
00942             break;
00943         }
00944 #if defined(__LCLINT__)
00945         assert(s != NULL);
00946 #endif
00947 
00948     /* Set flags for virtual file attributes */
00949     {   VFA_t *vfa;
00950         for (vfa = virtualFileAttributes; vfa->attribute != NULL; vfa++) {
00951             if (strcmp(s, vfa->attribute))
00952                 /*@innercontinue@*/ continue;
00953             if (!vfa->flag) {
00954                 if (!strcmp(s, "%dir"))
00955                     fl->isDir = 1;      /* XXX why not RPMFILE_DIR? */
00956             } else {
00957                 if (vfa->not)
00958                     fl->currentFlags &= ~vfa->flag;
00959                 else
00960                     fl->currentFlags |= vfa->flag;
00961             }
00962 
00963             /*@innerbreak@*/ break;
00964         }
00965         /* if we got an attribute, continue with next token */
00966         if (vfa->attribute != NULL)
00967             continue;
00968     }
00969 
00970         if (*fileName) {
00971             /* We already got a file -- error */
00972             rpmError(RPMERR_BADSPEC, _("Two files on one line: %s\n"),
00973                 *fileName);
00974             fl->processingFailed = 1;
00975             res = 1;
00976         }
00977 
00978         /*@-branchstate@*/
00979         if (*s != '/') {
00980             if (fl->currentFlags & RPMFILE_DOC) {
00981                 specialDoc = 1;
00982                 strcat(specialDocBuf, " ");
00983                 strcat(specialDocBuf, s);
00984             } else
00985             if (fl->currentFlags & (RPMFILE_POLICY|RPMFILE_PUBKEY|RPMFILE_ICON))
00986             {
00987                 *fileName = s;
00988             } else {
00989                 /* not in %doc, does not begin with / -- error */
00990                 rpmError(RPMERR_BADSPEC,
00991                     _("File must begin with \"/\": %s\n"), s);
00992                 fl->processingFailed = 1;
00993                 res = 1;
00994             }
00995         } else {
00996             *fileName = s;
00997         }
00998         /*@=branchstate@*/
00999     }
01000 
01001     if (specialDoc) {
01002         if (*fileName || (fl->currentFlags & ~(RPMFILE_DOC))) {
01003             rpmError(RPMERR_BADSPEC,
01004                      _("Can't mix special %%doc with other forms: %s\n"),
01005                      (*fileName ? *fileName : ""));
01006             fl->processingFailed = 1;
01007             res = 1;
01008         } else {
01009         /* XXX WATCHOUT: buf is an arg */
01010            {
01011                 static char *_docdir_fmt= 0;
01012                 static int oneshot = 0;
01013                 const char *ddir, *fmt, *errstr;
01014                 if (!oneshot) {
01015                     _docdir_fmt = rpmExpand("%{?_docdir_fmt}", NULL);
01016                     if (!_docdir_fmt || !*_docdir_fmt)
01017                         _docdir_fmt = "%{NAME}-%{VERSION}";
01018                     oneshot = 1;
01019                 }
01020                 fmt = headerSprintf(pkg->header, _docdir_fmt, rpmTagTable, rpmHeaderFormats, &errstr);
01021                 if (!fmt) {
01022                     rpmError(RPMERR_BADSPEC, _("illegal _docdir_fmt: %s\n"), errstr);
01023                     fl->processingFailed = 1;
01024                     res = 1;
01025                 }
01026                 ddir = rpmGetPath("%{_docdir}/", fmt, NULL);
01027                 strcpy(buf, ddir);
01028                 ddir = _free(ddir);
01029             }
01030 
01031         /* XXX FIXME: this is easy to do as macro expansion */
01032 
01033             if (! fl->passedSpecialDoc) {
01034                 pkg->specialDoc = newStringBuf();
01035                 appendStringBuf(pkg->specialDoc, "DOCDIR=$RPM_BUILD_ROOT");
01036                 appendLineStringBuf(pkg->specialDoc, buf);
01037                 appendLineStringBuf(pkg->specialDoc, "export DOCDIR");
01038                 appendLineStringBuf(pkg->specialDoc, "rm -rf $DOCDIR");
01039                 appendLineStringBuf(pkg->specialDoc, MKDIR_P " $DOCDIR");
01040 
01041                 /*@-temptrans@*/
01042                 *fileName = buf;
01043                 /*@=temptrans@*/
01044                 fl->passedSpecialDoc = 1;
01045                 fl->isSpecialDoc = 1;
01046             }
01047 
01048             appendStringBuf(pkg->specialDoc, "cp -pr ");
01049             appendStringBuf(pkg->specialDoc, specialDocBuf);
01050             appendLineStringBuf(pkg->specialDoc, " $DOCDIR");
01051         }
01052     }
01053 
01054     return res;
01055 }
01056 /*@=boundswrite@*/
01057 
01060 static int compareFileListRecs(const void * ap, const void * bp)        /*@*/
01061 {
01062     const char *a = ((FileListRec)ap)->fileURL;
01063     const char *b = ((FileListRec)bp)->fileURL;
01064     return strcmp(a, b);
01065 }
01066 
01073 static int isDoc(FileList fl, const char * fileName)    /*@*/
01074 {
01075     int x = fl->docDirCount;
01076     size_t k, l;
01077 
01078     k = strlen(fileName);
01079     while (x--) {
01080         l = strlen(fl->docDirs[x]);
01081         if (l < k && strncmp(fileName, fl->docDirs[x], l) == 0 && fileName[l] == '/')
01082             return 1;
01083     }
01084     return 0;
01085 }
01086 
01093 static int checkHardLinks(FileList fl)
01094         /*@*/
01095 {
01096     FileListRec ilp, jlp;
01097     int i, j;
01098 
01099     for (i = 0;  i < fl->fileListRecsUsed; i++) {
01100         ilp = fl->fileList + i;
01101         if (!(S_ISREG(ilp->fl_mode) && ilp->fl_nlink > 1))
01102             continue;
01103 
01104         for (j = i + 1; j < fl->fileListRecsUsed; j++) {
01105             jlp = fl->fileList + j;
01106             if (!S_ISREG(jlp->fl_mode))
01107                 /*@innercontinue@*/ continue;
01108             if (ilp->fl_nlink != jlp->fl_nlink)
01109                 /*@innercontinue@*/ continue;
01110             if (ilp->fl_ino != jlp->fl_ino)
01111                 /*@innercontinue@*/ continue;
01112             if (ilp->fl_dev != jlp->fl_dev)
01113                 /*@innercontinue@*/ continue;
01114             return 1;
01115         }
01116     }
01117     return 0;
01118 }
01119 
01129 /*@-bounds@*/
01130 static void genCpioListAndHeader(/*@partial@*/ FileList fl,
01131                 rpmfi * fip, Header h, int isSrc)
01132         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
01133         /*@modifies h, *fip, fl->processingFailed, fl->fileList,
01134                 rpmGlobalMacroContext, fileSystem, internalState @*/
01135 {
01136     int _addDotSlash = !(isSrc || rpmExpandNumeric("%{_noPayloadPrefix}"));
01137     int apathlen = 0;
01138     int dpathlen = 0;
01139     int skipLen = 0;
01140     rpmsx sx = NULL;
01141     const char * sxfn;
01142     size_t fnlen;
01143     FileListRec flp;
01144     char buf[BUFSIZ];
01145     int i;
01146     
01147     /* Sort the big list */
01148     qsort(fl->fileList, fl->fileListRecsUsed,
01149           sizeof(*(fl->fileList)), compareFileListRecs);
01150     
01151     /* Generate the header. */
01152     if (! isSrc) {
01153         skipLen = 1;
01154         if (fl->prefix)
01155             skipLen += strlen(fl->prefix);
01156     }
01157 
01158     sxfn = rpmGetPath("%{?_build_file_context_path}", NULL);
01159     if (sxfn != NULL && *sxfn != '\0')
01160         sx = rpmsxNew(sxfn);
01161 
01162     for (i = 0, flp = fl->fileList; i < fl->fileListRecsUsed; i++, flp++) {
01163         const char *s;
01164 
01165         /* Merge duplicate entries. */
01166         while (i < (fl->fileListRecsUsed - 1) &&
01167             !strcmp(flp->fileURL, flp[1].fileURL)) {
01168 
01169             /* Two entries for the same file found, merge the entries. */
01170             /* Note that an %exclude is a duplication of a file reference */
01171 
01172             /* file flags */
01173             flp[1].flags |= flp->flags; 
01174 
01175             if (!(flp[1].flags & RPMFILE_EXCLUDE))
01176                 rpmMessage(RPMMESS_WARNING, _("File listed twice: %s\n"),
01177                         flp->fileURL);
01178    
01179             /* file mode */
01180             if (S_ISDIR(flp->fl_mode)) {
01181                 if ((flp[1].specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)) <
01182                     (flp->specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)))
01183                         flp[1].fl_mode = flp->fl_mode;
01184             } else {
01185                 if ((flp[1].specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)) <
01186                     (flp->specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)))
01187                         flp[1].fl_mode = flp->fl_mode;
01188             }
01189 
01190             /* uid */
01191             if ((flp[1].specdFlags & (SPECD_UID | SPECD_DEFUID)) <
01192                 (flp->specdFlags & (SPECD_UID | SPECD_DEFUID)))
01193             {
01194                 flp[1].fl_uid = flp->fl_uid;
01195                 flp[1].uname = flp->uname;
01196             }
01197 
01198             /* gid */
01199             if ((flp[1].specdFlags & (SPECD_GID | SPECD_DEFGID)) <
01200                 (flp->specdFlags & (SPECD_GID | SPECD_DEFGID)))
01201             {
01202                 flp[1].fl_gid = flp->fl_gid;
01203                 flp[1].gname = flp->gname;
01204             }
01205 
01206             /* verify flags */
01207             if ((flp[1].specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)) <
01208                 (flp->specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)))
01209                     flp[1].verifyFlags = flp->verifyFlags;
01210 
01211             /* XXX to-do: language */
01212 
01213             flp++; i++;
01214         }
01215 
01216         /* Skip files that were marked with %exclude. */
01217         if (flp->flags & RPMFILE_EXCLUDE) continue;
01218 
01219         /* Omit '/' and/or URL prefix, leave room for "./" prefix */
01220         apathlen += (strlen(flp->fileURL) - skipLen + (_addDotSlash ? 3 : 1));
01221 
01222         /* Leave room for both dirname and basename NUL's */
01223         dpathlen += (strlen(flp->diskURL) + 2);
01224 
01225         /*
01226          * Make the header, the OLDFILENAMES will get converted to a 
01227          * compressed file list write before we write the actual package to
01228          * disk.
01229          */
01230         (void) headerAddOrAppendEntry(h, RPMTAG_OLDFILENAMES, RPM_STRING_ARRAY_TYPE,
01231                                &(flp->fileURL), 1);
01232 
01233 /*@-sizeoftype@*/
01234       if (sizeof(flp->fl_size) != sizeof(uint_32)) {
01235         uint_32 psize = (uint_32)flp->fl_size;
01236         (void) headerAddOrAppendEntry(h, RPMTAG_FILESIZES, RPM_INT32_TYPE,
01237                                &(psize), 1);
01238       } else {
01239         (void) headerAddOrAppendEntry(h, RPMTAG_FILESIZES, RPM_INT32_TYPE,
01240                                &(flp->fl_size), 1);
01241       }
01242         (void) headerAddOrAppendEntry(h, RPMTAG_FILEUSERNAME, RPM_STRING_ARRAY_TYPE,
01243                                &(flp->uname), 1);
01244         (void) headerAddOrAppendEntry(h, RPMTAG_FILEGROUPNAME, RPM_STRING_ARRAY_TYPE,
01245                                &(flp->gname), 1);
01246       if (sizeof(flp->fl_mtime) != sizeof(uint_32)) {
01247         uint_32 mtime = (uint_32)flp->fl_mtime;
01248         (void) headerAddOrAppendEntry(h, RPMTAG_FILEMTIMES, RPM_INT32_TYPE,
01249                                &(mtime), 1);
01250       } else {
01251         (void) headerAddOrAppendEntry(h, RPMTAG_FILEMTIMES, RPM_INT32_TYPE,
01252                                &(flp->fl_mtime), 1);
01253       }
01254       if (sizeof(flp->fl_mode) != sizeof(uint_16)) {
01255         uint_16 pmode = (uint_16)flp->fl_mode;
01256         (void) headerAddOrAppendEntry(h, RPMTAG_FILEMODES, RPM_INT16_TYPE,
01257                                &(pmode), 1);
01258       } else {
01259         (void) headerAddOrAppendEntry(h, RPMTAG_FILEMODES, RPM_INT16_TYPE,
01260                                &(flp->fl_mode), 1);
01261       }
01262       if (sizeof(flp->fl_rdev) != sizeof(uint_16)) {
01263         uint_16 prdev = (uint_16)flp->fl_rdev;
01264         (void) headerAddOrAppendEntry(h, RPMTAG_FILERDEVS, RPM_INT16_TYPE,
01265                                &(prdev), 1);
01266       } else {
01267         (void) headerAddOrAppendEntry(h, RPMTAG_FILERDEVS, RPM_INT16_TYPE,
01268                                &(flp->fl_rdev), 1);
01269       }
01270       if (sizeof(flp->fl_dev) != sizeof(uint_32)) {
01271         uint_32 pdevice = (uint_32)flp->fl_dev;
01272         (void) headerAddOrAppendEntry(h, RPMTAG_FILEDEVICES, RPM_INT32_TYPE,
01273                                &(pdevice), 1);
01274       } else {
01275         (void) headerAddOrAppendEntry(h, RPMTAG_FILEDEVICES, RPM_INT32_TYPE,
01276                                &(flp->fl_dev), 1);
01277       }
01278       if (sizeof(flp->fl_ino) != sizeof(uint_32)) {
01279         uint_32 ino = (uint_32)flp->fl_ino;
01280         (void) headerAddOrAppendEntry(h, RPMTAG_FILEINODES, RPM_INT32_TYPE,
01281                                 &(ino), 1);
01282       } else {
01283         (void) headerAddOrAppendEntry(h, RPMTAG_FILEINODES, RPM_INT32_TYPE,
01284                                 &(flp->fl_ino), 1);
01285       }
01286 /*@=sizeoftype@*/
01287 
01288         (void) headerAddOrAppendEntry(h, RPMTAG_FILELANGS, RPM_STRING_ARRAY_TYPE,
01289                                &(flp->langs),  1);
01290         
01291         /* We used to add these, but they should not be needed */
01292         /* (void) headerAddOrAppendEntry(h, RPMTAG_FILEUIDS,
01293          *                 RPM_INT32_TYPE, &(flp->fl_uid), 1);
01294          * (void) headerAddOrAppendEntry(h, RPMTAG_FILEGIDS,
01295          *                 RPM_INT32_TYPE, &(flp->fl_gid), 1);
01296          */
01297         
01298         buf[0] = '\0';
01299         if (S_ISREG(flp->fl_mode))
01300             (void) domd5(flp->diskURL, buf, 1, NULL);
01301         s = buf;
01302         (void) headerAddOrAppendEntry(h, RPMTAG_FILEMD5S, RPM_STRING_ARRAY_TYPE,
01303                                &s, 1);
01304         
01305         buf[0] = '\0';
01306         if (S_ISLNK(flp->fl_mode)) {
01307             buf[Readlink(flp->diskURL, buf, BUFSIZ)] = '\0';
01308             if (fl->buildRootURL) {
01309                 const char * buildRoot;
01310                 (void) urlPath(fl->buildRootURL, &buildRoot);
01311 
01312                 if (buf[0] == '/' && strcmp(buildRoot, "/") &&
01313                     !strncmp(buf, buildRoot, strlen(buildRoot))) {
01314                      rpmError(RPMERR_BADSPEC,
01315                                 _("Symlink points to BuildRoot: %s -> %s\n"),
01316                                 flp->fileURL, buf);
01317                     fl->processingFailed = 1;
01318                 }
01319             }
01320         }
01321         s = buf;
01322         (void) headerAddOrAppendEntry(h, RPMTAG_FILELINKTOS, RPM_STRING_ARRAY_TYPE,
01323                                &s, 1);
01324         
01325         if (flp->flags & RPMFILE_GHOST) {
01326             flp->verifyFlags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE |
01327                                 RPMVERIFY_LINKTO | RPMVERIFY_MTIME);
01328         }
01329         (void) headerAddOrAppendEntry(h, RPMTAG_FILEVERIFYFLAGS, RPM_INT32_TYPE,
01330                                &(flp->verifyFlags), 1);
01331         
01332         if (!isSrc && isDoc(fl, flp->fileURL))
01333             flp->flags |= RPMFILE_DOC;
01334         /* XXX Should directories have %doc/%config attributes? (#14531) */
01335         if (S_ISDIR(flp->fl_mode))
01336             flp->flags &= ~(RPMFILE_CONFIG|RPMFILE_DOC);
01337 
01338         (void) headerAddOrAppendEntry(h, RPMTAG_FILEFLAGS, RPM_INT32_TYPE,
01339                                &(flp->flags), 1);
01340 
01341         /* Add file security context to package. */
01342 /*@-branchstate@*/
01343         if (sx != NULL) {
01344             mode_t fmode = (uint_16)flp->fl_mode;
01345             s = rpmsxFContext(sx, flp->fileURL, fmode);
01346             if (s == NULL) s = "";
01347             (void) headerAddOrAppendEntry(h, RPMTAG_FILECONTEXTS, RPM_STRING_ARRAY_TYPE,
01348                                &s, 1);
01349         }
01350 /*@=branchstate@*/
01351 
01352     }
01353     sx = rpmsxFree(sx);
01354     sxfn = _free(sxfn);
01355 
01356     (void) headerAddEntry(h, RPMTAG_SIZE, RPM_INT32_TYPE,
01357                    &(fl->totalFileSize), 1);
01358 
01359     if (_addDotSlash)
01360         (void) rpmlibNeedsFeature(h, "PayloadFilesHavePrefix", "4.0-1");
01361 
01362     /* Choose how filenames are represented. */
01363     if (_noDirTokens)
01364         expandFilelist(h);
01365     else {
01366         compressFilelist(h);
01367         /* Binary packages with dirNames cannot be installed by legacy rpm. */
01368         (void) rpmlibNeedsFeature(h, "CompressedFileNames", "3.0.4-1");
01369     }
01370 
01371   { int scareMem = 0;
01372     rpmts ts = NULL;    /* XXX FIXME drill rpmts ts all the way down here */
01373     rpmfi fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, scareMem);
01374     char * a, * d;
01375 
01376     if (fi == NULL) return;             /* XXX can't happen */
01377 
01378 /*@-onlytrans@*/
01379     fi->te = xcalloc(1, sizeof(*fi->te));
01380 /*@=onlytrans@*/
01381     fi->te->type = TR_ADDED;
01382 
01383     fi->dnl = _free(fi->dnl);
01384     fi->bnl = _free(fi->bnl);
01385     if (!scareMem) fi->dil = _free(fi->dil);
01386 
01387     fi->dnl = xmalloc(fi->fc * sizeof(*fi->dnl) + dpathlen);
01388     d = (char *)(fi->dnl + fi->fc);
01389     *d = '\0';
01390 
01391     fi->bnl = xmalloc(fi->fc * (sizeof(*fi->bnl) + sizeof(*fi->dil)));
01392 /*@-dependenttrans@*/ /* FIX: artifact of spoofing headerGetEntry */
01393     fi->dil = (!scareMem)
01394         ? xcalloc(sizeof(*fi->dil), fi->fc)
01395         : (int *)(fi->bnl + fi->fc);
01396 /*@=dependenttrans@*/
01397 
01398     fi->apath = xmalloc(fi->fc * sizeof(*fi->apath) + apathlen + 1);
01399     a = (char *)(fi->apath + fi->fc);
01400     *a = '\0';
01401 
01402     fi->actions = xcalloc(sizeof(*fi->actions), fi->fc);
01403     fi->fmapflags = xcalloc(sizeof(*fi->fmapflags), fi->fc);
01404     fi->astriplen = 0;
01405     if (fl->buildRootURL)
01406         fi->astriplen = strlen(fl->buildRootURL);
01407     fi->striplen = 0;
01408     fi->fuser = NULL;
01409     fi->fgroup = NULL;
01410 
01411     /* Make the cpio list */
01412     if (fi->dil != NULL)        /* XXX can't happen */
01413     for (i = 0, flp = fl->fileList; i < fi->fc; i++, flp++) {
01414         char * b;
01415 
01416         /* Skip (possible) duplicate file entries, use last entry info. */
01417         while (((flp - fl->fileList) < (fl->fileListRecsUsed - 1)) &&
01418                 !strcmp(flp->fileURL, flp[1].fileURL))
01419             flp++;
01420 
01421         if (flp->flags & RPMFILE_EXCLUDE) {
01422             i--;
01423             continue;
01424         }
01425 
01426         if ((fnlen = strlen(flp->diskURL) + 1) > fi->fnlen)
01427             fi->fnlen = fnlen;
01428 
01429         /* Create disk directory and base name. */
01430         fi->dil[i] = i;
01431 /*@-dependenttrans@*/ /* FIX: artifact of spoofing headerGetEntry */
01432         fi->dnl[fi->dil[i]] = d;
01433 /*@=dependenttrans@*/
01434         d = stpcpy(d, flp->diskURL);
01435 
01436         /* Make room for the dirName NUL, find start of baseName. */
01437         for (b = d; b > fi->dnl[fi->dil[i]] && *b != '/'; b--)
01438             b[1] = b[0];
01439         b++;            /* dirname's end in '/' */
01440         *b++ = '\0';    /* terminate dirname, b points to basename */
01441         fi->bnl[i] = b;
01442         d += 2;         /* skip both dirname and basename NUL's */
01443 
01444         /* Create archive path, normally adding "./" */
01445         /*@-dependenttrans@*/   /* FIX: xstrdup? nah ... */
01446         fi->apath[i] = a;
01447         /*@=dependenttrans@*/
01448         if (_addDotSlash)
01449             a = stpcpy(a, "./");
01450         a = stpcpy(a, (flp->fileURL + skipLen));
01451         a++;            /* skip apath NUL */
01452 
01453         if (flp->flags & RPMFILE_GHOST) {
01454             fi->actions[i] = FA_SKIP;
01455             continue;
01456         }
01457         fi->actions[i] = FA_COPYOUT;
01458         fi->fmapflags[i] = CPIO_MAP_PATH |
01459                 CPIO_MAP_TYPE | CPIO_MAP_MODE | CPIO_MAP_UID | CPIO_MAP_GID;
01460         if (isSrc)
01461             fi->fmapflags[i] |= CPIO_FOLLOW_SYMLINKS;
01462 
01463     }
01464     /*@-branchstate -compdef@*/
01465     if (fip)
01466         *fip = fi;
01467     else
01468         fi = rpmfiFree(fi);
01469     /*@=branchstate =compdef@*/
01470   }
01471 }
01472 /*@=bounds@*/
01473 
01476 /*@-boundswrite@*/
01477 static /*@null@*/ FileListRec freeFileList(/*@only@*/ FileListRec fileList,
01478                         int count)
01479         /*@*/
01480 {
01481     while (count--) {
01482         fileList[count].diskURL = _free(fileList[count].diskURL);
01483         fileList[count].fileURL = _free(fileList[count].fileURL);
01484         fileList[count].langs = _free(fileList[count].langs);
01485     }
01486     fileList = _free(fileList);
01487     return NULL;
01488 }
01489 /*@=boundswrite@*/
01490 
01491 /* forward ref */
01492 static int recurseDir(FileList fl, const char * diskURL)
01493         /*@globals check_fileList, rpmGlobalMacroContext, h_errno,
01494                 fileSystem, internalState @*/
01495         /*@modifies *fl, fl->processingFailed,
01496                 fl->fileList, fl->fileListRecsAlloced, fl->fileListRecsUsed,
01497                 fl->totalFileSize, fl->fileCount, fl->inFtw, fl->isDir,
01498                 check_fileList, rpmGlobalMacroContext,
01499                 fileSystem, internalState @*/;
01500 
01508 /*@-boundswrite@*/
01509 static int addFile(FileList fl, const char * diskURL,
01510                 /*@null@*/ struct stat * statp)
01511         /*@globals check_fileList, rpmGlobalMacroContext, h_errno,
01512                 fileSystem, internalState @*/
01513         /*@modifies *statp, *fl, fl->processingFailed,
01514                 fl->fileList, fl->fileListRecsAlloced, fl->fileListRecsUsed,
01515                 fl->totalFileSize, fl->fileCount,
01516                 check_fileList, rpmGlobalMacroContext,
01517                 fileSystem, internalState @*/
01518 {
01519     const char *fileURL = diskURL;
01520     struct stat statbuf;
01521     mode_t fileMode;
01522     uid_t fileUid;
01523     gid_t fileGid;
01524     const char *fileUname;
01525     const char *fileGname;
01526     char *lang;
01527     
01528     /* Path may have prepended buildRootURL, so locate the original filename. */
01529     /*
01530      * XXX There are 3 types of entry into addFile:
01531      *
01532      *  From                    diskUrl                 statp
01533      *  =====================================================
01534      *  processBinaryFile       path                    NULL
01535      *  processBinaryFile       glob result path        NULL
01536      *  myftw                   path                    stat
01537      *
01538      */
01539     {   const char *fileName;
01540         (void) urlPath(fileURL, &fileName);
01541         if (fl->buildRootURL && strcmp(fl->buildRootURL, "/"))
01542             fileURL += strlen(fl->buildRootURL);
01543     }
01544 
01545     /* XXX make sure '/' can be packaged also */
01546     /*@-branchstate@*/
01547     if (*fileURL == '\0')
01548         fileURL = "/";
01549     /*@=branchstate@*/
01550 
01551     /* If we are using a prefix, validate the file */
01552     if (!fl->inFtw && fl->prefix) {
01553         const char *prefixTest;
01554         const char *prefixPtr = fl->prefix;
01555 
01556         (void) urlPath(fileURL, &prefixTest);
01557         while (*prefixPtr && *prefixTest && (*prefixTest == *prefixPtr)) {
01558             prefixPtr++;
01559             prefixTest++;
01560         }
01561         if (*prefixPtr || (*prefixTest && *prefixTest != '/')) {
01562             rpmError(RPMERR_BADSPEC, _("File doesn't match prefix (%s): %s\n"),
01563                      fl->prefix, fileURL);
01564             fl->processingFailed = 1;
01565             return RPMERR_BADSPEC;
01566         }
01567     }
01568 
01569     if (statp == NULL) {
01570         statp = &statbuf;
01571         memset(statp, 0, sizeof(*statp));
01572         if (fl->devtype) {
01573             time_t now = time(NULL);
01574 
01575             /* XXX hack up a stat structure for a %dev(...) directive. */
01576             statp->st_nlink = 1;
01577             statp->st_rdev =
01578                 ((fl->devmajor & 0xff) << 8) | (fl->devminor & 0xff);
01579             statp->st_dev = statp->st_rdev;
01580             statp->st_mode = (fl->devtype == 'b' ? S_IFBLK : S_IFCHR);
01581             statp->st_mode |= (fl->cur_ar.ar_fmode & 0777);
01582             statp->st_atime = now;
01583             statp->st_mtime = now;
01584             statp->st_ctime = now;
01585         } else if (Lstat(diskURL, statp)) {
01586             if(fl->currentFlags & RPMFILE_OPTIONAL) {
01587                 rpmMessage(RPMMESS_WARNING,
01588                     _("File not found: %s\n"), diskURL);
01589                 return 0;
01590             } else {
01591                 rpmError(RPMERR_BADSPEC, _("File not found: %s\n"), diskURL);
01592                 fl->processingFailed = 1;
01593                 return RPMERR_BADSPEC;
01594             }
01595         }
01596     }
01597 
01598     if ((! fl->isDir) && S_ISDIR(statp->st_mode)) {
01599 /*@-nullstate@*/ /* FIX: fl->buildRootURL may be NULL */
01600         return recurseDir(fl, diskURL);
01601 /*@=nullstate@*/
01602     }
01603 
01604     fileMode = statp->st_mode;
01605     fileUid = statp->st_uid;
01606     fileGid = statp->st_gid;
01607 
01608     if (S_ISDIR(fileMode) && fl->cur_ar.ar_dmodestr) {
01609         fileMode &= S_IFMT;
01610         fileMode |= fl->cur_ar.ar_dmode;
01611     } else if (fl->cur_ar.ar_fmodestr != NULL) {
01612         fileMode &= S_IFMT;
01613         fileMode |= fl->cur_ar.ar_fmode;
01614     }
01615     if (fl->cur_ar.ar_user) {
01616         fileUname = getUnameS(fl->cur_ar.ar_user);
01617     } else {
01618         fileUname = getUname(fileUid);
01619     }
01620     if (fl->cur_ar.ar_group) {
01621         fileGname = getGnameS(fl->cur_ar.ar_group);
01622     } else {
01623         fileGname = getGname(fileGid);
01624     }
01625         
01626     /* Default user/group to builder's user/group */
01627     if (fileUname == NULL)
01628         fileUname = getUname(getuid());
01629     if (fileGname == NULL)
01630         fileGname = getGname(getgid());
01631     
01632     /* S_XXX macro must be consistent with type in find call at check-files script */
01633     if (check_fileList && (S_ISREG(fileMode) || S_ISLNK(fileMode))) {
01634         appendStringBuf(check_fileList, diskURL);
01635         appendStringBuf(check_fileList, "\n");
01636     }
01637 
01638     /* Add to the file list */
01639     if (fl->fileListRecsUsed == fl->fileListRecsAlloced) {
01640         fl->fileListRecsAlloced += 128;
01641         fl->fileList = xrealloc(fl->fileList,
01642                         fl->fileListRecsAlloced * sizeof(*(fl->fileList)));
01643     }
01644             
01645     {   FileListRec flp = &fl->fileList[fl->fileListRecsUsed];
01646         int i;
01647 
01648         flp->fl_st = *statp;    /* structure assignment */
01649         flp->fl_mode = fileMode;
01650         flp->fl_uid = fileUid;
01651         flp->fl_gid = fileGid;
01652 
01653         flp->fileURL = xstrdup(fileURL);
01654         flp->diskURL = xstrdup(diskURL);
01655         flp->uname = fileUname;
01656         flp->gname = fileGname;
01657 
01658         if (fl->currentLangs && fl->nLangs > 0) {
01659             char * ncl;
01660             size_t nl = 0;
01661             
01662             for (i = 0; i < fl->nLangs; i++)
01663                 nl += strlen(fl->currentLangs[i]) + 1;
01664 
01665             flp->langs = ncl = xmalloc(nl);
01666             for (i = 0; i < fl->nLangs; i++) {
01667                 const char *ocl;
01668                 if (i)  *ncl++ = '|';
01669                 for (ocl = fl->currentLangs[i]; *ocl != '\0'; ocl++)
01670                         *ncl++ = *ocl;
01671                 *ncl = '\0';
01672             }
01673         } else if (! parseForRegexLang(fileURL, &lang)) {
01674             flp->langs = xstrdup(lang);
01675         } else {
01676             flp->langs = xstrdup("");
01677         }
01678 
01679         flp->flags = fl->currentFlags;
01680         flp->specdFlags = fl->currentSpecdFlags;
01681         flp->verifyFlags = fl->currentVerifyFlags;
01682 
01683         /* Hard links need be counted only once. */
01684         if (S_ISREG(flp->fl_mode) && flp->fl_nlink > 1) {
01685             FileListRec ilp;
01686             for (i = 0;  i < fl->fileListRecsUsed; i++) {
01687                 ilp = fl->fileList + i;
01688                 if (!S_ISREG(ilp->fl_mode))
01689                     continue;
01690                 if (flp->fl_nlink != ilp->fl_nlink)
01691                     continue;
01692                 if (flp->fl_ino != ilp->fl_ino)
01693                     continue;
01694                 if (flp->fl_dev != ilp->fl_dev)
01695                     continue;
01696                 break;
01697             }
01698         } else
01699             i = fl->fileListRecsUsed;
01700 
01701         if (!(flp->flags & RPMFILE_EXCLUDE) && S_ISREG(flp->fl_mode) && i >= fl->fileListRecsUsed) 
01702             fl->totalFileSize += flp->fl_size;
01703     }
01704 
01705     fl->fileListRecsUsed++;
01706     fl->fileCount++;
01707 
01708     return 0;
01709 }
01710 /*@=boundswrite@*/
01711 
01718 static int recurseDir(FileList fl, const char * diskURL)
01719 {
01720     char * ftsSet[2];
01721     FTS * ftsp;
01722     FTSENT * fts;
01723     int myFtsOpts = (FTS_COMFOLLOW | FTS_NOCHDIR | FTS_PHYSICAL);
01724     int rc = RPMERR_BADSPEC;
01725 
01726     fl->inFtw = 1;  /* Flag to indicate file has buildRootURL prefixed */
01727     fl->isDir = 1;  /* Keep it from following myftw() again         */
01728 
01729     ftsSet[0] = (char *) diskURL;
01730     ftsSet[1] = NULL;
01731     ftsp = Fts_open(ftsSet, myFtsOpts, NULL);
01732     while ((fts = Fts_read(ftsp)) != NULL) {
01733         switch (fts->fts_info) {
01734         case FTS_D:             /* preorder directory */
01735         case FTS_F:             /* regular file */
01736         case FTS_SL:            /* symbolic link */
01737         case FTS_SLNONE:        /* symbolic link without target */
01738         case FTS_DEFAULT:       /* none of the above */
01739             rc = addFile(fl, fts->fts_accpath, fts->fts_statp);
01740             /*@switchbreak@*/ break;
01741         case FTS_DOT:           /* dot or dot-dot */
01742         case FTS_DP:            /* postorder directory */
01743             rc = 0;
01744             /*@switchbreak@*/ break;
01745         case FTS_NS:            /* stat(2) failed */
01746         case FTS_DNR:           /* unreadable directory */
01747         case FTS_ERR:           /* error; errno is set */
01748         case FTS_DC:            /* directory that causes cycles */
01749         case FTS_NSOK:          /* no stat(2) requested */
01750         case FTS_INIT:          /* initialized only */
01751         case FTS_W:             /* whiteout object */
01752         default:
01753             rc = RPMERR_BADSPEC;
01754             /*@switchbreak@*/ break;
01755         }
01756         if (rc)
01757             break;
01758     }
01759     (void) Fts_close(ftsp);
01760 
01761     fl->isDir = 0;
01762     fl->inFtw = 0;
01763 
01764     return rc;
01765 }
01766 
01775 static int processMetadataFile(Package pkg, FileList fl, const char * fileURL,
01776                 rpmTag tag)
01777         /*@globals check_fileList, rpmGlobalMacroContext, h_errno,
01778                 fileSystem, internalState @*/
01779         /*@modifies pkg->header, *fl, fl->processingFailed,
01780                 fl->fileList, fl->fileListRecsAlloced, fl->fileListRecsUsed,
01781                 fl->totalFileSize, fl->fileCount,
01782                 check_fileList, rpmGlobalMacroContext,
01783                 fileSystem, internalState @*/
01784 {
01785     const char * buildURL = "%{_builddir}/%{?buildsubdir}/";
01786     const char * fn = NULL;
01787     const char * apkt = NULL;
01788     const unsigned char * pkt = NULL;
01789     ssize_t pktlen = 0;
01790     int absolute = 0;
01791     int rc = 1;
01792     int xx;
01793 
01794     (void) urlPath(fileURL, &fn);
01795     if (*fn == '/') {
01796         fn = rpmGenPath(fl->buildRootURL, NULL, fn);
01797         absolute = 1;
01798     } else
01799         fn = rpmGenPath(buildURL, NULL, fn);
01800 
01801 /*@-branchstate@*/
01802     switch (tag) {
01803     default:
01804         rpmError(RPMERR_BADSPEC, _("%s: can't load unknown tag (%d).\n"),
01805                 fn, tag);
01806         goto exit;
01807         /*@notreached@*/ break;
01808     case RPMTAG_PUBKEYS:
01809         if ((rc = pgpReadPkts(fn, &pkt, &pktlen)) <= 0) {
01810             rpmError(RPMERR_BADSPEC, _("%s: public key read failed.\n"), fn);
01811             goto exit;
01812         }
01813         if (rc != PGPARMOR_PUBKEY) {
01814             rpmError(RPMERR_BADSPEC, _("%s: not an armored public key.\n"), fn);
01815             goto exit;
01816         }
01817         apkt = pgpArmorWrap(PGPARMOR_PUBKEY, pkt, pktlen);
01818         break;
01819     case RPMTAG_POLICIES:
01820         if ((rc = rpmioSlurp(fn, &pkt, &pktlen)) != 0) {
01821             rpmError(RPMERR_BADSPEC, _("%s: *.te policy read failed.\n"), fn);
01822             goto exit;
01823         }
01824         apkt = (const char *) pkt;      /* XXX unsigned char */
01825         pkt = NULL;
01826         break;
01827     }
01828 /*@=branchstate@*/
01829 
01830     xx = headerAddOrAppendEntry(pkg->header, tag,
01831                 RPM_STRING_ARRAY_TYPE, &apkt, 1);
01832 
01833     rc = 0;
01834     if (absolute)
01835         rc = addFile(fl, fn, NULL);
01836 
01837 exit:
01838     apkt = _free(apkt);
01839     pkt = _free(pkt);
01840     fn = _free(fn);
01841     if (rc) {
01842         fl->processingFailed = 1;
01843         rc = RPMERR_BADSPEC;
01844     }
01845     return rc;
01846 }
01847 
01855 static int processBinaryFile(/*@unused@*/ Package pkg, FileList fl,
01856                 const char * fileURL)
01857         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
01858         /*@modifies *fl, fl->processingFailed,
01859                 fl->fileList, fl->fileListRecsAlloced, fl->fileListRecsUsed,
01860                 fl->totalFileSize, fl->fileCount,
01861                 rpmGlobalMacroContext, fileSystem, internalState @*/
01862 {
01863     int quote = 1;      /* XXX permit quoted glob characters. */
01864     int doGlob;
01865     const char *diskURL = NULL;
01866     int rc = 0;
01867     
01868     doGlob = Glob_pattern_p(fileURL, quote);
01869 
01870     /* Check that file starts with leading "/" */
01871     {   const char * fileName;
01872         (void) urlPath(fileURL, &fileName);
01873         if (*fileName != '/') {
01874             rpmError(RPMERR_BADSPEC, _("File needs leading \"/\": %s\n"),
01875                         fileName);
01876             rc = 1;
01877             goto exit;
01878         }
01879     }
01880     
01881     /* Copy file name or glob pattern removing multiple "/" chars. */
01882     /*
01883      * Note: rpmGetPath should guarantee a "canonical" path. That means
01884      * that the following pathologies should be weeded out:
01885      *          //bin//sh
01886      *          //usr//bin/
01887      *          /.././../usr/../bin//./sh
01888      */
01889     diskURL = rpmGenPath(fl->buildRootURL, NULL, fileURL);
01890 
01891     if (doGlob) {
01892         const char ** argv = NULL;
01893         int argc = 0;
01894         int i;
01895 
01896         /* XXX for %dev marker in file manifest only */
01897         if (fl->noGlob) {
01898             rpmError(RPMERR_BADSPEC, _("Glob not permitted: %s\n"),
01899                         diskURL);
01900             rc = 1;
01901             goto exit;
01902         }
01903 
01904         /*@-branchstate@*/
01905         rc = rpmGlob(diskURL, &argc, &argv);
01906         if (rc == 0 && argc >= 1) {
01907             for (i = 0; i < argc; i++) {
01908                 rc = addFile(fl, argv[i], NULL);
01909 /*@-boundswrite@*/
01910                 argv[i] = _free(argv[i]);
01911 /*@=boundswrite@*/
01912             }
01913             argv = _free(argv);
01914         } else {
01915             if(fl->currentFlags & RPMFILE_OPTIONAL) {
01916                 rpmMessage(RPMMESS_WARNING,
01917                     _("File not found by glob: %s\n"), diskURL);
01918                 rc = 0;
01919                 goto exit;
01920             } else {
01921                 rpmError(RPMERR_BADSPEC, _("File not found by glob: %s\n"),
01922                         diskURL);
01923                 rc = 1;
01924                 goto exit;
01925             }
01926         }
01927         /*@=branchstate@*/
01928     } else {
01929         rc = addFile(fl, diskURL, NULL);
01930     }
01931 
01932 exit:
01933     diskURL = _free(diskURL);
01934     if (rc) {
01935         fl->processingFailed = 1;
01936         rc = RPMERR_BADSPEC;
01937     }
01938     return rc;
01939 }
01940 
01943 /*@-boundswrite@*/
01944 static int processPackageFiles(Spec spec, Package pkg,
01945                                int installSpecialDoc, int test)
01946         /*@globals rpmGlobalMacroContext, h_errno,
01947                 fileSystem, internalState@*/
01948         /*@modifies spec->macros,
01949                 pkg->cpioList, pkg->fileList, pkg->specialDoc, pkg->header,
01950                 rpmGlobalMacroContext, fileSystem, internalState @*/
01951 {
01952     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
01953     struct FileList_s fl;
01954     char *s, **files, **fp;
01955     const char *fileName;
01956     char buf[BUFSIZ];
01957     struct AttrRec_s arbuf;
01958     AttrRec specialDocAttrRec = &arbuf;
01959     char *specialDoc = NULL;
01960 
01961     nullAttrRec(specialDocAttrRec);
01962     pkg->cpioList = NULL;
01963 
01964     if (pkg->fileFile) {
01965         const char *ffn;
01966         FILE * f;
01967         FD_t fd;
01968 
01969         /* XXX W2DO? urlPath might be useful here. */
01970         if (*pkg->fileFile == '/') {
01971             ffn = rpmGetPath(pkg->fileFile, NULL);
01972         } else {
01973             /* XXX FIXME: add %{buildsubdir} */
01974             ffn = rpmGetPath("%{_builddir}/",
01975                 (spec->buildSubdir ? spec->buildSubdir : "") ,
01976                 "/", pkg->fileFile, NULL);
01977         }
01978         fd = Fopen(ffn, "r.fpio");
01979 
01980         if (fd == NULL || Ferror(fd)) {
01981             rpmError(RPMERR_BADFILENAME,
01982                 _("Could not open %%files file %s: %s\n"),
01983                 ffn, Fstrerror(fd));
01984             return RPMERR_BADFILENAME;
01985         }
01986         ffn = _free(ffn);
01987 
01988         /*@+voidabstract@*/ f = fdGetFp(fd); /*@=voidabstract@*/
01989         if (f != NULL)
01990         while (fgets(buf, sizeof(buf), f)) {
01991             handleComments(buf);
01992             if (expandMacros(spec, spec->macros, buf, sizeof(buf))) {
01993                 rpmError(RPMERR_BADSPEC, _("line: %s\n"), buf);
01994                 return RPMERR_BADSPEC;
01995             }
01996             appendStringBuf(pkg->fileList, buf);
01997         }
01998         (void) Fclose(fd);
01999     }
02000     
02001     /* Init the file list structure */
02002     memset(&fl, 0, sizeof(fl));
02003 
02004     /* XXX spec->buildRootURL == NULL, then xstrdup("") is returned */
02005     fl.buildRootURL = rpmGenPath(spec->rootURL, spec->buildRootURL, NULL);
02006 
02007     if (hge(pkg->header, RPMTAG_DEFAULTPREFIX, NULL, (void **)&fl.prefix, NULL))
02008         fl.prefix = xstrdup(fl.prefix);
02009     else
02010         fl.prefix = NULL;
02011 
02012     fl.fileCount = 0;
02013     fl.totalFileSize = 0;
02014     fl.processingFailed = 0;
02015 
02016     fl.passedSpecialDoc = 0;
02017     fl.isSpecialDoc = 0;
02018 
02019     fl.isDir = 0;
02020     fl.inFtw = 0;
02021     fl.currentFlags = 0;
02022     fl.currentVerifyFlags = 0;
02023     
02024     fl.noGlob = 0;
02025     fl.devtype = 0;
02026     fl.devmajor = 0;
02027     fl.devminor = 0;
02028 
02029     nullAttrRec(&fl.cur_ar);
02030     nullAttrRec(&fl.def_ar);
02031     dupAttrRec(&root_ar, &fl.def_ar);   /* XXX assume %defattr(-,root,root) */
02032 
02033     fl.defVerifyFlags = RPMVERIFY_ALL;
02034     fl.nLangs = 0;
02035     fl.currentLangs = NULL;
02036 
02037     fl.currentSpecdFlags = 0;
02038     fl.defSpecdFlags = 0;
02039 
02040     fl.docDirCount = 0;
02041     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/doc");
02042     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/man");
02043     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/info");
02044     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/X11R6/man");
02045     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/share/doc");
02046     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/share/man");
02047     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/share/info");
02048     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/share/gtk-doc/html");
02049     fl.docDirs[fl.docDirCount++] = rpmGetPath("%{_docdir}", NULL);
02050     fl.docDirs[fl.docDirCount++] = rpmGetPath("%{_mandir}", NULL);
02051     fl.docDirs[fl.docDirCount++] = rpmGetPath("%{_infodir}", NULL);
02052     fl.docDirs[fl.docDirCount++] = rpmGetPath("%{_javadocdir}", NULL);
02053     
02054     fl.fileList = NULL;
02055     fl.fileListRecsAlloced = 0;
02056     fl.fileListRecsUsed = 0;
02057 
02058     s = getStringBuf(pkg->fileList);
02059     files = splitString(s, strlen(s), '\n');
02060 
02061     for (fp = files; *fp != NULL; fp++) {
02062         s = *fp;
02063         SKIPSPACE(s);
02064         if (*s == '\0')
02065             continue;
02066         fileName = NULL;
02067         /*@-nullpass@*/ /* LCL: buf is NULL ?!? */
02068         strcpy(buf, s);
02069         /*@=nullpass@*/
02070         
02071         /* Reset for a new line in %files */
02072         fl.isDir = 0;
02073         fl.inFtw = 0;
02074         fl.currentFlags = 0;
02075         /* turn explicit flags into %def'd ones (gosh this is hacky...) */
02076         fl.currentSpecdFlags = ((unsigned)fl.defSpecdFlags) >> 8;
02077         fl.currentVerifyFlags = fl.defVerifyFlags;
02078         fl.isSpecialDoc = 0;
02079 
02080         fl.noGlob = 0;
02081         fl.devtype = 0;
02082         fl.devmajor = 0;
02083         fl.devminor = 0;
02084 
02085         /* XXX should reset to %deflang value */
02086         if (fl.currentLangs) {
02087             int i;
02088             for (i = 0; i < fl.nLangs; i++)
02089                 /*@-unqualifiedtrans@*/
02090                 fl.currentLangs[i] = _free(fl.currentLangs[i]);
02091                 /*@=unqualifiedtrans@*/
02092             fl.currentLangs = _free(fl.currentLangs);
02093         }
02094         fl.nLangs = 0;
02095 
02096         dupAttrRec(&fl.def_ar, &fl.cur_ar);
02097 
02098         /*@-nullpass@*/ /* LCL: buf is NULL ?!? */
02099         if (parseForVerify(buf, &fl))
02100             continue;
02101         if (parseForAttr(buf, &fl))
02102             continue;
02103         if (parseForDev(buf, &fl))
02104             continue;
02105         if (parseForConfig(buf, &fl))
02106             continue;
02107         if (parseForLang(buf, &fl))
02108             continue;
02109         /*@-nullstate@*/        /* FIX: pkg->fileFile might be NULL */
02110         if (parseForSimple(spec, pkg, buf, &fl, &fileName))
02111         /*@=nullstate@*/
02112             continue;
02113         /*@=nullpass@*/
02114         if (fileName == NULL)
02115             continue;
02116 
02117         /*@-branchstate@*/
02118         if (fl.isSpecialDoc) {
02119             /* Save this stuff for last */
02120             specialDoc = _free(specialDoc);
02121             specialDoc = xstrdup(fileName);
02122             dupAttrRec(&fl.cur_ar, specialDocAttrRec);
02123         } else if (fl.currentFlags & RPMFILE_PUBKEY) {
02124 /*@-nullstate@*/        /* FIX: pkg->fileFile might be NULL */
02125             (void) processMetadataFile(pkg, &fl, fileName, RPMTAG_PUBKEYS);
02126 /*@=nullstate@*/
02127         } else if (fl.currentFlags & RPMFILE_POLICY) {
02128 /*@-nullstate@*/        /* FIX: pkg->fileFile might be NULL */
02129             (void) processMetadataFile(pkg, &fl, fileName, RPMTAG_POLICIES);
02130 /*@=nullstate@*/
02131         } else {
02132 /*@-nullstate@*/        /* FIX: pkg->fileFile might be NULL */
02133             (void) processBinaryFile(pkg, &fl, fileName);
02134 /*@=nullstate@*/
02135         }
02136         /*@=branchstate@*/
02137     }
02138 
02139     /* Now process special doc, if there is one */
02140     if (specialDoc) {
02141         if (installSpecialDoc) {
02142             int _missing_doc_files_terminate_build =
02143                     rpmExpandNumeric("%{?_missing_doc_files_terminate_build}");
02144             int rc;
02145 
02146             rc = doScript(spec, RPMBUILD_STRINGBUF, "%doc", pkg->specialDoc, test);
02147             if (rc && _missing_doc_files_terminate_build)
02148                 fl.processingFailed = rc;
02149         }
02150 
02151         /* Reset for %doc */
02152         fl.isDir = 0;
02153         fl.inFtw = 0;
02154         fl.currentFlags = 0;
02155         fl.currentVerifyFlags = fl.defVerifyFlags;
02156 
02157         fl.noGlob = 0;
02158         fl.devtype = 0;
02159         fl.devmajor = 0;
02160         fl.devminor = 0;
02161 
02162         /* XXX should reset to %deflang value */
02163         if (fl.currentLangs) {
02164             int i;
02165             for (i = 0; i < fl.nLangs; i++)
02166                 /*@-unqualifiedtrans@*/
02167                 fl.currentLangs[i] = _free(fl.currentLangs[i]);
02168                 /*@=unqualifiedtrans@*/
02169             fl.currentLangs = _free(fl.currentLangs);
02170         }
02171         fl.nLangs = 0;
02172 
02173         dupAttrRec(specialDocAttrRec, &fl.cur_ar);
02174         freeAttrRec(specialDocAttrRec);
02175 
02176         /*@-nullstate@*/        /* FIX: pkg->fileFile might be NULL */
02177         (void) processBinaryFile(pkg, &fl, specialDoc);
02178         /*@=nullstate@*/
02179 
02180         specialDoc = _free(specialDoc);
02181     }
02182     
02183     freeSplitString(files);
02184 
02185     if (fl.processingFailed)
02186         goto exit;
02187 
02188     /* Verify that file attributes scope over hardlinks correctly. */
02189     if (checkHardLinks(&fl))
02190         (void) rpmlibNeedsFeature(pkg->header,
02191                         "PartialHardlinkSets", "4.0.4-1");
02192 
02193     genCpioListAndHeader(&fl, &pkg->cpioList, pkg->header, 0);
02194 
02195     if (spec->timeCheck)
02196         timeCheck(spec->timeCheck, pkg->header);
02197     
02198 exit:
02199     fl.buildRootURL = _free(fl.buildRootURL);
02200     fl.prefix = _free(fl.prefix);
02201 
02202     freeAttrRec(&fl.cur_ar);
02203     freeAttrRec(&fl.def_ar);
02204 
02205     if (fl.currentLangs) {
02206         int i;
02207         for (i = 0; i < fl.nLangs; i++)
02208             /*@-unqualifiedtrans@*/
02209             fl.currentLangs[i] = _free(fl.currentLangs[i]);
02210             /*@=unqualifiedtrans@*/
02211         fl.currentLangs = _free(fl.currentLangs);
02212     }
02213 
02214     fl.fileList = freeFileList(fl.fileList, fl.fileListRecsUsed);
02215     while (fl.docDirCount--)
02216         fl.docDirs[fl.docDirCount] = _free(fl.docDirs[fl.docDirCount]);
02217     return fl.processingFailed;
02218 }
02219 /*@=boundswrite@*/
02220 
02221 void initSourceHeader(Spec spec)
02222 {
02223     HeaderIterator hi;
02224     int_32 tag, type, count;
02225     const void * ptr;
02226 
02227     spec->sourceHeader = headerNew();
02228     /* Only specific tags are added to the source package header */
02229     /*@-branchstate@*/
02230     for (hi = headerInitIterator(spec->packages->header);
02231         headerNextIterator(hi, &tag, &type, &ptr, &count);
02232         ptr = headerFreeData(ptr, type))
02233     {
02234         switch (tag) {
02235         case RPMTAG_NAME:
02236         case RPMTAG_VERSION:
02237         case RPMTAG_RELEASE:
02238         case RPMTAG_EPOCH:
02239         case RPMTAG_SUMMARY:
02240         case RPMTAG_DESCRIPTION:
02241         case RPMTAG_PACKAGER:
02242         case RPMTAG_DISTRIBUTION:
02243         case RPMTAG_DISTURL:
02244         case RPMTAG_VENDOR:
02245         case RPMTAG_LICENSE:
02246         case RPMTAG_GROUP:
02247         case RPMTAG_OS:
02248         case RPMTAG_ARCH:
02249         case RPMTAG_CHANGELOGTIME:
02250         case RPMTAG_CHANGELOGNAME:
02251         case RPMTAG_CHANGELOGTEXT:
02252         case RPMTAG_URL:
02253         case HEADER_I18NTABLE:
02254             if (ptr)
02255                 (void)headerAddEntry(spec->sourceHeader, tag, type, ptr, count);
02256             /*@switchbreak@*/ break;
02257         default:
02258             /* do not copy */
02259             /*@switchbreak@*/ break;
02260         }
02261     }
02262     hi = headerFreeIterator(hi);
02263     /*@=branchstate@*/
02264 
02265     /* Add the build restrictions */
02266     /*@-branchstate@*/
02267     for (hi = headerInitIterator(spec->buildRestrictions);
02268         headerNextIterator(hi, &tag, &type, &ptr, &count);
02269         ptr = headerFreeData(ptr, type))
02270     {
02271         if (ptr)
02272             (void) headerAddEntry(spec->sourceHeader, tag, type, ptr, count);
02273     }
02274     hi = headerFreeIterator(hi);
02275     /*@=branchstate@*/
02276 
02277     if (spec->BANames && spec->BACount > 0) {
02278         (void) headerAddEntry(spec->sourceHeader, RPMTAG_BUILDARCHS,
02279                        RPM_STRING_ARRAY_TYPE,
02280                        spec->BANames, spec->BACount);
02281     }
02282 }
02283 
02284 int processSourceFiles(Spec spec)
02285 {
02286     struct Source *srcPtr;
02287     StringBuf sourceFiles;
02288     int x, isSpec = 1;
02289     struct FileList_s fl;
02290     char *s, **files, **fp;
02291     Package pkg;
02292     static char *_srcdefattr;
02293     static int oneshot;
02294 
02295     if (!oneshot) {
02296         _srcdefattr = rpmExpand("%{?_srcdefattr}", NULL);
02297         if (_srcdefattr && !*_srcdefattr)
02298             _srcdefattr = _free(_srcdefattr);
02299         oneshot = 1;
02300     }
02301     sourceFiles = newStringBuf();
02302 
02303     /* XXX
02304      * XXX This is where the source header for noarch packages needs
02305      * XXX to be initialized.
02306      */
02307     if (spec->sourceHeader == NULL)
02308         initSourceHeader(spec);
02309 
02310     /* Construct the file list and source entries */
02311     appendLineStringBuf(sourceFiles, spec->specFile);
02312     if (spec->sourceHeader != NULL)
02313     for (srcPtr = spec->sources; srcPtr != NULL; srcPtr = srcPtr->next) {
02314         if (srcPtr->flags & RPMBUILD_ISSOURCE) {
02315             (void) headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_SOURCE,
02316                                    RPM_STRING_ARRAY_TYPE, &srcPtr->source, 1);
02317             if (srcPtr->flags & RPMBUILD_ISNO) {
02318                 (void) headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_NOSOURCE,
02319                                        RPM_INT32_TYPE, &srcPtr->num, 1);
02320             }
02321         }
02322         if (srcPtr->flags & RPMBUILD_ISPATCH) {
02323             (void) headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_PATCH,
02324                                    RPM_STRING_ARRAY_TYPE, &srcPtr->source, 1);
02325             if (srcPtr->flags & RPMBUILD_ISNO) {
02326                 (void) headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_NOPATCH,
02327                                        RPM_INT32_TYPE, &srcPtr->num, 1);
02328             }
02329         }
02330 
02331       { const char * sfn;
02332         sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
02333                 "%{_sourcedir}/", srcPtr->source, NULL);
02334         appendLineStringBuf(sourceFiles, sfn);
02335         sfn = _free(sfn);
02336       }
02337     }
02338 
02339     for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
02340         for (srcPtr = pkg->icon; srcPtr != NULL; srcPtr = srcPtr->next) {
02341             const char * sfn;
02342             sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
02343                 "%{_sourcedir}/", srcPtr->source, NULL);
02344             appendLineStringBuf(sourceFiles, sfn);
02345             sfn = _free(sfn);
02346         }
02347     }
02348 
02349     spec->sourceCpioList = NULL;
02350 
02351     /* Init the file list structure */
02352     memset(&fl, 0, sizeof(fl));
02353     if (_srcdefattr) {
02354         char *a = xmalloc(strlen(_srcdefattr) + 9 + 1);
02355         strcpy(a, "%defattr ");
02356         strcpy(a + 9, _srcdefattr);
02357         parseForAttr(a, &fl);
02358         a = _free(a);
02359     }
02360     fl.fileList = xcalloc((spec->numSources + 1), sizeof(*fl.fileList));
02361     fl.processingFailed = 0;
02362     fl.fileListRecsUsed = 0;
02363     fl.totalFileSize = 0;
02364     fl.prefix = NULL;
02365     fl.buildRootURL = NULL;
02366 
02367     s = getStringBuf(sourceFiles);
02368     files = splitString(s, strlen(s), '\n');
02369 
02370     /* The first source file is the spec file */
02371     x = 0;
02372     for (fp = files; *fp != NULL; fp++) {
02373         const char * diskURL, *diskPath;
02374         FileListRec flp;
02375 
02376         diskURL = *fp;
02377         SKIPSPACE(diskURL);
02378         if (! *diskURL)
02379             continue;
02380 
02381         flp = &fl.fileList[x];
02382 
02383         flp->flags = isSpec ? RPMFILE_SPECFILE : 0;
02384         /* files with leading ! are no source files */
02385         if (*diskURL == '!') {
02386             flp->flags |= RPMFILE_GHOST;
02387             diskURL++;
02388         }
02389 
02390         (void) urlPath(diskURL, &diskPath);
02391 
02392         flp->diskURL = xstrdup(diskURL);
02393         diskPath = strrchr(diskPath, '/');
02394         if (diskPath)
02395             diskPath++;
02396         else
02397             diskPath = diskURL;
02398 
02399         flp->fileURL = xstrdup(diskPath);
02400         flp->verifyFlags = RPMVERIFY_ALL;
02401 
02402         if (Stat(diskURL, &flp->fl_st)) {
02403             rpmError(RPMERR_BADSPEC, _("Bad file: %s: %s\n"),
02404                 diskURL, strerror(errno));
02405             fl.processingFailed = 1;
02406         }
02407 
02408         if (fl.def_ar.ar_fmodestr) {
02409             flp->fl_mode &= S_IFMT;
02410             flp->fl_mode |= fl.def_ar.ar_fmode;
02411         }
02412         if (fl.def_ar.ar_user) {
02413             flp->uname = getUnameS(fl.def_ar.ar_user);
02414         } else {
02415             flp->uname = getUname(flp->fl_uid);
02416         }
02417         if (fl.def_ar.ar_group) {
02418             flp->gname = getGnameS(fl.def_ar.ar_group);
02419         } else {
02420             flp->gname = getGname(flp->fl_gid);
02421         }
02422         flp->langs = xstrdup("");
02423         
02424         fl.totalFileSize += flp->fl_size;
02425         
02426         if (! (flp->uname && flp->gname)) {
02427             rpmError(RPMERR_BADSPEC, _("Bad owner/group: %s\n"), diskURL);
02428             fl.processingFailed = 1;
02429         }
02430 
02431         isSpec = 0;
02432         x++;
02433     }
02434     fl.fileListRecsUsed = x;
02435     freeSplitString(files);
02436 
02437     if (! fl.processingFailed) {
02438         if (spec->sourceHeader != NULL)
02439             genCpioListAndHeader(&fl, &spec->sourceCpioList,
02440                         spec->sourceHeader, 1);
02441     }
02442 
02443     sourceFiles = freeStringBuf(sourceFiles);
02444     fl.fileList = freeFileList(fl.fileList, fl.fileListRecsUsed);
02445     freeAttrRec(&fl.def_ar);
02446     return fl.processingFailed;
02447 }
02448 
02454 static int checkFiles(StringBuf fileList)
02455         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
02456         /*@modifies rpmGlobalMacroContext, fileSystem, internalState @*/
02457 {
02458 /*@-readonlytrans@*/
02459     static const char * av_ckfile[] = { "%{?__check_files}", NULL };
02460 /*@=readonlytrans@*/
02461     StringBuf sb_stdout = NULL;
02462     const char * s;
02463     int rc;
02464     
02465     s = rpmExpand(av_ckfile[0], NULL);
02466     if (!(s && *s)) {
02467         rc = -1;
02468         goto exit;
02469     }
02470     rc = 0;
02471 
02472     rpmMessage(RPMMESS_NORMAL, _("Checking for unpackaged file(s): %s\n"), s);
02473 
02474 /*@-boundswrite@*/
02475     rc = rpmfcExec(av_ckfile, fileList, &sb_stdout, 0);
02476 /*@=boundswrite@*/
02477     if (rc < 0)
02478         goto exit;
02479     
02480     if (sb_stdout) {
02481         int _unpackaged_files_terminate_build =
02482                 rpmExpandNumeric("%{?_unpackaged_files_terminate_build}");
02483         const char * t;
02484 
02485         t = getStringBuf(sb_stdout);
02486         if ((*t != '\0') && (*t != '\n')) {
02487             rc = (_unpackaged_files_terminate_build) ? 1 : 0;
02488             rpmMessage((rc ? RPMMESS_ERROR : RPMMESS_WARNING),
02489                 _("Installed (but unpackaged) file(s) found:\n%s"), t);
02490         }
02491     }
02492     
02493 exit:
02494     sb_stdout = freeStringBuf(sb_stdout);
02495     s = _free(s);
02496     return rc;
02497 }
02498 
02499 /*@-incondefs@*/
02500 int processBinaryFiles(Spec spec, int installSpecialDoc, int test)
02501         /*@globals check_fileList @*/
02502         /*@modifies check_fileList @*/
02503 {
02504     Package pkg;
02505     int res = 0;
02506     
02507     check_fileList = newStringBuf();
02508     
02509     for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
02510         const char *n, *v, *r;
02511         int rc;
02512 
02513         if (pkg->fileList == NULL)
02514             continue;
02515 
02516         (void) headerNVR(pkg->header, &n, &v, &r);
02517         rpmMessage(RPMMESS_NORMAL, _("Processing files: %s-%s-%s\n"), n, v, r);
02518                    
02519         if ((rc = processPackageFiles(spec, pkg, installSpecialDoc, test)))
02520             res = rc;
02521 
02522         if ((rc = rpmfcGenerateDepends(spec, pkg)))
02523             res = rc;
02524     }
02525 
02526     /* Now we have in fileList list of files from all packages.
02527      * We pass it to a script which does the work of finding missing
02528      * and duplicated files.
02529      */
02530     
02531     
02532     if (checkFiles(check_fileList) > 0) {
02533         if (res == 0)
02534             res = 1;
02535     }
02536     
02537     check_fileList = freeStringBuf(check_fileList);
02538     
02539     return res;
02540 }
02541 /*@=incondefs@*/

Generated on Sun Jun 1 21:56:21 2008 for rpm by  doxygen 1.5.4