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

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 #include <signal.h>     /* getOutputFrom() */
00013 
00014 #include <rpmio_internal.h>
00015 #include <rpmbuild.h>
00016 #include <rpmmacro.h>
00017 
00018 #include "buildio.h"
00019 
00020 #include "myftw.h"
00021 #include "md5.h"
00022 #include "debug.h"
00023 
00024 /*@access Header @*/
00025 /*@access StringBuf @*/
00026 /*@access TFI_t @*/
00027 /*@access FD_t @*/
00028 
00029 #define SKIPWHITE(_x)   {while(*(_x) && (isspace(*_x) || *(_x) == ',')) (_x)++;}
00030 #define SKIPNONWHITE(_x){while(*(_x) &&!(isspace(*_x) || *(_x) == ',')) (_x)++;}
00031 
00032 #define MAXDOCDIR 1024
00033 
00034 extern int _noDirTokens;
00035 
00036 #define SPECD_DEFFILEMODE       (1<<0)
00037 #define SPECD_DEFDIRMODE        (1<<1)
00038 #define SPECD_DEFUID            (1<<2)
00039 #define SPECD_DEFGID            (1<<3)
00040 #define SPECD_DEFVERIFY         (1<<4)
00041 
00042 #define SPECD_FILEMODE          (1<<8)
00043 #define SPECD_DIRMODE           (1<<9)
00044 #define SPECD_UID               (1<<10)
00045 #define SPECD_GID               (1<<11)
00046 #define SPECD_VERIFY            (1<<12)
00047 
00050 typedef struct {
00051     struct stat fl_st;
00052 #define fl_dev  fl_st.st_dev
00053 #define fl_ino  fl_st.st_ino
00054 #define fl_mode fl_st.st_mode
00055 #define fl_nlink fl_st.st_nlink /* unused */
00056 #define fl_uid  fl_st.st_uid
00057 #define fl_gid  fl_st.st_gid
00058 #define fl_rdev fl_st.st_rdev
00059 #define fl_size fl_st.st_size
00060 #define fl_mtime fl_st.st_mtime
00061 
00062     const char *diskURL;        /* get file from here       */
00063     const char *fileURL;        /* filename in cpio archive */
00064     /*@observer@*/ const char *uname;
00065     /*@observer@*/ const char *gname;
00066     int         flags;
00067     int         specdFlags;     /* which attributes have been explicitly specified. */
00068     int         verifyFlags;
00069     const char *langs;  /* XXX locales separated with | */
00070 } FileListRec;
00071 
00074 typedef struct {
00075     const char *ar_fmodestr;
00076     const char *ar_dmodestr;
00077     const char *ar_user;
00078     const char *ar_group;
00079     mode_t      ar_fmode;
00080     mode_t      ar_dmode;
00081 } AttrRec;
00082 
00085 static int multiLib = 0;        /* MULTILIB */
00086 
00090 struct FileList {
00091     const char *buildRootURL;
00092     const char *prefix;
00093 
00094     int fileCount;
00095     int totalFileSize;
00096     int processingFailed;
00097 
00098     int passedSpecialDoc;
00099     int isSpecialDoc;
00100     
00101     int isDir;
00102     int inFtw;
00103     int currentFlags;
00104     int currentSpecdFlags;
00105     int currentVerifyFlags;
00106     AttrRec cur_ar;
00107     AttrRec def_ar;
00108     int defSpecdFlags;
00109     int defVerifyFlags;
00110     int nLangs;
00111     /*@only@*/ const char **currentLangs;
00112 
00113     /* Hard coded limit of MAXDOCDIR docdirs.         */
00114     /* If you break it you are doing something wrong. */
00115     const char *docDirs[MAXDOCDIR];
00116     int docDirCount;
00117     
00118     FileListRec *fileList;
00119     int fileListRecsAlloced;
00120     int fileListRecsUsed;
00121 };
00122 
00125 static void nullAttrRec(/*@out@*/AttrRec *ar)
00126 {
00127     ar->ar_fmodestr = NULL;
00128     ar->ar_dmodestr = NULL;
00129     ar->ar_user = NULL;
00130     ar->ar_group = NULL;
00131     ar->ar_fmode = 0;
00132     ar->ar_dmode = 0;
00133 }
00134 
00137 static void freeAttrRec(AttrRec *ar)
00138 {
00139     FREE(ar->ar_fmodestr);
00140     FREE(ar->ar_dmodestr);
00141     FREE(ar->ar_user);
00142     FREE(ar->ar_group);
00143     /* XXX doesn't free ar (yet) */
00144 }
00145 
00148 static void dupAttrRec(AttrRec *oar, /*@out@*/ AttrRec *nar)
00149 {
00150     if (oar == nar)     /* XXX pathological paranoia */
00151         return;
00152     freeAttrRec(nar);
00153     nar->ar_fmodestr = (oar->ar_fmodestr ? xstrdup(oar->ar_fmodestr) : NULL);
00154     nar->ar_dmodestr = (oar->ar_dmodestr ? xstrdup(oar->ar_dmodestr) : NULL);
00155     nar->ar_user = (oar->ar_user ? xstrdup(oar->ar_user) : NULL);
00156     nar->ar_group = (oar->ar_group ? xstrdup(oar->ar_group) : NULL);
00157     nar->ar_fmode = oar->ar_fmode;
00158     nar->ar_dmode = oar->ar_dmode;
00159 }
00160 
00161 #if 0
00162 
00164 static void dumpAttrRec(const char *msg, AttrRec *ar) {
00165     if (msg)
00166         fprintf(stderr, "%s:\t", msg);
00167     fprintf(stderr, "(%s, %s, %s, %s)\n",
00168         ar->ar_fmodestr,
00169         ar->ar_user,
00170         ar->ar_group,
00171         ar->ar_dmodestr);
00172 }
00173 #endif
00174 
00175 /* strtokWithQuotes() modified from glibc strtok() */
00176 /* Copyright (C) 1991, 1996 Free Software Foundation, Inc.
00177    This file is part of the GNU C Library.
00178 
00179    The GNU C Library is free software; you can redistribute it and/or
00180    modify it under the terms of the GNU Library General Public License as
00181    published by the Free Software Foundation; either version 2 of the
00182    License, or (at your option) any later version.
00183 
00184    The GNU C Library is distributed in the hope that it will be useful,
00185    but WITHOUT ANY WARRANTY; without even the implied warranty of
00186    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00187    Library General Public License for more details.
00188 
00189    You should have received a copy of the GNU Library General Public
00190    License along with the GNU C Library; see the file COPYING.LIB.  If
00191    not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00192    Boston, MA 02111-1307, USA.  */
00193 
00196 static char *strtokWithQuotes(char *s, char *delim)
00197 {
00198     static char *olds = NULL;
00199     char *token;
00200 
00201     if (s == NULL) {
00202         s = olds;
00203     }
00204 
00205     /* Skip leading delimiters */
00206     s += strspn(s, delim);
00207     if (*s == '\0') {
00208         return NULL;
00209     }
00210 
00211     /* Find the end of the token.  */
00212     token = s;
00213     if (*token == '"') {
00214         token++;
00215         /* Find next " char */
00216         s = strchr(token, '"');
00217     } else {
00218         s = strpbrk(token, delim);
00219     }
00220 
00221     /* Terminate it */
00222     if (s == NULL) {
00223         /* This token finishes the string */
00224         olds = strchr(token, '\0');
00225     } else {
00226         /* Terminate the token and make olds point past it */
00227         *s = '\0';
00228         olds = s+1;
00229     }
00230 
00231     return token;
00232 }
00233 
00236 static void timeCheck(int tc, Header h)
00237 {
00238     int *mtime;
00239     char **files;
00240     int count, x, currentTime;
00241 
00242     headerGetEntry(h, RPMTAG_OLDFILENAMES, NULL, (void **) &files, &count);
00243     headerGetEntry(h, RPMTAG_FILEMTIMES, NULL, (void **) &mtime, NULL);
00244 
00245     currentTime = time(NULL);
00246     
00247     for (x = 0; x < count; x++) {
00248         if (currentTime - mtime[x] > tc) {
00249             rpmMessage(RPMMESS_WARNING, _("TIMECHECK failure: %s\n"), files[x]);
00250         }
00251     }
00252     FREE(files);
00253 }
00254 
00257 typedef struct VFA {
00258         char *  attribute;
00259         int     flag;
00260 } VFA_t;
00261 
00264 VFA_t verifyAttrs[] = {
00265         { "md5",        RPMVERIFY_MD5 },
00266         { "size",       RPMVERIFY_FILESIZE },
00267         { "link",       RPMVERIFY_LINKTO },
00268         { "user",       RPMVERIFY_USER },
00269         { "group",      RPMVERIFY_GROUP },
00270         { "mtime",      RPMVERIFY_MTIME },
00271         { "mode",       RPMVERIFY_MODE },
00272         { "rdev",       RPMVERIFY_RDEV },
00273         { NULL, 0 }
00274 };
00275 
00279 static int parseForVerify(char *buf, struct FileList *fl)
00280 {
00281     char *p, *pe, *q;
00282     const char *name;
00283     int *resultVerify;
00284     int not;
00285     int verifyFlags;
00286     int *specdFlags;
00287 
00288     if ((p = strstr(buf, (name = "%verify"))) != NULL) {
00289         resultVerify = &(fl->currentVerifyFlags);
00290         specdFlags = &fl->currentSpecdFlags;
00291     } else if ((p = strstr(buf, (name = "%defverify"))) != NULL) {
00292         resultVerify = &(fl->defVerifyFlags);
00293         specdFlags = &fl->defSpecdFlags;
00294     } else
00295         return 0;
00296 
00297     for (pe = p; (pe-p) < strlen(name); pe++)
00298         *pe = ' ';
00299 
00300     SKIPSPACE(pe);
00301 
00302     if (*pe != '(') {
00303         rpmError(RPMERR_BADSPEC, _("Missing '(' in %s %s\n"), name, pe);
00304         fl->processingFailed = 1;
00305         return RPMERR_BADSPEC;
00306     }
00307 
00308     /* Bracket %*verify args */
00309     *pe++ = ' ';
00310     for (p = pe; *pe && *pe != ')'; pe++)
00311         ;
00312 
00313     if (*pe == '\0') {
00314         rpmError(RPMERR_BADSPEC, _("Missing ')' in %s(%s\n"), name, p);
00315         fl->processingFailed = 1;
00316         return RPMERR_BADSPEC;
00317     }
00318 
00319     /* Localize. Erase parsed string */
00320     q = alloca((pe-p) + 1);
00321     strncpy(q, p, pe-p);
00322     q[pe-p] = '\0';
00323     while (p <= pe)
00324         *p++ = ' ';
00325 
00326     not = 0;
00327     verifyFlags = RPMVERIFY_NONE;
00328 
00329     for (p = q; *p; p = pe) {
00330         SKIPWHITE(p);
00331         if (*p == '\0')
00332             break;
00333         pe = p;
00334         SKIPNONWHITE(pe);
00335         if (*pe)
00336             *pe++ = '\0';
00337 
00338         {   VFA_t *vfa;
00339             for (vfa = verifyAttrs; vfa->attribute != NULL; vfa++) {
00340                 if (strcmp(p, vfa->attribute))
00341                     continue;
00342                 verifyFlags |= vfa->flag;
00343                 break;
00344             }
00345             if (vfa->attribute)
00346                 continue;
00347         }
00348 
00349         if (!strcmp(p, "not")) {
00350             not ^= 1;
00351         } else {
00352             rpmError(RPMERR_BADSPEC, _("Invalid %s token: %s\n"), name, p);
00353             fl->processingFailed = 1;
00354             return RPMERR_BADSPEC;
00355         }
00356     }
00357 
00358     *resultVerify = not ? ~(verifyFlags) : verifyFlags;
00359     *specdFlags |= SPECD_VERIFY;
00360 
00361     return 0;
00362 }
00363 
00364 #define isAttrDefault(_ars)     ((_ars)[0] == '-' && (_ars)[1] == '\0')
00365 
00369 static int parseForAttr(char *buf, struct FileList *fl)
00370 {
00371     char *p, *pe, *q;
00372     const char *name;
00373     int x;
00374     AttrRec arbuf, *ar = &arbuf, *ret_ar;
00375     int *specdFlags = NULL;
00376 
00377     if ((p = strstr(buf, (name = "%attr"))) != NULL) {
00378         ret_ar = &(fl->cur_ar);
00379         specdFlags = &fl->currentSpecdFlags;
00380     } else if ((p = strstr(buf, (name = "%defattr"))) != NULL) {
00381         ret_ar = &(fl->def_ar);
00382         specdFlags = &fl->defSpecdFlags;
00383     } else
00384         return 0;
00385 
00386     for (pe = p; (pe-p) < strlen(name); pe++)
00387         *pe = ' ';
00388 
00389     SKIPSPACE(pe);
00390 
00391     if (*pe != '(') {
00392         rpmError(RPMERR_BADSPEC, _("Missing '(' in %s %s\n"), name, pe);
00393         fl->processingFailed = 1;
00394         return RPMERR_BADSPEC;
00395     }
00396 
00397     /* Bracket %*attr args */
00398     *pe++ = ' ';
00399     for (p = pe; *pe && *pe != ')'; pe++)
00400         ;
00401 
00402     if (ret_ar == &(fl->def_ar)) {      /* %defattr */
00403         q = pe;
00404         q++;
00405         SKIPSPACE(q);
00406         if (*q) {
00407             rpmError(RPMERR_BADSPEC,
00408                      _("Non-white space follows %s(): %s\n"), name, q);
00409             fl->processingFailed = 1;
00410             return RPMERR_BADSPEC;
00411         }
00412     }
00413 
00414     /* Localize. Erase parsed string */
00415     q = alloca((pe-p) + 1);
00416     strncpy(q, p, pe-p);
00417     q[pe-p] = '\0';
00418     while (p <= pe)
00419         *p++ = ' ';
00420 
00421     nullAttrRec(ar);
00422 
00423     p = q; SKIPWHITE(p);
00424     if (*p) {
00425         pe = p; SKIPNONWHITE(pe); if (*pe) *pe++ = '\0';
00426         ar->ar_fmodestr = p;
00427         p = pe; SKIPWHITE(p);
00428     }
00429     if (*p) {
00430         pe = p; SKIPNONWHITE(pe); if (*pe) *pe++ = '\0';
00431         ar->ar_user = p;
00432         p = pe; SKIPWHITE(p);
00433     }
00434     if (*p) {
00435         pe = p; SKIPNONWHITE(pe); if (*pe) *pe++ = '\0';
00436         ar->ar_group = p;
00437         p = pe; SKIPWHITE(p);
00438     }
00439     if (*p && ret_ar == &(fl->def_ar)) {        /* %defattr */
00440         pe = p; SKIPNONWHITE(pe); if (*pe) *pe++ = '\0';
00441         ar->ar_dmodestr = p;
00442         p = pe; SKIPWHITE(p);
00443     }
00444 
00445     if (!(ar->ar_fmodestr && ar->ar_user && ar->ar_group) || *p != '\0') {
00446         rpmError(RPMERR_BADSPEC, _("Bad syntax: %s(%s)\n"), name, q);
00447         fl->processingFailed = 1;
00448         return RPMERR_BADSPEC;
00449     }
00450 
00451     /* Do a quick test on the mode argument and adjust for "-" */
00452     if (ar->ar_fmodestr && !isAttrDefault(ar->ar_fmodestr)) {
00453         unsigned int ui;
00454         x = sscanf(ar->ar_fmodestr, "%o", &ui);
00455         if ((x == 0) || (ar->ar_fmode & ~MYALLPERMS)) {
00456             rpmError(RPMERR_BADSPEC, _("Bad mode spec: %s(%s)\n"), name, q);
00457             fl->processingFailed = 1;
00458             return RPMERR_BADSPEC;
00459         }
00460         ar->ar_fmode = ui;
00461     } else
00462         ar->ar_fmodestr = NULL;
00463 
00464     if (ar->ar_dmodestr && !isAttrDefault(ar->ar_dmodestr)) {
00465         unsigned int ui;
00466         x = sscanf(ar->ar_dmodestr, "%o", &ui);
00467         if ((x == 0) || (ar->ar_dmode & ~MYALLPERMS)) {
00468             rpmError(RPMERR_BADSPEC, _("Bad dirmode spec: %s(%s)\n"), name, q);
00469             fl->processingFailed = 1;
00470             return RPMERR_BADSPEC;
00471         }
00472         ar->ar_dmode = ui;
00473     } else
00474         ar->ar_dmodestr = NULL;
00475 
00476     if (!(ar->ar_user && !isAttrDefault(ar->ar_user)))
00477         ar->ar_user = NULL;
00478 
00479     if (!(ar->ar_group && !isAttrDefault(ar->ar_group)))
00480         ar->ar_group = NULL;
00481 
00482     dupAttrRec(ar, ret_ar);
00483 
00484     /* XXX fix all this */
00485     *specdFlags |= SPECD_UID | SPECD_GID | SPECD_FILEMODE | SPECD_DIRMODE;
00486     
00487     return 0;
00488 }
00489 
00493 static int parseForConfig(char *buf, struct FileList *fl)
00494 {
00495     char *p, *pe, *q;
00496     const char *name;
00497 
00498     if ((p = strstr(buf, (name = "%config"))) == NULL)
00499         return 0;
00500 
00501     fl->currentFlags = RPMFILE_CONFIG;
00502 
00503     for (pe = p; (pe-p) < strlen(name); pe++)
00504         *pe = ' ';
00505     SKIPSPACE(pe);
00506     if (*pe != '(')
00507         return 0;
00508 
00509     /* Bracket %config args */
00510     *pe++ = ' ';
00511     for (p = pe; *pe && *pe != ')'; pe++)
00512         ;
00513 
00514     if (*pe == '\0') {
00515         rpmError(RPMERR_BADSPEC, _("Missing ')' in %s(%s\n"), name, p);
00516         fl->processingFailed = 1;
00517         return RPMERR_BADSPEC;
00518     }
00519 
00520     /* Localize. Erase parsed string */
00521     q = alloca((pe-p) + 1);
00522     strncpy(q, p, pe-p);
00523     q[pe-p] = '\0';
00524     while (p <= pe)
00525         *p++ = ' ';
00526 
00527     for (p = q; *p; p = pe) {
00528         SKIPWHITE(p);
00529         if (*p == '\0')
00530             break;
00531         pe = p;
00532         SKIPNONWHITE(pe);
00533         if (*pe)
00534             *pe++ = '\0';
00535         if (!strcmp(p, "missingok")) {
00536             fl->currentFlags |= RPMFILE_MISSINGOK;
00537         } else if (!strcmp(p, "noreplace")) {
00538             fl->currentFlags |= RPMFILE_NOREPLACE;
00539         } else {
00540             rpmError(RPMERR_BADSPEC, _("Invalid %s token: %s\n"), name, p);
00541             fl->processingFailed = 1;
00542             return RPMERR_BADSPEC;
00543         }
00544     }
00545 
00546     return 0;
00547 }
00548 
00551 static int langCmp(const void * ap, const void *bp) {
00552     return strcmp(*(const char **)ap, *(const char **)bp);
00553 }
00554 
00558 static int parseForLang(char *buf, struct FileList *fl)
00559 {
00560     char *p, *pe, *q;
00561     const char *name;
00562 
00563   while ((p = strstr(buf, (name = "%lang"))) != NULL) {
00564 
00565     for (pe = p; (pe-p) < strlen(name); pe++)
00566         *pe = ' ';
00567     SKIPSPACE(pe);
00568 
00569     if (*pe != '(') {
00570         rpmError(RPMERR_BADSPEC, _("Missing '(' in %s %s\n"), name, pe);
00571         fl->processingFailed = 1;
00572         return RPMERR_BADSPEC;
00573     }
00574 
00575     /* Bracket %lang args */
00576     *pe++ = ' ';
00577     for (pe = p; *pe && *pe != ')'; pe++)
00578         ;
00579 
00580     if (*pe == '\0') {
00581         rpmError(RPMERR_BADSPEC, _("Missing ')' in %s(%s\n"), name, p);
00582         fl->processingFailed = 1;
00583         return RPMERR_BADSPEC;
00584     }
00585 
00586     /* Localize. Erase parsed string */
00587     q = alloca((pe-p) + 1);
00588     strncpy(q, p, pe-p);
00589     q[pe-p] = '\0';
00590     while (p <= pe)
00591         *p++ = ' ';
00592 
00593     /* Parse multiple arguments from %lang */
00594     for (p = q; *p; p = pe) {
00595         char *newp;
00596         size_t np;
00597         int i;
00598 
00599         SKIPWHITE(p);
00600         pe = p;
00601         SKIPNONWHITE(pe);
00602 
00603         np = pe - p;
00604         
00605         /* Sanity check on locale lengths */
00606         if (np < 1 || (np == 1 && *p != 'C') || np >= 32) {
00607             rpmError(RPMERR_BADSPEC,
00608                 _("Unusual locale length: \"%.*s\" in %%lang(%s)\n"),
00609                 (int)np, p, q);
00610             fl->processingFailed = 1;
00611             return RPMERR_BADSPEC;
00612         }
00613 
00614         /* Check for duplicate locales */
00615         for (i = 0; i < fl->nLangs; i++) {
00616             if (strncmp(fl->currentLangs[i], p, np))
00617                 continue;
00618             rpmError(RPMERR_BADSPEC, _("Duplicate locale %.*s in %%lang(%s)\n"),
00619                 (int)np, p, q);
00620             fl->processingFailed = 1;
00621             return RPMERR_BADSPEC;
00622         }
00623 
00624         /* Add new locale */
00625         fl->currentLangs = (const char **) ((fl->currentLangs == NULL)
00626           ? xmalloc(sizeof(*fl->currentLangs))
00627           : xrealloc(fl->currentLangs,((fl->nLangs+1)*sizeof(*fl->currentLangs))));
00628         newp = xmalloc( np+1 );
00629         strncpy(newp, p, np);
00630         newp[np] = '\0';
00631         fl->currentLangs[fl->nLangs++] = newp;
00632         if (*pe == ',') pe++;   /* skip , if present */
00633     }
00634   }
00635 
00636     /* Insure that locales are sorted. */
00637     if (fl->currentLangs)
00638         qsort(fl->currentLangs, fl->nLangs, sizeof(*fl->currentLangs), langCmp);
00639 
00640     return 0;
00641 }
00642 
00645 static int parseForRegexLang(const char *fileName, /*@out@*/char **lang)
00646 {
00647     static int initialized = 0;
00648     static int hasRegex = 0;
00649     static regex_t compiledPatt;
00650     static char buf[BUFSIZ];
00651     int x;
00652     regmatch_t matches[2];
00653     const char *s;
00654 
00655     if (! initialized) {
00656         const char *patt = rpmExpand("%{_langpatt}", NULL);
00657         int rc = 0;
00658         if (!(patt && *patt != '%'))
00659             rc = 1;
00660         else if (regcomp(&compiledPatt, patt, REG_EXTENDED))
00661             rc = -1;
00662         free((void *)patt);
00663         if (rc)
00664             return rc;
00665         hasRegex = 1;
00666         initialized = 1;
00667     }
00668     
00669     if (! hasRegex || regexec(&compiledPatt, fileName, 2, matches, REG_NOTEOL))
00670         return 1;
00671 
00672     /* Got match */
00673     s = fileName + matches[1].rm_eo - 1;
00674     x = matches[1].rm_eo - matches[1].rm_so;
00675     buf[x] = '\0';
00676     while (x) {
00677         buf[--x] = *s--;
00678     }
00679     if (lang)
00680         *lang = buf;
00681     return 0;
00682 }
00683 
00686 static int parseForRegexMultiLib(const char *fileName)
00687 {
00688     static int initialized = 0;
00689     static int hasRegex = 0;
00690     static regex_t compiledPatt;
00691 
00692     if (! initialized) {
00693         const char *patt;
00694         int rc = 0;
00695 
00696         initialized = 1;
00697         patt = rpmExpand("%{_multilibpatt}", NULL);
00698         if (!(patt && *patt != '%'))
00699             rc = 1;
00700         else if (regcomp(&compiledPatt, patt, REG_EXTENDED | REG_NOSUB))
00701             rc = -1;
00702         free((void *)patt);
00703         if (rc)
00704             return rc;
00705         hasRegex = 1;
00706     }
00707 
00708     if (! hasRegex || regexec(&compiledPatt, fileName, 0, NULL, 0))
00709         return 1;
00710 
00711     return 0;
00712 }
00713 
00716 VFA_t virtualFileAttributes[] = {
00717         { "%dir",       0 },    /* XXX why not RPMFILE_DIR? */
00718         { "%doc",       RPMFILE_DOC },
00719         { "%ghost",     RPMFILE_GHOST },
00720         { "%exclude",   RPMFILE_EXCLUDE },
00721         { "%readme",    RPMFILE_README },
00722         { "%license",   RPMFILE_LICENSE },
00723         { "%multilib",  0 },
00724 
00725 #if WHY_NOT
00726         { "%spec",      RPMFILE_SPEC },
00727         { "%config",    RPMFILE_CONFIG },
00728         { "%donotuse",  RPMFILE_DONOTUSE },     /* XXX WTFO? */
00729         { "%missingok", RPMFILE_CONFIG|RPMFILE_MISSINGOK },
00730         { "%noreplace", RPMFILE_CONFIG|RPMFILE_NOREPLACE },
00731 #endif
00732 
00733         { NULL, 0 }
00734 };
00735 
00739 static int parseForSimple(/*@unused@*/Spec spec, Package pkg, char *buf,
00740                           struct FileList *fl, const char **fileName)
00741 {
00742     char *s, *t;
00743     int res, specialDoc = 0;
00744     char specialDocBuf[BUFSIZ];
00745 
00746     specialDocBuf[0] = '\0';
00747     *fileName = NULL;
00748     res = 0;
00749 
00750     t = buf;
00751     while ((s = strtokWithQuotes(t, " \t\n")) != NULL) {
00752         t = NULL;
00753         if (!strcmp(s, "%docdir")) {
00754             s = strtokWithQuotes(NULL, " \t\n");
00755             if (fl->docDirCount == MAXDOCDIR) {
00756                 rpmError(RPMERR_INTERNAL, _("Hit limit for %%docdir\n"));
00757                 fl->processingFailed = 1;
00758                 res = 1;
00759             }
00760             fl->docDirs[fl->docDirCount++] = xstrdup(s);
00761             if (strtokWithQuotes(NULL, " \t\n")) {
00762                 rpmError(RPMERR_INTERNAL, _("Only one arg for %%docdir\n"));
00763                 fl->processingFailed = 1;
00764                 res = 1;
00765             }
00766             break;
00767         }
00768 
00769     /* Set flags for virtual file attributes */
00770     {   VFA_t *vfa;
00771         for (vfa = virtualFileAttributes; vfa->attribute != NULL; vfa++) {
00772             if (strcmp(s, vfa->attribute))
00773                 continue;
00774             if (!vfa->flag) {
00775                 if (!strcmp(s, "%dir"))
00776                     fl->isDir = 1;      /* XXX why not RPMFILE_DIR? */
00777                 else if (!strcmp(s, "%multilib"))
00778                     fl->currentFlags |= multiLib;
00779             } else
00780                 fl->currentFlags |= vfa->flag;
00781             break;
00782         }
00783         /* if we got an attribute, continue with next token */
00784         if (vfa->attribute != NULL)
00785             continue;
00786     }
00787 
00788         if (*fileName) {
00789             /* We already got a file -- error */
00790             rpmError(RPMERR_BADSPEC, _("Two files on one line: %s\n"),
00791                 *fileName);
00792             fl->processingFailed = 1;
00793             res = 1;
00794         }
00795 
00796         if (*s != '/') {
00797             if (fl->currentFlags & RPMFILE_DOC) {
00798                 specialDoc = 1;
00799                 strcat(specialDocBuf, " ");
00800                 strcat(specialDocBuf, s);
00801             } else {
00802                 /* not in %doc, does not begin with / -- error */
00803                 rpmError(RPMERR_BADSPEC,
00804                     _("File must begin with \"/\": %s\n"), s);
00805                 fl->processingFailed = 1;
00806                 res = 1;
00807             }
00808         } else {
00809             *fileName = s;
00810         }
00811     }
00812 
00813     if (specialDoc) {
00814         if (*fileName || (fl->currentFlags & ~(RPMFILE_DOC))) {
00815             rpmError(RPMERR_BADSPEC,
00816                      _("Can't mix special %%doc with other forms: %s\n"),
00817                      *fileName);
00818             fl->processingFailed = 1;
00819             res = 1;
00820         } else {
00821         /* XXX WATCHOUT: buf is an arg */
00822             {   const char *ddir, *n, *v;
00823 
00824                 headerNVR(pkg->header, &n, &v, NULL);
00825 
00826                 ddir = rpmGetPath("%{_docdir}/", n, "-", v, NULL);
00827                 strcpy(buf, ddir);
00828                 free((void *)ddir);
00829             }
00830 
00831         /* XXX FIXME: this is easy to do as macro expansion */
00832 
00833             if (! fl->passedSpecialDoc) {
00834                 pkg->specialDoc = newStringBuf();
00835                 appendStringBuf(pkg->specialDoc, "DOCDIR=$RPM_BUILD_ROOT");
00836                 appendLineStringBuf(pkg->specialDoc, buf);
00837                 appendLineStringBuf(pkg->specialDoc, "export DOCDIR");
00838                 appendLineStringBuf(pkg->specialDoc, "rm -rf $DOCDIR");
00839                 appendLineStringBuf(pkg->specialDoc, MKDIR_P " $DOCDIR");
00840 
00841                 *fileName = buf;
00842                 fl->passedSpecialDoc = 1;
00843                 fl->isSpecialDoc = 1;
00844             }
00845 
00846             appendStringBuf(pkg->specialDoc, "cp -pr ");
00847             appendStringBuf(pkg->specialDoc, specialDocBuf);
00848             appendLineStringBuf(pkg->specialDoc, " $DOCDIR");
00849         }
00850     }
00851 
00852     return res;
00853 }
00854 
00857 static int compareFileListRecs(const void *ap, const void *bp)
00858 {
00859     const char *a = ((FileListRec *)ap)->fileURL;
00860     const char *b = ((FileListRec *)bp)->fileURL;
00861     return strcmp(a, b);
00862 }
00863 
00867 static int isDoc(struct FileList *fl, const char *fileName)
00868 {
00869     int x = fl->docDirCount;
00870 
00871     while (x--) {
00872         if (strstr(fileName, fl->docDirs[x]) == fileName)
00873             return 1;
00874     }
00875     return 0;
00876 }
00877 
00883 static void checkHardLinks(struct FileList *fl)
00884 {
00885     char nlangs[BUFSIZ];
00886     FileListRec *ilp, *jlp;
00887     int i, j;
00888 
00889     for (i = 0;  i < fl->fileListRecsUsed; i++) {
00890         char *te;
00891 
00892         ilp = fl->fileList + i;
00893         if (!(S_ISREG(ilp->fl_mode) && ilp->fl_nlink > 1))
00894             continue;
00895         if (ilp->flags & RPMFILE_SPECFILE)
00896             continue;
00897 
00898         te = nlangs;
00899         *te = '\0';
00900         for (j = i + 1; j < fl->fileListRecsUsed; j++) {
00901             jlp = fl->fileList + j;
00902             if (!S_ISREG(jlp->fl_mode))
00903                 continue;
00904             if (ilp->fl_nlink != jlp->fl_nlink)
00905                 continue;
00906             if (ilp->fl_ino != jlp->fl_ino)
00907                 continue;
00908             if (ilp->fl_dev != jlp->fl_dev)
00909                 continue;
00910             if (!strcmp(ilp->langs, jlp->langs)) {
00911                 jlp->flags |= RPMFILE_SPECFILE;
00912                 continue;
00913             }
00914             if (te == nlangs)
00915                 te = stpcpy(te, ilp->langs);
00916             *te++ = '|';
00917             te = stpcpy(te, jlp->langs);
00918         }
00919 
00920         /* Are locales distributed over hard links correctly? */
00921         if (te == nlangs)
00922             continue;
00923 
00924         free((void *)ilp->langs);
00925         ilp->langs = xstrdup(nlangs);
00926         for (j = i + 1; j < fl->fileListRecsUsed; j++) {
00927             jlp = fl->fileList + j;
00928             if (!S_ISREG(jlp->fl_mode))
00929                 continue;
00930             if (ilp->fl_nlink != jlp->fl_nlink)
00931                 continue;
00932             if (ilp->fl_ino != jlp->fl_ino)
00933                 continue;
00934             if (ilp->fl_dev != jlp->fl_dev)
00935                 continue;
00936             jlp->flags |= RPMFILE_SPECFILE;
00937             free((void *)jlp->langs);
00938             jlp->langs = xstrdup(nlangs);
00939         }
00940     }
00941 
00942     for (i = 0;  i < fl->fileListRecsUsed; i++) {
00943         ilp = fl->fileList + i;
00944         ilp->flags &= ~RPMFILE_SPECFILE;
00945     }
00946 }
00947 
00953 static void genCpioListAndHeader(struct FileList *fl, TFI_t *cpioList,
00954                                  Header h, int isSrc)
00955 {
00956     int _addDotSlash = !(isSrc || rpmExpandNumeric("%{_noPayloadPrefix}"));
00957     uint_32 multiLibMask = 0;
00958     int apathlen = 0;
00959     int dpathlen = 0;
00960     int skipLen = 0;
00961     FileListRec *flp;
00962     char buf[BUFSIZ];
00963     int i;
00964     
00965     /* Sort the big list */
00966     qsort(fl->fileList, fl->fileListRecsUsed,
00967           sizeof(*(fl->fileList)), compareFileListRecs);
00968     
00969     /* Generate the header. */
00970     if (! isSrc) {
00971         skipLen = 1;
00972         if (fl->prefix)
00973             skipLen += strlen(fl->prefix);
00974     }
00975 
00976     for (i = 0, flp = fl->fileList; i < fl->fileListRecsUsed; i++, flp++) {
00977         char *s;
00978 
00979 #ifdef  DYING
00980         if (i < (fl->fileListRecsUsed - 1) &&
00981             !strcmp(flp->fileURL, flp[1].fileURL))
00982         {
00983             rpmError(RPMERR_BADSPEC, _("File listed twice: %s\n"),
00984                 flp->fileURL);
00985             fl->processingFailed = 1;
00986         }
00987 #endif
00988 
00989         /* Merge duplicate entries. */
00990         while (i < (fl->fileListRecsUsed - 1) &&
00991             !strcmp(flp->fileURL, flp[1].fileURL)) {
00992 
00993             /* Two entries for the same file found, merge the entries. */
00994 
00995             /* file flags */
00996             flp[1].flags |= flp->flags; 
00997    
00998             /* file mode */
00999             if (S_ISDIR(flp->fl_mode)) {
01000                 if ((flp[1].specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)) <
01001                     (flp->specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)))
01002                         flp[1].fl_mode = flp->fl_mode;
01003             } else {
01004                 if ((flp[1].specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)) <
01005                     (flp->specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)))
01006                         flp[1].fl_mode = flp->fl_mode;
01007             }
01008 
01009             /* uid */
01010             if ((flp[1].specdFlags & (SPECD_UID | SPECD_DEFUID)) <
01011                 (flp->specdFlags & (SPECD_UID | SPECD_DEFUID)))
01012             {
01013                 flp[1].fl_uid = flp->fl_uid;
01014                 flp[1].uname = flp->uname;
01015             }
01016 
01017             /* gid */
01018             if ((flp[1].specdFlags & (SPECD_GID | SPECD_DEFGID)) <
01019                 (flp->specdFlags & (SPECD_GID | SPECD_DEFGID)))
01020             {
01021                 flp[1].fl_gid = flp->fl_gid;
01022                 flp[1].gname = flp->gname;
01023             }
01024 
01025             /* verify flags */
01026             if ((flp[1].specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)) <
01027                 (flp->specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)))
01028                     flp[1].verifyFlags = flp->verifyFlags;
01029 
01030             /* XXX to-do: language */
01031 
01032             flp++; i++;
01033         }
01034 
01035         /* Skip files that were marked with %exclude. */
01036         if (flp->flags & RPMFILE_EXCLUDE) continue;
01037 
01038         /* Omit '/' and/or URL prefix, leave room for "./" prefix */
01039         apathlen += (strlen(flp->fileURL) - skipLen + (_addDotSlash ? 3 : 1));
01040 
01041         /* Leave room for both dirname and basename NUL's */
01042         dpathlen += (strlen(flp->diskURL) + 2);
01043 
01044         if (flp->flags & RPMFILE_MULTILIB_MASK)
01045             multiLibMask |=
01046                 (1 << ((flp->flags & RPMFILE_MULTILIB_MASK))
01047                       >> RPMFILE_MULTILIB_SHIFT);
01048 
01049         /*
01050          * Make the header, the OLDFILENAMES will get converted to a 
01051          * compressed file list write before we write the actual package to
01052          * disk.
01053          */
01054         headerAddOrAppendEntry(h, RPMTAG_OLDFILENAMES, RPM_STRING_ARRAY_TYPE,
01055                                &(flp->fileURL), 1);
01056 
01057       if (sizeof(flp->fl_size) != sizeof(uint_32)) {
01058         uint_32 psize = (uint_32)flp->fl_size;
01059         headerAddOrAppendEntry(h, RPMTAG_FILESIZES, RPM_INT32_TYPE,
01060                                &(psize), 1);
01061       } else {
01062         headerAddOrAppendEntry(h, RPMTAG_FILESIZES, RPM_INT32_TYPE,
01063                                &(flp->fl_size), 1);
01064       }
01065         headerAddOrAppendEntry(h, RPMTAG_FILEUSERNAME, RPM_STRING_ARRAY_TYPE,
01066                                &(flp->uname), 1);
01067         headerAddOrAppendEntry(h, RPMTAG_FILEGROUPNAME, RPM_STRING_ARRAY_TYPE,
01068                                &(flp->gname), 1);
01069         headerAddOrAppendEntry(h, RPMTAG_FILEMTIMES, RPM_INT32_TYPE,
01070                                &(flp->fl_mtime), 1);
01071       if (sizeof(flp->fl_mode) != sizeof(uint_16)) {
01072         uint_16 pmode = (uint_16)flp->fl_mode;
01073         headerAddOrAppendEntry(h, RPMTAG_FILEMODES, RPM_INT16_TYPE,
01074                                &(pmode), 1);
01075       } else {
01076         headerAddOrAppendEntry(h, RPMTAG_FILEMODES, RPM_INT16_TYPE,
01077                                &(flp->fl_mode), 1);
01078       }
01079       if (sizeof(flp->fl_rdev) != sizeof(uint_16)) {
01080         uint_16 prdev = (uint_16)flp->fl_rdev;
01081         headerAddOrAppendEntry(h, RPMTAG_FILERDEVS, RPM_INT16_TYPE,
01082                                &(prdev), 1);
01083       } else {
01084         headerAddOrAppendEntry(h, RPMTAG_FILERDEVS, RPM_INT16_TYPE,
01085                                &(flp->fl_rdev), 1);
01086       }
01087       if (sizeof(flp->fl_dev) != sizeof(uint_32)) {
01088         uint_32 pdevice = (uint_32)flp->fl_dev;
01089         headerAddOrAppendEntry(h, RPMTAG_FILEDEVICES, RPM_INT32_TYPE,
01090                                &(pdevice), 1);
01091       } else {
01092         headerAddOrAppendEntry(h, RPMTAG_FILEDEVICES, RPM_INT32_TYPE,
01093                                &(flp->fl_dev), 1);
01094       }
01095         headerAddOrAppendEntry(h, RPMTAG_FILEINODES, RPM_INT32_TYPE,
01096                                &(flp->fl_ino), 1);
01097 
01098         headerAddOrAppendEntry(h, RPMTAG_FILELANGS, RPM_STRING_ARRAY_TYPE,
01099                                &(flp->langs),  1);
01100         
01101         /* We used to add these, but they should not be needed */
01102         /* headerAddOrAppendEntry(h, RPMTAG_FILEUIDS,
01103          *                 RPM_INT32_TYPE, &(flp->fl_uid), 1);
01104          * headerAddOrAppendEntry(h, RPMTAG_FILEGIDS,
01105          *                 RPM_INT32_TYPE, &(flp->fl_gid), 1);
01106          */
01107         
01108         buf[0] = '\0';
01109         if (S_ISREG(flp->fl_mode))
01110             mdfile(flp->diskURL, buf);
01111         s = buf;
01112         headerAddOrAppendEntry(h, RPMTAG_FILEMD5S, RPM_STRING_ARRAY_TYPE,
01113                                &s, 1);
01114         
01115         buf[0] = '\0';
01116         if (S_ISLNK(flp->fl_mode)) {
01117             buf[Readlink(flp->diskURL, buf, BUFSIZ)] = '\0';
01118             if (fl->buildRootURL) {
01119                 const char * buildRoot;
01120                 (void) urlPath(fl->buildRootURL, &buildRoot);
01121 
01122                 if (buf[0] == '/' && strcmp(buildRoot, "/") &&
01123                     !strncmp(buf, buildRoot, strlen(buildRoot))) {
01124                      rpmError(RPMERR_BADSPEC,
01125                                 _("Symlink points to BuildRoot: %s -> %s\n"),
01126                                 flp->fileURL, buf);
01127                     fl->processingFailed = 1;
01128                 }
01129             }
01130         }
01131         s = buf;
01132         headerAddOrAppendEntry(h, RPMTAG_FILELINKTOS, RPM_STRING_ARRAY_TYPE,
01133                                &s, 1);
01134         
01135         if (flp->flags & RPMFILE_GHOST) {
01136             flp->verifyFlags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE |
01137                                 RPMVERIFY_LINKTO | RPMVERIFY_MTIME);
01138         }
01139         headerAddOrAppendEntry(h, RPMTAG_FILEVERIFYFLAGS, RPM_INT32_TYPE,
01140                                &(flp->verifyFlags), 1);
01141         
01142         if (!isSrc && isDoc(fl, flp->fileURL))
01143             flp->flags |= RPMFILE_DOC;
01144         /* XXX Should directories have %doc/%config attributes? (#14531) */
01145         if (S_ISDIR(flp->fl_mode))
01146             flp->flags &= ~(RPMFILE_CONFIG|RPMFILE_DOC);
01147 
01148         headerAddOrAppendEntry(h, RPMTAG_FILEFLAGS, RPM_INT32_TYPE,
01149                                &(flp->flags), 1);
01150 
01151     }
01152     headerAddEntry(h, RPMTAG_SIZE, RPM_INT32_TYPE,
01153                    &(fl->totalFileSize), 1);
01154 
01155     /* XXX This should be added always so that packages look alike.
01156      * XXX However, there is logic in files.c/depends.c that checks for
01157      * XXX existence (rather than value) that will need to change as well.
01158      */
01159     if (multiLibMask)
01160         headerAddEntry(h, RPMTAG_MULTILIBS, RPM_INT32_TYPE,
01161                        &multiLibMask, 1);
01162 
01163     if (_addDotSlash)
01164         rpmlibNeedsFeature(h, "PayloadFilesHavePrefix", "4.0-1");
01165 
01166     /* Choose how filenames are represented. */
01167     if (_noDirTokens)
01168         expandFilelist(h);
01169     else {
01170         compressFilelist(h);
01171         /* Binary packages with dirNames cannot be installed by legacy rpm. */
01172         rpmlibNeedsFeature(h, "CompressedFileNames", "3.0.4-1");
01173     }
01174 
01175   { TFI_t fi = xcalloc(sizeof(*fi), 1);
01176     char * a, * d;
01177 
01178     fi->type = TR_ADDED;
01179     loadFi(h, fi);
01180     if (fi->dnl) {
01181         free((void *)fi->dnl); fi->dnl = NULL;
01182     }
01183     if (fi->bnl) {
01184         free((void *)fi->bnl); fi->bnl = NULL;
01185     }
01186 
01187     fi->dnl = xmalloc(fi->fc * sizeof(*fi->dnl) + dpathlen);
01188     d = (char *)(fi->dnl + fi->fc);
01189     *d = '\0';
01190 
01191     fi->bnl = xmalloc(fi->fc * (sizeof(*fi->bnl) + sizeof(*fi->dil)));
01192     fi->dil = (int *)(fi->bnl + fi->fc);
01193 
01194     fi->apath = xmalloc(fi->fc * sizeof(*fi->apath) + apathlen);
01195     a = (char *)(fi->apath + fi->fc);
01196     *a = '\0';
01197 
01198     fi->actions = xcalloc(sizeof(*fi->actions), fi->fc);
01199     fi->fmapflags = xcalloc(sizeof(*fi->fmapflags), fi->fc);
01200     fi->astriplen = 0;
01201     if (fl->buildRootURL)
01202         fi->astriplen = strlen(fl->buildRootURL);
01203     fi->striplen = 0;
01204     fi->fuser = NULL;
01205     fi->fuids = xcalloc(sizeof(*fi->fuids), fi->fc);
01206     fi->fgroup = NULL;
01207     fi->fgids = xcalloc(sizeof(*fi->fgids), fi->fc);
01208 
01209     /* Make the cpio list */
01210     for (i = 0, flp = fl->fileList; i < fi->fc; i++, flp++) {
01211         char * b;
01212 
01213         /* Create disk directory and base name. */
01214         fi->dil[i] = i;
01215         fi->dnl[fi->dil[i]] = d;
01216         d = stpcpy(d, flp->diskURL);
01217 
01218         /* Make room for the dirName NUL, find start of baseName. */
01219         for (b = d; b > fi->dnl[fi->dil[i]] && *b != '/'; b--)
01220             b[1] = b[0];
01221         b++;            /* dirname's end in '/' */
01222         *b++ = '\0';    /* terminate dirname, b points to basename */
01223         fi->bnl[i] = b;
01224         d += 2;         /* skip both dirname and basename NUL's */
01225 
01226         /* Create archive path, normally adding "./" */
01227         fi->apath[i] = a;
01228         if (_addDotSlash) a = stpcpy(a, "./");
01229         a = stpcpy(a, (flp->fileURL + skipLen));
01230         a++;            /* skip apath NUL */
01231 
01232         if (flp->flags & RPMFILE_GHOST) {
01233             fi->actions[i] = FA_SKIP;
01234             continue;
01235         }
01236         fi->actions[i] = FA_COPYOUT;
01237         fi->fuids[i] = flp->fl_uid;
01238         fi->fgids[i] = flp->fl_gid;
01239         fi->fmapflags[i] =
01240                 CPIO_MAP_PATH | CPIO_MAP_MODE | CPIO_MAP_UID | CPIO_MAP_GID;
01241         if (isSrc)
01242             fi->fmapflags[i] |= CPIO_FOLLOW_SYMLINKS;
01243         if (flp->flags & RPMFILE_MULTILIB_MASK)
01244             fi->fmapflags[i] |= CPIO_MULTILIB;
01245 
01246     }
01247     if (cpioList)
01248         *cpioList = fi;
01249     else
01250         free(fi);
01251   }
01252 }
01253 
01256 static void freeFileList(FileListRec *fileList, int count)
01257 {
01258     while (count--) {
01259         FREE(fileList[count].diskURL);
01260         FREE(fileList[count].fileURL);
01261         FREE(fileList[count].langs);
01262     }
01263     FREE(fileList);
01264 }
01265 
01269 static int addFile(struct FileList *fl, const char * diskURL, struct stat *statp)
01270 {
01271     const char *fileURL = diskURL;
01272     struct stat statbuf;
01273     mode_t fileMode;
01274     uid_t fileUid;
01275     gid_t fileGid;
01276     const char *fileUname;
01277     const char *fileGname;
01278     char *lang;
01279     
01280     /* Path may have prepended buildRootURL, so locate the original filename. */
01281     /*
01282      * XXX There are 3 types of entry into addFile:
01283      *
01284      *  From                    diskUrl                 statp
01285      *  =====================================================
01286      *  processBinaryFile       path                    NULL
01287      *  processBinaryFile       glob result path        NULL
01288      *  myftw                   path                    stat
01289      *
01290      */
01291     {   const char *fileName;
01292         (void) urlPath(fileURL, &fileName);
01293         if (fl->buildRootURL && strcmp(fl->buildRootURL, "/"))
01294             fileURL += strlen(fl->buildRootURL);
01295     }
01296 
01297     /* XXX make sure '/' can be packaged also */
01298     if (*fileURL == '\0')
01299         fileURL = "/";
01300 
01301     /* If we are using a prefix, validate the file */
01302     if (!fl->inFtw && fl->prefix) {
01303         const char *prefixTest;
01304         const char *prefixPtr = fl->prefix;
01305 
01306         (void) urlPath(fileURL, &prefixTest);
01307         while (*prefixPtr && *prefixTest && (*prefixTest == *prefixPtr)) {
01308             prefixPtr++;
01309             prefixTest++;
01310         }
01311         if (*prefixPtr || (*prefixTest && *prefixTest != '/')) {
01312             rpmError(RPMERR_BADSPEC, _("File doesn't match prefix (%s): %s\n"),
01313                      fl->prefix, fileURL);
01314             fl->processingFailed = 1;
01315             return RPMERR_BADSPEC;
01316         }
01317     }
01318 
01319     if (statp == NULL) {
01320         statp = &statbuf;
01321         if (Lstat(diskURL, statp)) {
01322             rpmError(RPMERR_BADSPEC, _("File not found: %s\n"), diskURL);
01323             fl->processingFailed = 1;
01324             return RPMERR_BADSPEC;
01325         }
01326     }
01327 
01328     if ((! fl->isDir) && S_ISDIR(statp->st_mode)) {
01329         /* We use our own ftw() call, because ftw() uses stat()    */
01330         /* instead of lstat(), which causes it to follow symlinks! */
01331         /* It also has better callback support.                    */
01332         
01333         fl->inFtw = 1;  /* Flag to indicate file has buildRootURL prefixed */
01334         fl->isDir = 1;  /* Keep it from following myftw() again         */
01335         myftw(diskURL, 16, (myftwFunc) addFile, fl);
01336         fl->isDir = 0;
01337         fl->inFtw = 0;
01338         return 0;
01339     }
01340 
01341     fileMode = statp->st_mode;
01342     fileUid = statp->st_uid;
01343     fileGid = statp->st_gid;
01344 
01345     if (S_ISDIR(fileMode) && fl->cur_ar.ar_dmodestr) {
01346         fileMode &= S_IFMT;
01347         fileMode |= fl->cur_ar.ar_dmode;
01348     } else if (fl->cur_ar.ar_fmodestr != NULL) {
01349         fileMode &= S_IFMT;
01350         fileMode |= fl->cur_ar.ar_fmode;
01351     }
01352     if (fl->cur_ar.ar_user) {
01353         fileUname = getUnameS(fl->cur_ar.ar_user);
01354     } else {
01355         fileUname = getUname(fileUid);
01356     }
01357     if (fl->cur_ar.ar_group) {
01358         fileGname = getGnameS(fl->cur_ar.ar_group);
01359     } else {
01360         fileGname = getGname(fileGid);
01361     }
01362         
01363 #if 0   /* XXX this looks dumb to me */
01364     if (! (fileUname && fileGname)) {
01365         rpmError(RPMERR_BADSPEC, _("Bad owner/group: %s\n"), diskName);
01366         fl->processingFailed = 1;
01367         return RPMERR_BADSPEC;
01368     }
01369 #else
01370     /* Default user/group to builder's user/group */
01371     if (fileUname == NULL)
01372         fileUname = getUname(getuid());
01373     if (fileGname == NULL)
01374         fileGname = getGname(getgid());
01375 #endif
01376     
01377     rpmMessage(RPMMESS_DEBUG, _("File %4d: %07o %s.%s\t %s\n"), fl->fileCount,
01378         fileMode, fileUname, fileGname, fileURL);
01379 
01380     /* Add to the file list */
01381     if (fl->fileListRecsUsed == fl->fileListRecsAlloced) {
01382         fl->fileListRecsAlloced += 128;
01383         fl->fileList = xrealloc(fl->fileList,
01384                         fl->fileListRecsAlloced * sizeof(*(fl->fileList)));
01385     }
01386             
01387     {   FileListRec * flp = &fl->fileList[fl->fileListRecsUsed];
01388 
01389         flp->fl_st = *statp;    /* structure assignment */
01390         flp->fl_mode = fileMode;
01391         flp->fl_uid = fileUid;
01392         flp->fl_gid = fileGid;
01393 
01394         flp->fileURL = xstrdup(fileURL);
01395         flp->diskURL = xstrdup(diskURL);
01396         flp->uname = fileUname;
01397         flp->gname = fileGname;
01398 
01399         if (fl->currentLangs && fl->nLangs > 0) {
01400             char *ncl;
01401             size_t nl = 0;
01402             int i;
01403             
01404             for (i = 0; i < fl->nLangs; i++)
01405                 nl += strlen(fl->currentLangs[i]) + 1;
01406 
01407             flp->langs = ncl = xmalloc(nl);
01408             for (i = 0; i < fl->nLangs; i++) {
01409                 const char *ocl;
01410                 if (i)  *ncl++ = '|';
01411                 for (ocl = fl->currentLangs[i]; *ocl; ocl++)
01412                         *ncl++ = *ocl;
01413                 *ncl = '\0';
01414             }
01415         } else if (! parseForRegexLang(fileURL, &lang)) {
01416             flp->langs = xstrdup(lang);
01417         } else {
01418             flp->langs = xstrdup("");
01419         }
01420 
01421         flp->flags = fl->currentFlags;
01422         flp->specdFlags = fl->currentSpecdFlags;
01423         flp->verifyFlags = fl->currentVerifyFlags;
01424 
01425         if (multiLib
01426             && !(flp->flags & RPMFILE_MULTILIB_MASK)
01427             && !parseForRegexMultiLib(fileURL))
01428             flp->flags |= multiLib;
01429 
01430         fl->totalFileSize += flp->fl_size;
01431     }
01432 
01433     fl->fileListRecsUsed++;
01434     fl->fileCount++;
01435 
01436     return 0;
01437 }
01438 
01442 static int processBinaryFile(/*@unused@*/Package pkg, struct FileList *fl,
01443         const char *fileURL)
01444 {
01445     int doGlob;
01446     const char *diskURL = NULL;
01447     int rc = 0;
01448     
01449     doGlob = myGlobPatternP(fileURL);
01450 
01451     /* Check that file starts with leading "/" */
01452     {   const char * fileName;
01453         (void) urlPath(fileURL, &fileName);
01454         if (*fileName != '/') {
01455             rpmError(RPMERR_BADSPEC, _("File needs leading \"/\": %s\n"),
01456                         fileName);
01457             rc = 1;
01458             goto exit;
01459         }
01460     }
01461     
01462     /* Copy file name or glob pattern removing multiple "/" chars. */
01463     /*
01464      * Note: rpmGetPath should guarantee a "canonical" path. That means
01465      * that the following pathologies should be weeded out:
01466      *          //bin//sh
01467      *          //usr//bin/
01468      *          /.././../usr/../bin//./sh
01469      */
01470     diskURL = rpmGenPath(fl->buildRootURL, NULL, fileURL);
01471 
01472     if (doGlob) {
01473         const char ** argv = NULL;
01474         int argc = 0;
01475         int i;
01476 
01477         rc = rpmGlob(diskURL, &argc, &argv);
01478         if (rc == 0 && argc >= 1 && !myGlobPatternP(argv[0])) {
01479             for (i = 0; i < argc; i++) {
01480                 rc = addFile(fl, argv[i], NULL);
01481                 free((void *)argv[i]);
01482             }
01483             free((void *)argv);
01484         } else {
01485             rpmError(RPMERR_BADSPEC, _("File not found by glob: %s\n"),
01486                         diskURL);
01487             rc = 1;
01488         }
01489     } else {
01490         rc = addFile(fl, diskURL, NULL);
01491     }
01492 
01493 exit:
01494     if (diskURL)
01495         free((void *)diskURL);
01496     if (rc)
01497         fl->processingFailed = 1;
01498     return rc;
01499 }
01500 
01503 static int processPackageFiles(Spec spec, Package pkg,
01504                                int installSpecialDoc, int test)
01505 {
01506     struct FileList fl;
01507     char *s, **files, **fp;
01508     const char *fileName;
01509     char buf[BUFSIZ];
01510     AttrRec specialDocAttrRec;
01511     char *specialDoc = NULL;
01512 
01513 #ifdef MULTILIB
01514     multiLib = rpmExpandNumeric("%{_multilibno}");
01515     if (multiLib)
01516         multiLib = RPMFILE_MULTILIB(multiLib);
01517 #endif /* MULTILIB */
01518     
01519     nullAttrRec(&specialDocAttrRec);
01520     pkg->cpioList = NULL;
01521 
01522     if (pkg->fileFile) {
01523         const char *ffn;
01524         FD_t fd;
01525 
01526         /* XXX W2DO? urlPath might be useful here. */
01527         if (*pkg->fileFile == '/') {
01528             ffn = rpmGetPath(pkg->fileFile, NULL);
01529         } else {
01530             /* XXX FIXME: add %{_buildsubdir} */
01531             ffn = rpmGetPath("%{_builddir}/",
01532                 (spec->buildSubdir ? spec->buildSubdir : "") ,
01533                 "/", pkg->fileFile, NULL);
01534         }
01535         fd = Fopen(ffn, "r.fpio");
01536 
01537         if (fd == NULL || Ferror(fd)) {
01538             rpmError(RPMERR_BADFILENAME,
01539                 _("Could not open %%files file %s: %s\n"),
01540                 ffn, Fstrerror(fd));
01541             return RPMERR_BADFILENAME;
01542         }
01543         free((void *)ffn);
01544 
01545         while (fgets(buf, sizeof(buf), (FILE *)fdGetFp(fd))) {
01546             handleComments(buf);
01547             if (expandMacros(spec, spec->macros, buf, sizeof(buf))) {
01548                 rpmError(RPMERR_BADSPEC, _("line: %s\n"), buf);
01549                 return RPMERR_BADSPEC;
01550             }
01551             appendStringBuf(pkg->fileList, buf);
01552         }
01553         Fclose(fd);
01554     }
01555     
01556     /* Init the file list structure */
01557 
01558     /* XXX spec->buildRootURL == NULL, then xstrdup("") is returned */
01559     fl.buildRootURL = rpmGenPath(spec->rootURL, spec->buildRootURL, NULL);
01560 
01561     if (headerGetEntry(pkg->header, RPMTAG_DEFAULTPREFIX,
01562                        NULL, (void **)&fl.prefix, NULL)) {
01563         fl.prefix = xstrdup(fl.prefix);
01564     } else {
01565         fl.prefix = NULL;
01566     }
01567 
01568     fl.fileCount = 0;
01569     fl.totalFileSize = 0;
01570     fl.processingFailed = 0;
01571 
01572     fl.passedSpecialDoc = 0;
01573     fl.isSpecialDoc = 0;
01574 
01575     fl.isDir = 0;
01576     fl.inFtw = 0;
01577     fl.currentFlags = 0;
01578     fl.currentVerifyFlags = 0;
01579     
01580     nullAttrRec(&fl.cur_ar);
01581     nullAttrRec(&fl.def_ar);
01582 
01583     fl.defVerifyFlags = RPMVERIFY_ALL;
01584     fl.nLangs = 0;
01585     fl.currentLangs = NULL;
01586 
01587     fl.currentSpecdFlags = 0;
01588     fl.defSpecdFlags = 0;
01589 
01590     fl.docDirCount = 0;
01591     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/doc");
01592     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/man");
01593     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/info");
01594     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/X11R6/man");
01595     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/share/doc");
01596     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/share/man");
01597     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/share/info");
01598     fl.docDirs[fl.docDirCount++] = rpmGetPath("%{_docdir}", NULL);
01599     fl.docDirs[fl.docDirCount++] = rpmGetPath("%{_mandir}", NULL);
01600     fl.docDirs[fl.docDirCount++] = rpmGetPath("%{_infodir}", NULL);
01601     
01602     fl.fileList = NULL;
01603     fl.fileListRecsAlloced = 0;
01604     fl.fileListRecsUsed = 0;
01605 
01606     s = getStringBuf(pkg->fileList);
01607     files = splitString(s, strlen(s), '\n');
01608 
01609     for (fp = files; *fp != NULL; fp++) {
01610         s = *fp;
01611         SKIPSPACE(s);
01612         if (*s == '\0')
01613             continue;
01614         fileName = NULL;
01615         strcpy(buf, s);
01616         
01617         /* Reset for a new line in %files */
01618         fl.isDir = 0;
01619         fl.inFtw = 0;
01620         fl.currentFlags = 0;
01621         /* turn explicit flags into %def'd ones (gosh this is hacky...) */
01622         fl.currentSpecdFlags = fl.defSpecdFlags>>8;
01623         fl.currentVerifyFlags = fl.defVerifyFlags;
01624         fl.isSpecialDoc = 0;
01625 
01626         /* XXX should reset to %deflang value */
01627         if (fl.currentLangs) {
01628             int i;
01629             for (i = 0; i < fl.nLangs; i++)
01630                 free((void *)fl.currentLangs[i]);
01631             FREE(fl.currentLangs);
01632         }
01633         fl.nLangs = 0;
01634 
01635         dupAttrRec(&fl.def_ar, &fl.cur_ar);
01636 
01637         if (parseForVerify(buf, &fl))
01638             continue;
01639         if (parseForAttr(buf, &fl))
01640             continue;
01641         if (parseForConfig(buf, &fl))
01642             continue;
01643         if (parseForLang(buf, &fl))
01644             continue;
01645         if (parseForSimple(spec, pkg, buf, &fl, &fileName))
01646             continue;
01647         if (fileName == NULL)
01648             continue;
01649 
01650         if (fl.isSpecialDoc) {
01651             /* Save this stuff for last */
01652             FREE(specialDoc);
01653             specialDoc = xstrdup(fileName);
01654             dupAttrRec(&fl.cur_ar, &specialDocAttrRec);
01655         } else {
01656             processBinaryFile(pkg, &fl, fileName);
01657         }
01658     }
01659 
01660     /* Now process special doc, if there is one */
01661     if (specialDoc) {
01662         if (installSpecialDoc) {
01663             doScript(spec, RPMBUILD_STRINGBUF, "%doc", pkg->specialDoc, test);
01664         }
01665 
01666         /* Reset for %doc */
01667         fl.isDir = 0;
01668         fl.inFtw = 0;
01669         fl.currentFlags = 0;
01670         fl.currentVerifyFlags = 0;
01671 
01672         /* XXX should reset to %deflang value */
01673         if (fl.currentLangs) {
01674             int i;
01675             for (i = 0; i < fl.nLangs; i++)
01676                 free((void *)fl.currentLangs[i]);
01677             FREE(fl.currentLangs);
01678         }
01679         fl.nLangs = 0;
01680 
01681         dupAttrRec(&specialDocAttrRec, &fl.cur_ar);
01682         freeAttrRec(&specialDocAttrRec);
01683 
01684         processBinaryFile(pkg, &fl, specialDoc);
01685 
01686         FREE(specialDoc);
01687     }
01688     
01689     freeSplitString(files);
01690 
01691     if (fl.processingFailed)
01692         goto exit;
01693 
01694     /* Verify that file attributes scope over hardlinks correctly. */
01695     checkHardLinks(&fl);
01696 
01697     genCpioListAndHeader(&fl, (TFI_t *)&pkg->cpioList, pkg->header, 0);
01698 
01699     if (spec->timeCheck)
01700         timeCheck(spec->timeCheck, pkg->header);
01701     
01702 exit:
01703     FREE(fl.buildRootURL);
01704     FREE(fl.prefix);
01705 
01706     freeAttrRec(&fl.cur_ar);
01707     freeAttrRec(&fl.def_ar);
01708 
01709     if (fl.currentLangs) {
01710         int i;
01711         for (i = 0; i < fl.nLangs; i++)
01712             free((void *)fl.currentLangs[i]);
01713         FREE(fl.currentLangs);
01714     }
01715 
01716     freeFileList(fl.fileList, fl.fileListRecsUsed);
01717     while (fl.docDirCount--) {
01718         FREE(fl.docDirs[fl.docDirCount]);
01719     }
01720     return fl.processingFailed;
01721 }
01722 
01723 void initSourceHeader(Spec spec)
01724 {
01725     HeaderIterator hi;
01726     int_32 tag, type, count;
01727     const void * ptr;
01728 
01729     spec->sourceHeader = headerNew();
01730     /* Only specific tags are added to the source package header */
01731     for (hi = headerInitIterator(spec->packages->header);
01732         headerNextIterator(hi, &tag, &type, &ptr, &count);
01733         ptr = headerFreeData(ptr, type))
01734     {
01735         switch (tag) {
01736         case RPMTAG_NAME:
01737         case RPMTAG_VERSION:
01738         case RPMTAG_RELEASE:
01739         case RPMTAG_EPOCH:
01740         case RPMTAG_SUMMARY:
01741         case RPMTAG_DESCRIPTION:
01742         case RPMTAG_PACKAGER:
01743         case RPMTAG_DISTRIBUTION:
01744         case RPMTAG_DISTURL:
01745         case RPMTAG_VENDOR:
01746         case RPMTAG_LICENSE:
01747         case RPMTAG_GROUP:
01748         case RPMTAG_OS:
01749         case RPMTAG_ARCH:
01750         case RPMTAG_CHANGELOGTIME:
01751         case RPMTAG_CHANGELOGNAME:
01752         case RPMTAG_CHANGELOGTEXT:
01753         case RPMTAG_URL:
01754         case HEADER_I18NTABLE:
01755             headerAddEntry(spec->sourceHeader, tag, type, ptr, count);
01756             break;
01757         default:
01758             /* do not copy */
01759             break;
01760         }
01761     }
01762     headerFreeIterator(hi);
01763 
01764     /* Add the build restrictions */
01765     for (hi = headerInitIterator(spec->buildRestrictions);
01766         headerNextIterator(hi, &tag, &type, &ptr, &count);
01767         ptr = headerFreeData(ptr, type))
01768     {
01769         headerAddEntry(spec->sourceHeader, tag, type, ptr, count);
01770     }
01771     headerFreeIterator(hi);
01772 
01773     if (spec->buildArchitectureCount) {
01774         headerAddEntry(spec->sourceHeader, RPMTAG_BUILDARCHS,
01775                        RPM_STRING_ARRAY_TYPE,
01776                        spec->buildArchitectures, spec->buildArchitectureCount);
01777     }
01778 }
01779 
01780 int processSourceFiles(Spec spec)
01781 {
01782     struct Source *srcPtr;
01783     StringBuf sourceFiles;
01784     int x, isSpec = 1;
01785     struct FileList fl;
01786     char *s, **files, **fp;
01787     Package pkg;
01788 
01789     sourceFiles = newStringBuf();
01790 
01791     /* XXX
01792      * XXX This is where the source header for noarch packages needs
01793      * XXX to be initialized.
01794      */
01795     if (spec->sourceHeader == NULL)
01796         initSourceHeader(spec);
01797 
01798     /* Construct the file list and source entries */
01799     appendLineStringBuf(sourceFiles, spec->specFile);
01800     for (srcPtr = spec->sources; srcPtr != NULL; srcPtr = srcPtr->next) {
01801         if (srcPtr->flags & RPMBUILD_ISSOURCE) {
01802             headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_SOURCE,
01803                                    RPM_STRING_ARRAY_TYPE, &srcPtr->source, 1);
01804             if (srcPtr->flags & RPMBUILD_ISNO) {
01805                 headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_NOSOURCE,
01806                                        RPM_INT32_TYPE, &srcPtr->num, 1);
01807             }
01808         }
01809         if (srcPtr->flags & RPMBUILD_ISPATCH) {
01810             headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_PATCH,
01811                                    RPM_STRING_ARRAY_TYPE, &srcPtr->source, 1);
01812             if (srcPtr->flags & RPMBUILD_ISNO) {
01813                 headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_NOPATCH,
01814                                        RPM_INT32_TYPE, &srcPtr->num, 1);
01815             }
01816         }
01817 
01818       { const char *s;
01819         s = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
01820                 "%{_sourcedir}/", srcPtr->source, NULL);
01821         appendLineStringBuf(sourceFiles, s);
01822         free((void *)s);
01823       }
01824     }
01825 
01826     for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
01827         for (srcPtr = pkg->icon; srcPtr != NULL; srcPtr = srcPtr->next) {
01828             const char *s;
01829             s = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
01830                 "%{_sourcedir}/", srcPtr->source, NULL);
01831             appendLineStringBuf(sourceFiles, s);
01832             free((void *)s);
01833         }
01834     }
01835 
01836     spec->sourceCpioList = NULL;
01837 
01838     fl.fileList = xmalloc((spec->numSources + 1) * sizeof(FileListRec));
01839     fl.processingFailed = 0;
01840     fl.fileListRecsUsed = 0;
01841     fl.totalFileSize = 0;
01842     fl.prefix = NULL;
01843 
01844     s = getStringBuf(sourceFiles);
01845     files = splitString(s, strlen(s), '\n');
01846 
01847     /* The first source file is the spec file */
01848     x = 0;
01849     for (fp = files; *fp != NULL; fp++) {
01850         const char * diskURL, *diskPath;
01851         FileListRec *flp;
01852 
01853         diskURL = *fp;
01854         SKIPSPACE(diskURL);
01855         if (! *diskURL)
01856             continue;
01857 
01858         flp = &fl.fileList[x];
01859 
01860         flp->flags = isSpec ? RPMFILE_SPECFILE : 0;
01861         /* files with leading ! are no source files */
01862         if (*diskURL == '!') {
01863             flp->flags |= RPMFILE_GHOST;
01864             diskURL++;
01865         }
01866 
01867         urlPath(diskURL, &diskPath);
01868 
01869         flp->diskURL = xstrdup(diskURL);
01870         diskPath = strrchr(diskPath, '/');
01871         if (diskPath)
01872             diskPath++;
01873         else
01874             diskPath = diskURL;
01875 
01876         flp->fileURL = xstrdup(diskPath);
01877         flp->verifyFlags = RPMVERIFY_ALL;
01878 
01879         if (Stat(diskURL, &flp->fl_st)) {
01880             rpmError(RPMERR_BADSPEC, _("Bad file: %s: %s\n"),
01881                 diskURL, strerror(errno));
01882             fl.processingFailed = 1;
01883         }
01884 
01885         flp->uname = getUname(flp->fl_uid);
01886         flp->gname = getGname(flp->fl_gid);
01887         flp->langs = xstrdup("");
01888         
01889         fl.totalFileSize += flp->fl_size;
01890         
01891         if (! (flp->uname && flp->gname)) {
01892             rpmError(RPMERR_BADSPEC, _("Bad owner/group: %s\n"), diskURL);
01893             fl.processingFailed = 1;
01894         }
01895 
01896         isSpec = 0;
01897         x++;
01898     }
01899     fl.fileListRecsUsed = x;
01900     freeSplitString(files);
01901 
01902     if (! fl.processingFailed) {
01903         genCpioListAndHeader