Add rpmfcAddFileDep() to handle file dependencies securely
[platform/upstream/rpm.git] / build / files.c
1 /** \ingroup rpmbuild
2  * \file build/files.c
3  *  The post-build, pre-packaging file tree walk to assemble the package
4  *  manifest.
5  */
6
7 #include "system.h"
8
9 #define MYALLPERMS      07777
10
11 #include <regex.h>
12
13 #include <rpm/rpmbuild.h>
14 #include <rpm/rpmpgp.h>
15 #include <rpm/argv.h>
16 #include <rpm/rpmfc.h>
17 #include <rpm/rpmfileutil.h>    /* rpmDoDigest() */
18 #include <rpm/rpmlog.h>
19
20 #include "rpmio/rpmio_internal.h"       /* XXX rpmioSlurp */
21 #include "rpmio/fts.h"
22 #include "lib/cpio.h"
23 #include "lib/rpmfi_internal.h" /* XXX pretty much all rpmfi internals... */
24 #include "lib/rpmte_internal.h" /* XXX rpmte init */
25 #include "lib/legacy.h"         /* XXX expandFileList, compressFileList */
26 #include "build/buildio.h"
27
28 #include "debug.h"
29
30 #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
31 #define SKIPWHITE(_x)   {while(*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;}
32 #define SKIPNONWHITE(_x){while(*(_x) &&!(risspace(*_x) || *(_x) == ',')) (_x)++;}
33
34 #define MAXDOCDIR 1024
35
36 /**
37  */
38 typedef enum specdFlags_e {
39     SPECD_DEFFILEMODE   = (1 << 0),
40     SPECD_DEFDIRMODE    = (1 << 1),
41     SPECD_DEFUID        = (1 << 2),
42     SPECD_DEFGID        = (1 << 3),
43     SPECD_DEFVERIFY     = (1 << 4),
44
45     SPECD_FILEMODE      = (1 << 8),
46     SPECD_DIRMODE       = (1 << 9),
47     SPECD_UID           = (1 << 10),
48     SPECD_GID           = (1 << 11),
49     SPECD_VERIFY        = (1 << 12)
50 } specdFlags;
51
52 /**
53  */
54 typedef struct FileListRec_s {
55     struct stat fl_st;
56 #define fl_dev  fl_st.st_dev
57 #define fl_ino  fl_st.st_ino
58 #define fl_mode fl_st.st_mode
59 #define fl_nlink fl_st.st_nlink
60 #define fl_uid  fl_st.st_uid
61 #define fl_gid  fl_st.st_gid
62 #define fl_rdev fl_st.st_rdev
63 #define fl_size fl_st.st_size
64 #define fl_mtime fl_st.st_mtime
65
66     char *diskURL;              /* get file from here       */
67     char *fileURL;              /* filename in cpio archive */
68     const char *uname;
69     const char *gname;
70     unsigned    flags;
71     specdFlags  specdFlags;     /* which attributes have been explicitly specified. */
72     rpmVerifyFlags verifyFlags;
73     char *langs;                /* XXX locales separated with | */
74 } * FileListRec;
75
76 /**
77  */
78 typedef struct AttrRec_s {
79     char *ar_fmodestr;
80     char *ar_dmodestr;
81     char *ar_user;
82     char *ar_group;
83     mode_t      ar_fmode;
84     mode_t      ar_dmode;
85 } * AttrRec;
86
87 static struct AttrRec_s root_ar = { NULL, NULL, "root", "root", 0, 0 };
88
89 /* list of files */
90 static StringBuf check_fileList = NULL;
91
92 /**
93  * Package file tree walk data.
94  */
95 typedef struct FileList_s {
96     char * buildRootURL;
97     char * prefix;
98
99     int fileCount;
100     rpm_off_t totalFileSize;
101     int processingFailed;
102
103     int passedSpecialDoc;
104     int isSpecialDoc;
105
106     int noGlob;
107     unsigned devtype;
108     unsigned devmajor;
109     int devminor;
110     
111     int isDir;
112     int inFtw;
113     rpmfileAttrs currentFlags;
114     specdFlags currentSpecdFlags;
115     rpmVerifyFlags currentVerifyFlags;
116     struct AttrRec_s cur_ar;
117     struct AttrRec_s def_ar;
118     specdFlags defSpecdFlags;
119     rpmVerifyFlags defVerifyFlags;
120     int nLangs;
121     char ** currentLangs;
122
123     /* Hard coded limit of MAXDOCDIR docdirs.         */
124     /* If you break it you are doing something wrong. */
125     char * docDirs[MAXDOCDIR];
126     int docDirCount;
127     
128     FileListRec fileList;
129     int fileListRecsAlloced;
130     int fileListRecsUsed;
131 } * FileList;
132
133 /**
134  */
135 static void nullAttrRec(AttrRec ar)
136 {
137     ar->ar_fmodestr = NULL;
138     ar->ar_dmodestr = NULL;
139     ar->ar_user = NULL;
140     ar->ar_group = NULL;
141     ar->ar_fmode = 0;
142     ar->ar_dmode = 0;
143 }
144
145 /**
146  */
147 static void freeAttrRec(AttrRec ar)
148 {
149     ar->ar_fmodestr = _free(ar->ar_fmodestr);
150     ar->ar_dmodestr = _free(ar->ar_dmodestr);
151     ar->ar_user = _free(ar->ar_user);
152     ar->ar_group = _free(ar->ar_group);
153     /* XXX doesn't free ar (yet) */
154     return;
155 }
156
157 /**
158  */
159 static void dupAttrRec(const AttrRec oar, AttrRec nar)
160 {
161     if (oar == nar)
162         return;
163     freeAttrRec(nar);
164     nar->ar_fmodestr = (oar->ar_fmodestr ? xstrdup(oar->ar_fmodestr) : NULL);
165     nar->ar_dmodestr = (oar->ar_dmodestr ? xstrdup(oar->ar_dmodestr) : NULL);
166     nar->ar_user = (oar->ar_user ? xstrdup(oar->ar_user) : NULL);
167     nar->ar_group = (oar->ar_group ? xstrdup(oar->ar_group) : NULL);
168     nar->ar_fmode = oar->ar_fmode;
169     nar->ar_dmode = oar->ar_dmode;
170 }
171
172 #if 0
173 /**
174  */
175 static void dumpAttrRec(const char * msg, AttrRec ar)
176 {
177     if (msg)
178         fprintf(stderr, "%s:\t", msg);
179     fprintf(stderr, "(%s, %s, %s, %s)\n",
180         ar->ar_fmodestr,
181         ar->ar_user,
182         ar->ar_group,
183         ar->ar_dmodestr);
184 }
185 #endif
186
187 /**
188  * strtokWithQuotes.
189  * @param s
190  * @param delim
191  */
192 static char *strtokWithQuotes(char *s, const char *delim)
193 {
194     static char *olds = NULL;
195     char *token;
196
197     if (s == NULL)
198         s = olds;
199     if (s == NULL)
200         return NULL;
201
202     /* Skip leading delimiters */
203     s += strspn(s, delim);
204     if (*s == '\0')
205         return NULL;
206
207     /* Find the end of the token.  */
208     token = s;
209     if (*token == '"') {
210         token++;
211         /* Find next " char */
212         s = strchr(token, '"');
213     } else {
214         s = strpbrk(token, delim);
215     }
216
217     /* Terminate it */
218     if (s == NULL) {
219         /* This token finishes the string */
220         olds = strchr(token, '\0');
221     } else {
222         /* Terminate the token and make olds point past it */
223         *s = '\0';
224         olds = s+1;
225     }
226
227     return token;
228 }
229
230 /**
231  */
232 static void timeCheck(int tc, Header h)
233 {
234     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
235     HFD_t hfd = headerFreeData;
236     rpm_time_t * mtime;
237     const char ** files;
238     rpmTagType fnt;
239     rpm_count_t count, x;
240     time_t currentTime = time(NULL);
241
242     x = hge(h, RPMTAG_OLDFILENAMES, &fnt, (rpm_data_t *) &files, &count);
243     x = hge(h, RPMTAG_FILEMTIMES, NULL, (rpm_data_t *) &mtime, NULL);
244     
245     for (x = 0; x < count; x++) {
246         if ((currentTime - mtime[x]) > tc)
247             rpmlog(RPMLOG_WARNING, _("TIMECHECK failure: %s\n"), files[x]);
248     }
249     files = hfd(files, fnt);
250 }
251
252 /**
253  */
254 typedef const struct VFA {
255     const char const * attribute;
256     int not;
257     int flag;
258 } VFA_t;
259
260 /**
261  */
262 VFA_t const verifyAttrs[] = {
263     { "md5",    0,      RPMVERIFY_MD5 },
264     { "size",   0,      RPMVERIFY_FILESIZE },
265     { "link",   0,      RPMVERIFY_LINKTO },
266     { "user",   0,      RPMVERIFY_USER },
267     { "group",  0,      RPMVERIFY_GROUP },
268     { "mtime",  0,      RPMVERIFY_MTIME },
269     { "mode",   0,      RPMVERIFY_MODE },
270     { "rdev",   0,      RPMVERIFY_RDEV },
271     { NULL, 0,  0 }
272 };
273
274 /**
275  * Parse %verify and %defverify from file manifest.
276  * @param buf           current spec file line
277  * @param fl            package file tree walk data
278  * @return              RPMRC_OK on success
279  */
280 static rpmRC parseForVerify(const char * buf, FileList fl)
281 {
282     char *p, *pe, *q;
283     const char *name;
284     rpmVerifyFlags *resultVerify;
285     int negated;
286     rpmVerifyFlags verifyFlags;
287     specdFlags * specdFlags;
288
289     if ((p = strstr(buf, (name = "%verify"))) != NULL) {
290         resultVerify = &(fl->currentVerifyFlags);
291         specdFlags = &fl->currentSpecdFlags;
292     } else if ((p = strstr(buf, (name = "%defverify"))) != NULL) {
293         resultVerify = &(fl->defVerifyFlags);
294         specdFlags = &fl->defSpecdFlags;
295     } else
296         return RPMRC_OK;
297
298     for (pe = p; (pe-p) < strlen(name); pe++)
299         *pe = ' ';
300
301     SKIPSPACE(pe);
302
303     if (*pe != '(') {
304         rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
305         fl->processingFailed = 1;
306         return RPMRC_FAIL;
307     }
308
309     /* Bracket %*verify args */
310     *pe++ = ' ';
311     for (p = pe; *pe && *pe != ')'; pe++)
312         {};
313
314     if (*pe == '\0') {
315         rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
316         fl->processingFailed = 1;
317         return RPMRC_FAIL;
318     }
319
320     /* Localize. Erase parsed string */
321     q = alloca((pe-p) + 1);
322     strncpy(q, p, pe-p);
323     q[pe-p] = '\0';
324     while (p <= pe)
325         *p++ = ' ';
326
327     negated = 0;
328     verifyFlags = RPMVERIFY_NONE;
329
330     for (p = q; *p != '\0'; p = pe) {
331         SKIPWHITE(p);
332         if (*p == '\0')
333             break;
334         pe = p;
335         SKIPNONWHITE(pe);
336         if (*pe != '\0')
337             *pe++ = '\0';
338
339         {   VFA_t *vfa;
340             for (vfa = verifyAttrs; vfa->attribute != NULL; vfa++) {
341                 if (strcmp(p, vfa->attribute))
342                     continue;
343                 verifyFlags |= vfa->flag;
344                 break;
345             }
346             if (vfa->attribute)
347                 continue;
348         }
349
350         if (!strcmp(p, "not")) {
351             negated ^= 1;
352         } else {
353             rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p);
354             fl->processingFailed = 1;
355             return RPMRC_FAIL;
356         }
357     }
358
359     *resultVerify = negated ? ~(verifyFlags) : verifyFlags;
360     *specdFlags |= SPECD_VERIFY;
361
362     return RPMRC_OK;
363 }
364
365 #define isAttrDefault(_ars)     ((_ars)[0] == '-' && (_ars)[1] == '\0')
366
367 /**
368  * Parse %dev from file manifest.
369  * @param buf           current spec file line
370  * @param fl            package file tree walk data
371  * @return              RPMRC_OK on success
372  */
373 static rpmRC parseForDev(const char * buf, FileList fl)
374 {
375     const char * name;
376     const char * errstr = NULL;
377     char *p, *pe, *q;
378     int rc = RPMRC_FAIL;        /* assume error */
379
380     if ((p = strstr(buf, (name = "%dev"))) == NULL)
381         return RPMRC_OK;
382
383     for (pe = p; (pe-p) < strlen(name); pe++)
384         *pe = ' ';
385     SKIPSPACE(pe);
386
387     if (*pe != '(') {
388         errstr = "'('";
389         goto exit;
390     }
391
392     /* Bracket %dev args */
393     *pe++ = ' ';
394     for (p = pe; *pe && *pe != ')'; pe++)
395         {};
396     if (*pe != ')') {
397         errstr = "')'";
398         goto exit;
399     }
400
401     /* Localize. Erase parsed string */
402     q = alloca((pe-p) + 1);
403     strncpy(q, p, pe-p);
404     q[pe-p] = '\0';
405     while (p <= pe)
406         *p++ = ' ';
407
408     p = q; SKIPWHITE(p);
409     pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
410     if (*p == 'b')
411         fl->devtype = 'b';
412     else if (*p == 'c')
413         fl->devtype = 'c';
414     else {
415         errstr = "devtype";
416         goto exit;
417     }
418
419     p = pe; SKIPWHITE(p);
420     pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
421     for (pe = p; *pe && risdigit(*pe); pe++)
422         {} ;
423     if (*pe == '\0') {
424         fl->devmajor = atoi(p);
425         if (!(fl->devmajor >= 0 && fl->devmajor < 256)) {
426             errstr = "devmajor";
427             goto exit;
428         }
429         pe++;
430     } else {
431         errstr = "devmajor";
432         goto exit;
433     }
434
435     p = pe; SKIPWHITE(p);
436     pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
437     for (pe = p; *pe && risdigit(*pe); pe++)
438         {} ;
439     if (*pe == '\0') {
440         fl->devminor = atoi(p);
441         if (!(fl->devminor >= 0 && fl->devminor < 256)) {
442             errstr = "devminor";
443             goto exit;
444         }
445         pe++;
446     } else {
447         errstr = "devminor";
448         goto exit;
449     }
450
451     fl->noGlob = 1;
452
453     rc = RPMRC_OK;
454
455 exit:
456     if (rc) {
457         rpmlog(RPMLOG_ERR, _("Missing %s in %s %s\n"), errstr, name, p);
458         fl->processingFailed = 1;
459     }
460     return rc;
461 }
462
463 /**
464  * Parse %attr and %defattr from file manifest.
465  * @param buf           current spec file line
466  * @param fl            package file tree walk data
467  * @return              0 on success
468  */
469 static int parseForAttr(const char * buf, FileList fl)
470 {
471     const char *name;
472     char *p, *pe, *q;
473     int x;
474     struct AttrRec_s arbuf;
475     AttrRec ar = &arbuf, ret_ar;
476     specdFlags * specdFlags;
477
478     if ((p = strstr(buf, (name = "%attr"))) != NULL) {
479         ret_ar = &(fl->cur_ar);
480         specdFlags = &fl->currentSpecdFlags;
481     } else if ((p = strstr(buf, (name = "%defattr"))) != NULL) {
482         ret_ar = &(fl->def_ar);
483         specdFlags = &fl->defSpecdFlags;
484     } else
485         return 0;
486
487     for (pe = p; (pe-p) < strlen(name); pe++)
488         *pe = ' ';
489
490     SKIPSPACE(pe);
491
492     if (*pe != '(') {
493         rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
494         fl->processingFailed = 1;
495         return RPMRC_FAIL;
496     }
497
498     /* Bracket %*attr args */
499     *pe++ = ' ';
500     for (p = pe; *pe && *pe != ')'; pe++)
501         {};
502
503     if (ret_ar == &(fl->def_ar)) {      /* %defattr */
504         q = pe;
505         q++;
506         SKIPSPACE(q);
507         if (*q != '\0') {
508             rpmlog(RPMLOG_ERR,
509                      _("Non-white space follows %s(): %s\n"), name, q);
510             fl->processingFailed = 1;
511             return RPMRC_FAIL;
512         }
513     }
514
515     /* Localize. Erase parsed string */
516     q = alloca((pe-p) + 1);
517     strncpy(q, p, pe-p);
518     q[pe-p] = '\0';
519     while (p <= pe)
520         *p++ = ' ';
521
522     nullAttrRec(ar);
523
524     p = q; SKIPWHITE(p);
525     if (*p != '\0') {
526         pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
527         ar->ar_fmodestr = p;
528         p = pe; SKIPWHITE(p);
529     }
530     if (*p != '\0') {
531         pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
532         ar->ar_user = p;
533         p = pe; SKIPWHITE(p);
534     }
535     if (*p != '\0') {
536         pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
537         ar->ar_group = p;
538         p = pe; SKIPWHITE(p);
539     }
540     if (*p != '\0' && ret_ar == &(fl->def_ar)) {        /* %defattr */
541         pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
542         ar->ar_dmodestr = p;
543         p = pe; SKIPWHITE(p);
544     }
545
546     if (!(ar->ar_fmodestr && ar->ar_user && ar->ar_group) || *p != '\0') {
547         rpmlog(RPMLOG_ERR, _("Bad syntax: %s(%s)\n"), name, q);
548         fl->processingFailed = 1;
549         return RPMRC_FAIL;
550     }
551
552     /* Do a quick test on the mode argument and adjust for "-" */
553     if (ar->ar_fmodestr && !isAttrDefault(ar->ar_fmodestr)) {
554         unsigned int ui;
555         x = sscanf(ar->ar_fmodestr, "%o", &ui);
556         if ((x == 0) || (ar->ar_fmode & ~MYALLPERMS)) {
557             rpmlog(RPMLOG_ERR, _("Bad mode spec: %s(%s)\n"), name, q);
558             fl->processingFailed = 1;
559             return RPMRC_FAIL;
560         }
561         ar->ar_fmode = ui;
562     } else
563         ar->ar_fmodestr = NULL;
564
565     if (ar->ar_dmodestr && !isAttrDefault(ar->ar_dmodestr)) {
566         unsigned int ui;
567         x = sscanf(ar->ar_dmodestr, "%o", &ui);
568         if ((x == 0) || (ar->ar_dmode & ~MYALLPERMS)) {
569             rpmlog(RPMLOG_ERR, _("Bad dirmode spec: %s(%s)\n"), name, q);
570             fl->processingFailed = 1;
571             return RPMRC_FAIL;
572         }
573         ar->ar_dmode = ui;
574     } else
575         ar->ar_dmodestr = NULL;
576
577     if (!(ar->ar_user && !isAttrDefault(ar->ar_user)))
578         ar->ar_user = NULL;
579
580     if (!(ar->ar_group && !isAttrDefault(ar->ar_group)))
581         ar->ar_group = NULL;
582
583     dupAttrRec(ar, ret_ar);
584
585     /* XXX fix all this */
586     *specdFlags |= SPECD_UID | SPECD_GID | SPECD_FILEMODE | SPECD_DIRMODE;
587     
588     return 0;
589 }
590
591 /**
592  * Parse %config from file manifest.
593  * @param buf           current spec file line
594  * @param fl            package file tree walk data
595  * @return              RPMRC_OK on success
596  */
597 static rpmRC parseForConfig(const char * buf, FileList fl)
598 {
599     char *p, *pe, *q;
600     const char *name;
601
602     if ((p = strstr(buf, (name = "%config"))) == NULL)
603         return RPMRC_OK;
604
605     fl->currentFlags |= RPMFILE_CONFIG;
606
607     /* Erase "%config" token. */
608     for (pe = p; (pe-p) < strlen(name); pe++)
609         *pe = ' ';
610     SKIPSPACE(pe);
611     if (*pe != '(')
612         return RPMRC_OK;
613
614     /* Bracket %config args */
615     *pe++ = ' ';
616     for (p = pe; *pe && *pe != ')'; pe++)
617         {};
618
619     if (*pe == '\0') {
620         rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
621         fl->processingFailed = 1;
622         return RPMRC_FAIL;
623     }
624
625     /* Localize. Erase parsed string. */
626     q = alloca((pe-p) + 1);
627     strncpy(q, p, pe-p);
628     q[pe-p] = '\0';
629     while (p <= pe)
630         *p++ = ' ';
631
632     for (p = q; *p != '\0'; p = pe) {
633         SKIPWHITE(p);
634         if (*p == '\0')
635             break;
636         pe = p;
637         SKIPNONWHITE(pe);
638         if (*pe != '\0')
639             *pe++ = '\0';
640         if (!strcmp(p, "missingok")) {
641             fl->currentFlags |= RPMFILE_MISSINGOK;
642         } else if (!strcmp(p, "noreplace")) {
643             fl->currentFlags |= RPMFILE_NOREPLACE;
644         } else {
645             rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p);
646             fl->processingFailed = 1;
647             return RPMRC_FAIL;
648         }
649     }
650
651     return RPMRC_OK;
652 }
653
654 /**
655  */
656 static int langCmp(const void * ap, const void * bp)
657 {
658     return strcmp(*(const char **)ap, *(const char **)bp);
659 }
660
661 /**
662  * Parse %lang from file manifest.
663  * @param buf           current spec file line
664  * @param fl            package file tree walk data
665  * @return              RPMRC_OK on success
666  */
667 static int parseForLang(const char * buf, FileList fl)
668 {
669     char *p, *pe, *q;
670     const char *name;
671
672   while ((p = strstr(buf, (name = "%lang"))) != NULL) {
673
674     for (pe = p; (pe-p) < strlen(name); pe++)
675         *pe = ' ';
676     SKIPSPACE(pe);
677
678     if (*pe != '(') {
679         rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
680         fl->processingFailed = 1;
681         return RPMRC_FAIL;
682     }
683
684     /* Bracket %lang args */
685     *pe++ = ' ';
686     for (pe = p; *pe && *pe != ')'; pe++)
687         {};
688
689     if (*pe == '\0') {
690         rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
691         fl->processingFailed = 1;
692         return RPMRC_FAIL;
693     }
694
695     /* Localize. Erase parsed string. */
696     q = alloca((pe-p) + 1);
697     strncpy(q, p, pe-p);
698     q[pe-p] = '\0';
699     while (p <= pe)
700         *p++ = ' ';
701
702     /* Parse multiple arguments from %lang */
703     for (p = q; *p != '\0'; p = pe) {
704         char *newp;
705         size_t np;
706         int i;
707
708         SKIPWHITE(p);
709         pe = p;
710         SKIPNONWHITE(pe);
711
712         np = pe - p;
713         
714         /* Sanity check on locale lengths */
715         if (np < 1 || (np == 1 && *p != 'C') || np >= 32) {
716             rpmlog(RPMLOG_ERR,
717                 _("Unusual locale length: \"%.*s\" in %%lang(%s)\n"),
718                 (int)np, p, q);
719             fl->processingFailed = 1;
720             return RPMRC_FAIL;
721         }
722
723         /* Check for duplicate locales */
724         if (fl->currentLangs != NULL)
725         for (i = 0; i < fl->nLangs; i++) {
726             if (strncmp(fl->currentLangs[i], p, np))
727                 continue;
728             rpmlog(RPMLOG_ERR, _("Duplicate locale %.*s in %%lang(%s)\n"),
729                 (int)np, p, q);
730             fl->processingFailed = 1;
731             return RPMRC_FAIL;
732         }
733
734         /* Add new locale */
735         fl->currentLangs = xrealloc(fl->currentLangs,
736                                 (fl->nLangs + 1) * sizeof(*fl->currentLangs));
737         newp = xmalloc( np+1 );
738         strncpy(newp, p, np);
739         newp[np] = '\0';
740         fl->currentLangs[fl->nLangs++] = newp;
741         if (*pe == ',') pe++;   /* skip , if present */
742     }
743   }
744
745     /* Insure that locales are sorted. */
746     if (fl->currentLangs)
747         qsort(fl->currentLangs, fl->nLangs, sizeof(*fl->currentLangs), langCmp);
748
749     return RPMRC_OK;
750 }
751
752 /**
753  */
754 static int parseForRegexLang(const char * fileName, char ** lang)
755 {
756     static int initialized = 0;
757     static int hasRegex = 0;
758     static regex_t compiledPatt;
759     static char buf[BUFSIZ];
760     int x;
761     regmatch_t matches[2];
762     const char *s;
763
764     if (! initialized) {
765         char *patt = rpmExpand("%{?_langpatt}", NULL);
766         int rc = 0;
767         if (!(patt && *patt != '\0'))
768             rc = 1;
769         else if (regcomp(&compiledPatt, patt, REG_EXTENDED))
770             rc = -1;
771         patt = _free(patt);
772         if (rc)
773             return rc;
774         hasRegex = 1;
775         initialized = 1;
776     }
777     
778     memset(matches, 0, sizeof(matches));
779     if (! hasRegex || regexec(&compiledPatt, fileName, 2, matches, REG_NOTEOL))
780         return 1;
781
782     /* Got match */
783     s = fileName + matches[1].rm_eo - 1;
784     x = matches[1].rm_eo - matches[1].rm_so;
785     buf[x] = '\0';
786     while (x) {
787         buf[--x] = *s--;
788     }
789     if (lang)
790         *lang = buf;
791     return 0;
792 }
793
794 /**
795  */
796 VFA_t virtualFileAttributes[] = {
797         { "%dir",       0,      0 },    /* XXX why not RPMFILE_DIR? */
798         { "%doc",       0,      RPMFILE_DOC },
799         { "%ghost",     0,      RPMFILE_GHOST },
800         { "%exclude",   0,      RPMFILE_EXCLUDE },
801         { "%readme",    0,      RPMFILE_README },
802         { "%license",   0,      RPMFILE_LICENSE },
803         { "%pubkey",    0,      RPMFILE_PUBKEY },
804         { "%policy",    0,      RPMFILE_POLICY },
805
806 #if WHY_NOT
807         { "%icon",      0,      RPMFILE_ICON },
808         { "%spec",      0,      RPMFILE_SPEC },
809         { "%config",    0,      RPMFILE_CONFIG },
810         { "%missingok", 0,      RPMFILE_CONFIG|RPMFILE_MISSINGOK },
811         { "%noreplace", 0,      RPMFILE_CONFIG|RPMFILE_NOREPLACE },
812 #endif
813
814         { NULL, 0, 0 }
815 };
816
817 /**
818  * Parse simple attributes (e.g. %dir) from file manifest.
819  * @param spec
820  * @param pkg
821  * @param buf           current spec file line
822  * @param fl            package file tree walk data
823  * @retval *fileName    file name
824  * @return              RPMRC_OK on success
825  */
826 static rpmRC parseForSimple(rpmSpec spec, Package pkg, char * buf,
827                           FileList fl, const char ** fileName)
828 {
829     char *s, *t;
830     int res, specialDoc = 0;
831     char specialDocBuf[BUFSIZ];
832
833     specialDocBuf[0] = '\0';
834     *fileName = NULL;
835     res = RPMRC_OK;
836
837     t = buf;
838     while ((s = strtokWithQuotes(t, " \t\n")) != NULL) {
839         t = NULL;
840         if (!strcmp(s, "%docdir")) {
841             s = strtokWithQuotes(NULL, " \t\n");
842             if (fl->docDirCount == MAXDOCDIR) {
843                 rpmlog(RPMLOG_ERR, _("Hit limit for %%docdir\n"));
844                 fl->processingFailed = 1;
845                 res = RPMRC_FAIL;
846             }
847         
848             if (s != NULL)
849                 fl->docDirs[fl->docDirCount++] = xstrdup(s);
850             if (s == NULL || strtokWithQuotes(NULL, " \t\n")) {
851                 rpmlog(RPMLOG_ERR, _("Only one arg for %%docdir\n"));
852                 fl->processingFailed = 1;
853                 res = RPMRC_FAIL;
854             }
855             break;
856         }
857
858     /* Set flags for virtual file attributes */
859     {   VFA_t *vfa;
860         for (vfa = virtualFileAttributes; vfa->attribute != NULL; vfa++) {
861             if (strcmp(s, vfa->attribute))
862                 continue;
863             if (!vfa->flag) {
864                 if (!strcmp(s, "%dir"))
865                     fl->isDir = 1;      /* XXX why not RPMFILE_DIR? */
866             } else {
867                 if (vfa->not)
868                     fl->currentFlags &= ~vfa->flag;
869                 else
870                     fl->currentFlags |= vfa->flag;
871             }
872
873             break;
874         }
875         /* if we got an attribute, continue with next token */
876         if (vfa->attribute != NULL)
877             continue;
878     }
879
880         if (*fileName) {
881             /* We already got a file -- error */
882             rpmlog(RPMLOG_ERR, _("Two files on one line: %s\n"),
883                 *fileName);
884             fl->processingFailed = 1;
885             res = RPMRC_FAIL;
886         }
887
888         if (*s != '/') {
889             if (fl->currentFlags & RPMFILE_DOC) {
890                 specialDoc = 1;
891                 strcat(specialDocBuf, " ");
892                 strcat(specialDocBuf, s);
893             } else
894             if (fl->currentFlags & (RPMFILE_POLICY|RPMFILE_PUBKEY|RPMFILE_ICON))
895             {
896                 *fileName = s;
897             } else {
898                 /* not in %doc, does not begin with / -- error */
899                 rpmlog(RPMLOG_ERR,
900                     _("File must begin with \"/\": %s\n"), s);
901                 fl->processingFailed = 1;
902                 res = RPMRC_FAIL;
903             }
904         } else {
905             *fileName = s;
906         }
907     }
908
909     if (specialDoc) {
910         if (*fileName || (fl->currentFlags & ~(RPMFILE_DOC))) {
911             rpmlog(RPMLOG_ERR,
912                      _("Can't mix special %%doc with other forms: %s\n"),
913                      (*fileName ? *fileName : ""));
914             fl->processingFailed = 1;
915             res = RPMRC_FAIL;
916         } else {
917         /* XXX WATCHOUT: buf is an arg */
918            {
919                 static const char *_docdir_fmt = NULL;
920                 static int oneshot = 0;
921                 char *ddir, *fmt;
922                 errmsg_t errstr;
923                 if (!oneshot) {
924                     _docdir_fmt = rpmExpand("%{?_docdir_fmt}", NULL);
925                     if (!_docdir_fmt || !*_docdir_fmt)
926                         _docdir_fmt = "%{NAME}-%{VERSION}";
927                     oneshot = 1;
928                 }
929                 fmt = headerSprintf(pkg->header, _docdir_fmt, rpmTagTable, rpmHeaderFormats, &errstr);
930                 if (!fmt) {
931                     rpmlog(RPMLOG_ERR, _("illegal _docdir_fmt: %s\n"), errstr);
932                     fl->processingFailed = 1;
933                     res = RPMRC_FAIL;
934                 }
935                 ddir = rpmGetPath("%{_docdir}/", fmt, NULL);
936                 strcpy(buf, ddir);
937                 ddir = _free(ddir);
938             }
939
940         /* XXX FIXME: this is easy to do as macro expansion */
941
942             if (! fl->passedSpecialDoc) {
943                 pkg->specialDoc = newStringBuf();
944                 appendStringBuf(pkg->specialDoc, "DOCDIR=$RPM_BUILD_ROOT");
945                 appendLineStringBuf(pkg->specialDoc, buf);
946                 appendLineStringBuf(pkg->specialDoc, "export DOCDIR");
947                 appendLineStringBuf(pkg->specialDoc, "rm -rf $DOCDIR");
948                 appendLineStringBuf(pkg->specialDoc, RPM_MKDIR_P " $DOCDIR");
949
950                 *fileName = buf;
951                 fl->passedSpecialDoc = 1;
952                 fl->isSpecialDoc = 1;
953             }
954
955             appendStringBuf(pkg->specialDoc, "cp -pr ");
956             appendStringBuf(pkg->specialDoc, specialDocBuf);
957             appendLineStringBuf(pkg->specialDoc, " $DOCDIR");
958         }
959     }
960
961     return res;
962 }
963
964 /**
965  */
966 static int compareFileListRecs(const void * ap, const void * bp)        
967 {
968     const char *a = ((FileListRec)ap)->fileURL;
969     const char *b = ((FileListRec)bp)->fileURL;
970     return strcmp(a, b);
971 }
972
973 /**
974  * Test if file is located in a %docdir.
975  * @param fl            package file tree walk data
976  * @param fileName      file path
977  * @return              1 if doc file, 0 if not
978  */
979 static int isDoc(FileList fl, const char * fileName)    
980 {
981     int x = fl->docDirCount;
982     size_t k, l;
983
984     k = strlen(fileName);
985     while (x--) {
986         l = strlen(fl->docDirs[x]);
987         if (l < k && strncmp(fileName, fl->docDirs[x], l) == 0 && fileName[l] == '/')
988             return 1;
989     }
990     return 0;
991 }
992
993 /**
994  * Verify that file attributes scope over hardlinks correctly.
995  * If partial hardlink sets are possible, then add tracking dependency.
996  * @param fl            package file tree walk data
997  * @return              1 if partial hardlink sets can exist, 0 otherwise.
998  */
999 static int checkHardLinks(FileList fl)
1000 {
1001     FileListRec ilp, jlp;
1002     int i, j;
1003
1004     for (i = 0;  i < fl->fileListRecsUsed; i++) {
1005         ilp = fl->fileList + i;
1006         if (!(S_ISREG(ilp->fl_mode) && ilp->fl_nlink > 1))
1007             continue;
1008
1009         for (j = i + 1; j < fl->fileListRecsUsed; j++) {
1010             jlp = fl->fileList + j;
1011             if (!S_ISREG(jlp->fl_mode))
1012                 continue;
1013             if (ilp->fl_nlink != jlp->fl_nlink)
1014                 continue;
1015             if (ilp->fl_ino != jlp->fl_ino)
1016                 continue;
1017             if (ilp->fl_dev != jlp->fl_dev)
1018                 continue;
1019             return 1;
1020         }
1021     }
1022     return 0;
1023 }
1024
1025 /**
1026  * Add file entries to header.
1027  * @todo Should directories have %doc/%config attributes? (#14531)
1028  * @todo Remove RPMTAG_OLDFILENAMES, add dirname/basename instead.
1029  * @param fl            package file tree walk data
1030  * @retval *fip         file info for package
1031  * @param h
1032  * @param isSrc
1033  */
1034 static void genCpioListAndHeader(FileList fl,
1035                 rpmfi * fip, Header h, int isSrc)
1036 {
1037     int _addDotSlash = !(isSrc || rpmExpandNumeric("%{_noPayloadPrefix}"));
1038     size_t apathlen = 0;
1039     size_t dpathlen = 0;
1040     size_t skipLen = 0;
1041     size_t fnlen;
1042     FileListRec flp;
1043     char buf[BUFSIZ];
1044     int i;
1045     
1046     /* Sort the big list */
1047     qsort(fl->fileList, fl->fileListRecsUsed,
1048           sizeof(*(fl->fileList)), compareFileListRecs);
1049     
1050     /* Generate the header. */
1051     if (! isSrc) {
1052         skipLen = 1;
1053         if (fl->prefix)
1054             skipLen += strlen(fl->prefix);
1055     }
1056
1057     for (i = 0, flp = fl->fileList; i < fl->fileListRecsUsed; i++, flp++) {
1058         const char *s;
1059
1060         /* Merge duplicate entries. */
1061         while (i < (fl->fileListRecsUsed - 1) &&
1062             !strcmp(flp->fileURL, flp[1].fileURL)) {
1063
1064             /* Two entries for the same file found, merge the entries. */
1065             /* Note that an %exclude is a duplication of a file reference */
1066
1067             /* file flags */
1068             flp[1].flags |= flp->flags; 
1069
1070             if (!(flp[1].flags & RPMFILE_EXCLUDE))
1071                 rpmlog(RPMLOG_WARNING, _("File listed twice: %s\n"),
1072                         flp->fileURL);
1073    
1074             /* file mode */
1075             if (S_ISDIR(flp->fl_mode)) {
1076                 if ((flp[1].specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)) <
1077                     (flp->specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)))
1078                         flp[1].fl_mode = flp->fl_mode;
1079             } else {
1080                 if ((flp[1].specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)) <
1081                     (flp->specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)))
1082                         flp[1].fl_mode = flp->fl_mode;
1083             }
1084
1085             /* uid */
1086             if ((flp[1].specdFlags & (SPECD_UID | SPECD_DEFUID)) <
1087                 (flp->specdFlags & (SPECD_UID | SPECD_DEFUID)))
1088             {
1089                 flp[1].fl_uid = flp->fl_uid;
1090                 flp[1].uname = flp->uname;
1091             }
1092
1093             /* gid */
1094             if ((flp[1].specdFlags & (SPECD_GID | SPECD_DEFGID)) <
1095                 (flp->specdFlags & (SPECD_GID | SPECD_DEFGID)))
1096             {
1097                 flp[1].fl_gid = flp->fl_gid;
1098                 flp[1].gname = flp->gname;
1099             }
1100
1101             /* verify flags */
1102             if ((flp[1].specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)) <
1103                 (flp->specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)))
1104                     flp[1].verifyFlags = flp->verifyFlags;
1105
1106             /* XXX to-do: language */
1107
1108             flp++; i++;
1109         }
1110
1111         /* Skip files that were marked with %exclude. */
1112         if (flp->flags & RPMFILE_EXCLUDE) continue;
1113
1114         /* Omit '/' and/or URL prefix, leave room for "./" prefix */
1115         apathlen += (strlen(flp->fileURL) - skipLen + (_addDotSlash ? 3 : 1));
1116
1117         /* Leave room for both dirname and basename NUL's */
1118         dpathlen += (strlen(flp->diskURL) + 2);
1119
1120         /*
1121          * Make the header, the OLDFILENAMES will get converted to a 
1122          * compressed file list write before we write the actual package to
1123          * disk.
1124          */
1125         (void) headerAddOrAppendEntry(h, RPMTAG_OLDFILENAMES, RPM_STRING_ARRAY_TYPE,
1126                                &(flp->fileURL), 1);
1127
1128       if (sizeof(flp->fl_size) != sizeof(rpm_off_t)) {
1129         rpm_off_t psize = (rpm_off_t)flp->fl_size;
1130         (void) headerAddOrAppendEntry(h, RPMTAG_FILESIZES, RPM_INT32_TYPE,
1131                                &(psize), 1);
1132       } else {
1133         (void) headerAddOrAppendEntry(h, RPMTAG_FILESIZES, RPM_INT32_TYPE,
1134                                &(flp->fl_size), 1);
1135       }
1136         (void) headerAddOrAppendEntry(h, RPMTAG_FILEUSERNAME, RPM_STRING_ARRAY_TYPE,
1137                                &(flp->uname), 1);
1138         (void) headerAddOrAppendEntry(h, RPMTAG_FILEGROUPNAME, RPM_STRING_ARRAY_TYPE,
1139                                &(flp->gname), 1);
1140       if (sizeof(flp->fl_mtime) != sizeof(rpm_time_t)) {
1141         rpm_time_t mtime = (rpm_time_t)flp->fl_mtime;
1142         (void) headerAddOrAppendEntry(h, RPMTAG_FILEMTIMES, RPM_INT32_TYPE,
1143                                &(mtime), 1);
1144       } else {
1145         (void) headerAddOrAppendEntry(h, RPMTAG_FILEMTIMES, RPM_INT32_TYPE,
1146                                &(flp->fl_mtime), 1);
1147       }
1148       if (sizeof(flp->fl_mode) != sizeof(rpm_mode_t)) {
1149         rpm_mode_t pmode = (rpm_mode_t)flp->fl_mode;
1150         (void) headerAddOrAppendEntry(h, RPMTAG_FILEMODES, RPM_INT16_TYPE,
1151                                &(pmode), 1);
1152       } else {
1153         (void) headerAddOrAppendEntry(h, RPMTAG_FILEMODES, RPM_INT16_TYPE,
1154                                &(flp->fl_mode), 1);
1155       }
1156       if (sizeof(flp->fl_rdev) != sizeof(rpm_rdev_t)) {
1157         rpm_rdev_t prdev = (rpm_rdev_t)flp->fl_rdev;
1158         (void) headerAddOrAppendEntry(h, RPMTAG_FILERDEVS, RPM_INT16_TYPE,
1159                                &(prdev), 1);
1160       } else {
1161         (void) headerAddOrAppendEntry(h, RPMTAG_FILERDEVS, RPM_INT16_TYPE,
1162                                &(flp->fl_rdev), 1);
1163       }
1164       if (sizeof(flp->fl_dev) != sizeof(rpm_dev_t)) {
1165         rpm_dev_t pdevice = (rpm_dev_t)flp->fl_dev;
1166         (void) headerAddOrAppendEntry(h, RPMTAG_FILEDEVICES, RPM_INT32_TYPE,
1167                                &(pdevice), 1);
1168       } else {
1169         (void) headerAddOrAppendEntry(h, RPMTAG_FILEDEVICES, RPM_INT32_TYPE,
1170                                &(flp->fl_dev), 1);
1171       }
1172       if (sizeof(flp->fl_ino) != sizeof(rpm_ino_t)) {
1173         rpm_ino_t ino = (rpm_ino_t)flp->fl_ino;
1174         (void) headerAddOrAppendEntry(h, RPMTAG_FILEINODES, RPM_INT32_TYPE,
1175                                 &(ino), 1);
1176       } else {
1177         (void) headerAddOrAppendEntry(h, RPMTAG_FILEINODES, RPM_INT32_TYPE,
1178                                 &(flp->fl_ino), 1);
1179       }
1180
1181         (void) headerAddOrAppendEntry(h, RPMTAG_FILELANGS, RPM_STRING_ARRAY_TYPE,
1182                                &(flp->langs),  1);
1183         
1184         /* We used to add these, but they should not be needed */
1185         /* (void) headerAddOrAppendEntry(h, RPMTAG_FILEUIDS,
1186          *                 RPM_INT32_TYPE, &(flp->fl_uid), 1);
1187          * (void) headerAddOrAppendEntry(h, RPMTAG_FILEGIDS,
1188          *                 RPM_INT32_TYPE, &(flp->fl_gid), 1);
1189          */
1190         
1191         buf[0] = '\0';
1192         if (S_ISREG(flp->fl_mode))
1193             (void) rpmDoDigest(PGPHASHALGO_MD5, flp->diskURL, 1, 
1194                                (unsigned char *)buf, NULL);
1195         s = buf;
1196         (void) headerAddOrAppendEntry(h, RPMTAG_FILEMD5S, RPM_STRING_ARRAY_TYPE,
1197                                &s, 1);
1198         
1199         buf[0] = '\0';
1200         if (S_ISLNK(flp->fl_mode)) {
1201             buf[readlink(flp->diskURL, buf, BUFSIZ)] = '\0';
1202             if (fl->buildRootURL) {
1203                 const char * buildRoot;
1204                 (void) urlPath(fl->buildRootURL, &buildRoot);
1205
1206                 if (buf[0] == '/' && strcmp(buildRoot, "/") &&
1207                     !strncmp(buf, buildRoot, strlen(buildRoot))) {
1208                      rpmlog(RPMLOG_ERR,
1209                                 _("Symlink points to BuildRoot: %s -> %s\n"),
1210                                 flp->fileURL, buf);
1211                     fl->processingFailed = 1;
1212                 }
1213             }
1214         }
1215         s = buf;
1216         (void) headerAddOrAppendEntry(h, RPMTAG_FILELINKTOS, RPM_STRING_ARRAY_TYPE,
1217                                &s, 1);
1218         
1219         if (flp->flags & RPMFILE_GHOST) {
1220             flp->verifyFlags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE |
1221                                 RPMVERIFY_LINKTO | RPMVERIFY_MTIME);
1222         }
1223         (void) headerAddOrAppendEntry(h, RPMTAG_FILEVERIFYFLAGS, RPM_INT32_TYPE,
1224                                &(flp->verifyFlags), 1);
1225         
1226         if (!isSrc && isDoc(fl, flp->fileURL))
1227             flp->flags |= RPMFILE_DOC;
1228         /* XXX Should directories have %doc/%config attributes? (#14531) */
1229         if (S_ISDIR(flp->fl_mode))
1230             flp->flags &= ~(RPMFILE_CONFIG|RPMFILE_DOC);
1231
1232         (void) headerAddOrAppendEntry(h, RPMTAG_FILEFLAGS, RPM_INT32_TYPE,
1233                                &(flp->flags), 1);
1234
1235     }
1236
1237     (void) headerAddEntry(h, RPMTAG_SIZE, RPM_INT32_TYPE,
1238                    &(fl->totalFileSize), 1);
1239
1240     if (_addDotSlash)
1241         (void) rpmlibNeedsFeature(h, "PayloadFilesHavePrefix", "4.0-1");
1242
1243     /* Choose how filenames are represented. */
1244     if (_noDirTokens)
1245         expandFilelist(h);
1246     else {
1247         compressFilelist(h);
1248         /* Binary packages with dirNames cannot be installed by legacy rpm. */
1249         (void) rpmlibNeedsFeature(h, "CompressedFileNames", "3.0.4-1");
1250     }
1251
1252   { int scareMem = 0;
1253     rpmts ts = NULL;    /* XXX FIXME drill rpmts ts all the way down here */
1254     rpmfi fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, scareMem);
1255     char * a, * d;
1256
1257     if (fi == NULL) return;             /* XXX can't happen */
1258
1259     fi->te = xcalloc(1, sizeof(*fi->te));
1260     fi->te->type = TR_ADDED;
1261
1262     fi->dnl = _free(fi->dnl);
1263     fi->bnl = _free(fi->bnl);
1264     if (!scareMem) fi->dil = _free(fi->dil);
1265
1266     fi->dnl = xmalloc(fi->fc * sizeof(*fi->dnl) + dpathlen);
1267     d = (char *)(fi->dnl + fi->fc);
1268     *d = '\0';
1269
1270     fi->bnl = xmalloc(fi->fc * (sizeof(*fi->bnl) + sizeof(*fi->dil)));
1271 /* FIX: artifact of spoofing headerGetEntry */
1272     fi->dil = (!scareMem)
1273         ? xcalloc(sizeof(*fi->dil), fi->fc)
1274         : (int *)(fi->bnl + fi->fc);
1275
1276     fi->apath = xmalloc(fi->fc * sizeof(*fi->apath) + apathlen + 1);
1277     a = (char *)(fi->apath + fi->fc);
1278     *a = '\0';
1279
1280     fi->actions = xcalloc(sizeof(*fi->actions), fi->fc);
1281     fi->fmapflags = xcalloc(sizeof(*fi->fmapflags), fi->fc);
1282     fi->astriplen = 0;
1283     if (fl->buildRootURL)
1284         fi->astriplen = strlen(fl->buildRootURL);
1285     fi->striplen = 0;
1286     fi->fuser = NULL;
1287     fi->fgroup = NULL;
1288
1289     /* Make the cpio list */
1290     if (fi->dil != NULL)        /* XXX can't happen */
1291     for (i = 0, flp = fl->fileList; i < fi->fc; i++, flp++) {
1292         char * b;
1293
1294         /* Skip (possible) duplicate file entries, use last entry info. */
1295         while (((flp - fl->fileList) < (fl->fileListRecsUsed - 1)) &&
1296                 !strcmp(flp->fileURL, flp[1].fileURL))
1297             flp++;
1298
1299         if (flp->flags & RPMFILE_EXCLUDE) {
1300             i--;
1301             continue;
1302         }
1303
1304         if ((fnlen = strlen(flp->diskURL) + 1) > fi->fnlen)
1305             fi->fnlen = fnlen;
1306
1307         /* Create disk directory and base name. */
1308         fi->dil[i] = i;
1309 /* FIX: artifact of spoofing headerGetEntry */
1310         fi->dnl[fi->dil[i]] = d;
1311         d = stpcpy(d, flp->diskURL);
1312
1313         /* Make room for the dirName NUL, find start of baseName. */
1314         for (b = d; b > fi->dnl[fi->dil[i]] && *b != '/'; b--)
1315             b[1] = b[0];
1316         b++;            /* dirname's end in '/' */
1317         *b++ = '\0';    /* terminate dirname, b points to basename */
1318         fi->bnl[i] = b;
1319         d += 2;         /* skip both dirname and basename NUL's */
1320
1321         /* Create archive path, normally adding "./" */
1322         fi->apath[i] = a;
1323         if (_addDotSlash)
1324             a = stpcpy(a, "./");
1325         a = stpcpy(a, (flp->fileURL + skipLen));
1326         a++;            /* skip apath NUL */
1327
1328         if (flp->flags & RPMFILE_GHOST) {
1329             fi->actions[i] = FA_SKIP;
1330             continue;
1331         }
1332         fi->actions[i] = FA_COPYOUT;
1333         fi->fmapflags[i] = CPIO_MAP_PATH |
1334                 CPIO_MAP_TYPE | CPIO_MAP_MODE | CPIO_MAP_UID | CPIO_MAP_GID;
1335         if (isSrc)
1336             fi->fmapflags[i] |= CPIO_FOLLOW_SYMLINKS;
1337
1338     }
1339     if (fip)
1340         *fip = fi;
1341     else
1342         fi = rpmfiFree(fi);
1343   }
1344 }
1345
1346 /**
1347  */
1348 static FileListRec freeFileList(FileListRec fileList,
1349                         int count)
1350 {
1351     while (count--) {
1352         fileList[count].diskURL = _free(fileList[count].diskURL);
1353         fileList[count].fileURL = _free(fileList[count].fileURL);
1354         fileList[count].langs = _free(fileList[count].langs);
1355     }
1356     fileList = _free(fileList);
1357     return NULL;
1358 }
1359
1360 /* forward ref */
1361 static rpmRC recurseDir(FileList fl, const char * diskURL);
1362
1363 /**
1364  * Add a file to the package manifest.
1365  * @param fl            package file tree walk data
1366  * @param diskURL       path to file
1367  * @param statp         file stat (possibly NULL)
1368  * @return              RPMRC_OK on success
1369  */
1370 static rpmRC addFile(FileList fl, const char * diskURL,
1371                 struct stat * statp)
1372 {
1373     const char *fileURL = diskURL;
1374     struct stat statbuf;
1375     mode_t fileMode;
1376     uid_t fileUid;
1377     gid_t fileGid;
1378     const char *fileUname;
1379     const char *fileGname;
1380     char *lang;
1381     
1382     /* Path may have prepended buildRootURL, so locate the original filename. */
1383     /*
1384      * XXX There are 3 types of entry into addFile:
1385      *
1386      *  From                    diskUrl                 statp
1387      *  =====================================================
1388      *  processBinaryFile       path                    NULL
1389      *  processBinaryFile       glob result path        NULL
1390      *  myftw                   path                    stat
1391      *
1392      */
1393     {   const char *fileName;
1394         (void) urlPath(fileURL, &fileName);
1395         if (fl->buildRootURL && strcmp(fl->buildRootURL, "/"))
1396             fileURL += strlen(fl->buildRootURL);
1397     }
1398
1399     /* XXX make sure '/' can be packaged also */
1400     if (*fileURL == '\0')
1401         fileURL = "/";
1402
1403     /* If we are using a prefix, validate the file */
1404     if (!fl->inFtw && fl->prefix) {
1405         const char *prefixTest;
1406         const char *prefixPtr = fl->prefix;
1407
1408         (void) urlPath(fileURL, &prefixTest);
1409         while (*prefixPtr && *prefixTest && (*prefixTest == *prefixPtr)) {
1410             prefixPtr++;
1411             prefixTest++;
1412         }
1413         if (*prefixPtr || (*prefixTest && *prefixTest != '/')) {
1414             rpmlog(RPMLOG_ERR, _("File doesn't match prefix (%s): %s\n"),
1415                      fl->prefix, fileURL);
1416             fl->processingFailed = 1;
1417             return RPMRC_FAIL;
1418         }
1419     }
1420
1421     if (statp == NULL) {
1422         statp = &statbuf;
1423         memset(statp, 0, sizeof(*statp));
1424         if (fl->devtype) {
1425             time_t now = time(NULL);
1426
1427             /* XXX hack up a stat structure for a %dev(...) directive. */
1428             statp->st_nlink = 1;
1429             statp->st_rdev =
1430                 ((fl->devmajor & 0xff) << 8) | (fl->devminor & 0xff);
1431             statp->st_dev = statp->st_rdev;
1432             statp->st_mode = (fl->devtype == 'b' ? S_IFBLK : S_IFCHR);
1433             statp->st_mode |= (fl->cur_ar.ar_fmode & 0777);
1434             statp->st_atime = now;
1435             statp->st_mtime = now;
1436             statp->st_ctime = now;
1437         } else if (lstat(diskURL, statp)) {
1438             rpmlog(RPMLOG_ERR, _("File not found: %s\n"), diskURL);
1439             fl->processingFailed = 1;
1440             return RPMRC_FAIL;
1441         }
1442     }
1443
1444     if ((! fl->isDir) && S_ISDIR(statp->st_mode)) {
1445 /* FIX: fl->buildRootURL may be NULL */
1446         return recurseDir(fl, diskURL);
1447     }
1448
1449     fileMode = statp->st_mode;
1450     fileUid = statp->st_uid;
1451     fileGid = statp->st_gid;
1452
1453     if (S_ISDIR(fileMode) && fl->cur_ar.ar_dmodestr) {
1454         fileMode &= S_IFMT;
1455         fileMode |= fl->cur_ar.ar_dmode;
1456     } else if (fl->cur_ar.ar_fmodestr != NULL) {
1457         fileMode &= S_IFMT;
1458         fileMode |= fl->cur_ar.ar_fmode;
1459     }
1460     if (fl->cur_ar.ar_user) {
1461         fileUname = getUnameS(fl->cur_ar.ar_user);
1462     } else {
1463         fileUname = getUname(fileUid);
1464     }
1465     if (fl->cur_ar.ar_group) {
1466         fileGname = getGnameS(fl->cur_ar.ar_group);
1467     } else {
1468         fileGname = getGname(fileGid);
1469     }
1470         
1471     /* Default user/group to builder's user/group */
1472     if (fileUname == NULL)
1473         fileUname = getUname(getuid());
1474     if (fileGname == NULL)
1475         fileGname = getGname(getgid());
1476     
1477     /* S_XXX macro must be consistent with type in find call at check-files script */
1478     if (check_fileList && (S_ISREG(fileMode) || S_ISLNK(fileMode))) {
1479         appendStringBuf(check_fileList, diskURL);
1480         appendStringBuf(check_fileList, "\n");
1481     }
1482
1483     /* Add to the file list */
1484     if (fl->fileListRecsUsed == fl->fileListRecsAlloced) {
1485         fl->fileListRecsAlloced += 128;
1486         fl->fileList = xrealloc(fl->fileList,
1487                         fl->fileListRecsAlloced * sizeof(*(fl->fileList)));
1488     }
1489             
1490     {   FileListRec flp = &fl->fileList[fl->fileListRecsUsed];
1491         int i;
1492
1493         flp->fl_st = *statp;    /* structure assignment */
1494         flp->fl_mode = fileMode;
1495         flp->fl_uid = fileUid;
1496         flp->fl_gid = fileGid;
1497
1498         flp->fileURL = xstrdup(fileURL);
1499         flp->diskURL = xstrdup(diskURL);
1500         flp->uname = fileUname;
1501         flp->gname = fileGname;
1502
1503         if (fl->currentLangs && fl->nLangs > 0) {
1504             char * ncl;
1505             size_t nl = 0;
1506             
1507             for (i = 0; i < fl->nLangs; i++)
1508                 nl += strlen(fl->currentLangs[i]) + 1;
1509
1510             flp->langs = ncl = xmalloc(nl);
1511             for (i = 0; i < fl->nLangs; i++) {
1512                 const char *ocl;
1513                 if (i)  *ncl++ = '|';
1514                 for (ocl = fl->currentLangs[i]; *ocl != '\0'; ocl++)
1515                         *ncl++ = *ocl;
1516                 *ncl = '\0';
1517             }
1518         } else if (! parseForRegexLang(fileURL, &lang)) {
1519             flp->langs = xstrdup(lang);
1520         } else {
1521             flp->langs = xstrdup("");
1522         }
1523
1524         flp->flags = fl->currentFlags;
1525         flp->specdFlags = fl->currentSpecdFlags;
1526         flp->verifyFlags = fl->currentVerifyFlags;
1527
1528         /* Hard links need be counted only once. */
1529         if (S_ISREG(flp->fl_mode) && flp->fl_nlink > 1) {
1530             FileListRec ilp;
1531             for (i = 0;  i < fl->fileListRecsUsed; i++) {
1532                 ilp = fl->fileList + i;
1533                 if (!S_ISREG(ilp->fl_mode))
1534                     continue;
1535                 if (flp->fl_nlink != ilp->fl_nlink)
1536                     continue;
1537                 if (flp->fl_ino != ilp->fl_ino)
1538                     continue;
1539                 if (flp->fl_dev != ilp->fl_dev)
1540                     continue;
1541                 break;
1542             }
1543         } else
1544             i = fl->fileListRecsUsed;
1545
1546         if (!(flp->flags & RPMFILE_EXCLUDE) && S_ISREG(flp->fl_mode) && i >= fl->fileListRecsUsed) 
1547             fl->totalFileSize += flp->fl_size;
1548     }
1549
1550     fl->fileListRecsUsed++;
1551     fl->fileCount++;
1552
1553     return 0;
1554 }
1555
1556 /**
1557  * Add directory (and all of its files) to the package manifest.
1558  * @param fl            package file tree walk data
1559  * @param diskURL       path to file
1560  * @return              RPMRC_OK on success
1561  */
1562 static rpmRC recurseDir(FileList fl, const char * diskURL)
1563 {
1564     char * ftsSet[2];
1565     FTS * ftsp;
1566     FTSENT * fts;
1567     int myFtsOpts = (FTS_COMFOLLOW | FTS_NOCHDIR | FTS_PHYSICAL);
1568     int rc = RPMRC_FAIL;
1569
1570     fl->inFtw = 1;  /* Flag to indicate file has buildRootURL prefixed */
1571     fl->isDir = 1;  /* Keep it from following myftw() again         */
1572
1573     ftsSet[0] = (char *) diskURL;
1574     ftsSet[1] = NULL;
1575     ftsp = Fts_open(ftsSet, myFtsOpts, NULL);
1576     while ((fts = Fts_read(ftsp)) != NULL) {
1577         switch (fts->fts_info) {
1578         case FTS_D:             /* preorder directory */
1579         case FTS_F:             /* regular file */
1580         case FTS_SL:            /* symbolic link */
1581         case FTS_SLNONE:        /* symbolic link without target */
1582         case FTS_DEFAULT:       /* none of the above */
1583             rc = addFile(fl, fts->fts_accpath, fts->fts_statp);
1584             break;
1585         case FTS_DOT:           /* dot or dot-dot */
1586         case FTS_DP:            /* postorder directory */
1587             rc = 0;
1588             break;
1589         case FTS_NS:            /* stat(2) failed */
1590         case FTS_DNR:           /* unreadable directory */
1591         case FTS_ERR:           /* error; errno is set */
1592         case FTS_DC:            /* directory that causes cycles */
1593         case FTS_NSOK:          /* no stat(2) requested */
1594         case FTS_INIT:          /* initialized only */
1595         case FTS_W:             /* whiteout object */
1596         default:
1597             rc = RPMRC_FAIL;
1598             break;
1599         }
1600         if (rc)
1601             break;
1602     }
1603     (void) Fts_close(ftsp);
1604
1605     fl->isDir = 0;
1606     fl->inFtw = 0;
1607
1608     return rc;
1609 }
1610
1611 /**
1612  * Add a pubkey/policy/icon to a binary package.
1613  * @param pkg
1614  * @param fl            package file tree walk data
1615  * @param fileURL       path to file, relative is builddir, absolute buildroot.
1616  * @param tag           tag to add
1617  * @return              RPMRC_OK on success
1618  */
1619 static rpmRC processMetadataFile(Package pkg, FileList fl, const char * fileURL,
1620                 rpmTag tag)
1621 {
1622     const char * buildURL = "%{_builddir}/%{?buildsubdir}/";
1623     const char * urlfn = NULL;
1624     char * fn = NULL;
1625     char * apkt = NULL;
1626     uint8_t * pkt = NULL;
1627     ssize_t pktlen = 0;
1628     int absolute = 0;
1629     int rc = RPMRC_FAIL;
1630     int xx;
1631
1632     (void) urlPath(fileURL, &urlfn);
1633     if (*urlfn == '/') {
1634         fn = rpmGenPath(fl->buildRootURL, NULL, urlfn);
1635         absolute = 1;
1636     } else
1637         fn = rpmGenPath(buildURL, NULL, urlfn);
1638
1639     switch (tag) {
1640     default:
1641         rpmlog(RPMLOG_ERR, _("%s: can't load unknown tag (%d).\n"),
1642                 fn, tag);
1643         goto exit;
1644         break;
1645     case RPMTAG_PUBKEYS: {
1646         if ((xx = pgpReadPkts(fn, &pkt, (size_t *)&pktlen)) <= 0) {
1647             rc = RPMRC_FAIL;
1648             rpmlog(RPMLOG_ERR, _("%s: public key read failed.\n"), fn);
1649             goto exit;
1650         }
1651         if (xx != PGPARMOR_PUBKEY) {
1652             rc = RPMRC_FAIL;
1653             rpmlog(RPMLOG_ERR, _("%s: not an armored public key.\n"), fn);
1654             goto exit;
1655         }
1656         apkt = pgpArmorWrap(PGPARMOR_PUBKEY, pkt, pktlen);
1657         break;
1658     }
1659     case RPMTAG_POLICIES:
1660         if ((xx = rpmioSlurp(fn, &pkt, &pktlen)) != 0) {
1661             rc = RPMRC_FAIL;
1662             rpmlog(RPMLOG_ERR, _("%s: *.te policy read failed.\n"), fn);
1663             goto exit;
1664         }
1665         apkt = (char *) pkt;    /* XXX unsigned char */
1666         pkt = NULL;
1667         break;
1668     }
1669
1670     xx = headerAddOrAppendEntry(pkg->header, tag,
1671                 RPM_STRING_ARRAY_TYPE, &apkt, 1);
1672
1673     rc = RPMRC_OK;
1674     if (absolute)
1675         rc = addFile(fl, fn, NULL);
1676
1677 exit:
1678     apkt = _free(apkt);
1679     pkt = _free(pkt);
1680     fn = _free(fn);
1681     if (rc) {
1682         fl->processingFailed = 1;
1683         rc = RPMRC_FAIL;
1684     }
1685     return rc;
1686 }
1687
1688 /**
1689  * Add a file to a binary package.
1690  * @param pkg
1691  * @param fl            package file tree walk data
1692  * @param fileURL
1693  * @return              RPMRC_OK on success
1694  */
1695 static rpmRC processBinaryFile(Package pkg, FileList fl,
1696                 const char * fileURL)
1697 {
1698     int quote = 1;      /* XXX permit quoted glob characters. */
1699     int doGlob;
1700     char *diskURL = NULL;
1701     int rc = RPMRC_OK;
1702     
1703     doGlob = glob_pattern_p(fileURL, quote);
1704
1705     /* Check that file starts with leading "/" */
1706     {   const char * fileName;
1707         (void) urlPath(fileURL, &fileName);
1708         if (*fileName != '/') {
1709             rpmlog(RPMLOG_ERR, _("File needs leading \"/\": %s\n"),
1710                         fileName);
1711             rc = RPMRC_FAIL;
1712             goto exit;
1713         }
1714     }
1715     
1716     /* Copy file name or glob pattern removing multiple "/" chars. */
1717     /*
1718      * Note: rpmGetPath should guarantee a "canonical" path. That means
1719      * that the following pathologies should be weeded out:
1720      *          //bin//sh
1721      *          //usr//bin/
1722      *          /.././../usr/../bin//./sh
1723      */
1724     diskURL = rpmGenPath(fl->buildRootURL, NULL, fileURL);
1725
1726     if (doGlob) {
1727         char ** argv = NULL;
1728         int argc = 0;
1729         int i;
1730
1731         /* XXX for %dev marker in file manifest only */
1732         if (fl->noGlob) {
1733             rpmlog(RPMLOG_ERR, _("Glob not permitted: %s\n"),
1734                         diskURL);
1735             rc = RPMRC_FAIL;
1736             goto exit;
1737         }
1738
1739         rc = rpmGlob(diskURL, &argc, &argv);
1740         if (rc == 0 && argc >= 1) {
1741             for (i = 0; i < argc; i++) {
1742                 rc = addFile(fl, argv[i], NULL);
1743                 argv[i] = _free(argv[i]);
1744             }
1745             argv = _free(argv);
1746         } else {
1747             rpmlog(RPMLOG_ERR, _("File not found by glob: %s\n"),
1748                         diskURL);
1749             rc = RPMRC_FAIL;
1750             goto exit;
1751         }
1752     } else {
1753         rc = addFile(fl, diskURL, NULL);
1754     }
1755
1756 exit:
1757     diskURL = _free(diskURL);
1758     if (rc) {
1759         fl->processingFailed = 1;
1760         rc = RPMRC_FAIL;
1761     }
1762     return rc;
1763 }
1764
1765 /**
1766  */
1767 static rpmRC processPackageFiles(rpmSpec spec, Package pkg,
1768                                int installSpecialDoc, int test)
1769 {
1770     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
1771     struct FileList_s fl;
1772     char *s, **files, **fp;
1773     const char *fileName;
1774     char buf[BUFSIZ];
1775     struct AttrRec_s arbuf;
1776     AttrRec specialDocAttrRec = &arbuf;
1777     char *specialDoc = NULL;
1778
1779     nullAttrRec(specialDocAttrRec);
1780     pkg->cpioList = NULL;
1781
1782     if (pkg->fileFile) {
1783         char *ffn;
1784         FILE * f;
1785         FD_t fd;
1786
1787         /* XXX W2DO? urlPath might be useful here. */
1788         if (*pkg->fileFile == '/') {
1789             ffn = rpmGetPath(pkg->fileFile, NULL);
1790         } else {
1791             /* XXX FIXME: add %{buildsubdir} */
1792             ffn = rpmGetPath("%{_builddir}/",
1793                 (spec->buildSubdir ? spec->buildSubdir : "") ,
1794                 "/", pkg->fileFile, NULL);
1795         }
1796         fd = Fopen(ffn, "r.fpio");
1797
1798         if (fd == NULL || Ferror(fd)) {
1799             rpmlog(RPMLOG_ERR,
1800                 _("Could not open %%files file %s: %s\n"),
1801                 ffn, Fstrerror(fd));
1802             return RPMRC_FAIL;
1803         }
1804         ffn = _free(ffn);
1805
1806         f = fdGetFILE(fd);
1807         if (f != NULL)
1808         while (fgets(buf, sizeof(buf), f)) {
1809             handleComments(buf);
1810             if (expandMacros(spec, spec->macros, buf, sizeof(buf))) {
1811                 rpmlog(RPMLOG_ERR, _("line: %s\n"), buf);
1812                 return RPMRC_FAIL;
1813             }
1814             appendStringBuf(pkg->fileList, buf);
1815         }
1816         (void) Fclose(fd);
1817     }
1818     
1819     /* Init the file list structure */
1820     memset(&fl, 0, sizeof(fl));
1821
1822     /* XXX spec->buildRootURL == NULL, then xstrdup("") is returned */
1823     fl.buildRootURL = rpmGenPath(spec->rootURL, spec->buildRootURL, NULL);
1824
1825     if (hge(pkg->header, RPMTAG_DEFAULTPREFIX, NULL, (rpm_data_t *)&fl.prefix, NULL))
1826         fl.prefix = xstrdup(fl.prefix);
1827     else
1828         fl.prefix = NULL;
1829
1830     fl.fileCount = 0;
1831     fl.totalFileSize = 0;
1832     fl.processingFailed = 0;
1833
1834     fl.passedSpecialDoc = 0;
1835     fl.isSpecialDoc = 0;
1836
1837     fl.isDir = 0;
1838     fl.inFtw = 0;
1839     fl.currentFlags = 0;
1840     fl.currentVerifyFlags = 0;
1841     
1842     fl.noGlob = 0;
1843     fl.devtype = 0;
1844     fl.devmajor = 0;
1845     fl.devminor = 0;
1846
1847     nullAttrRec(&fl.cur_ar);
1848     nullAttrRec(&fl.def_ar);
1849     dupAttrRec(&root_ar, &fl.def_ar);   /* XXX assume %defattr(-,root,root) */
1850
1851     fl.defVerifyFlags = RPMVERIFY_ALL;
1852     fl.nLangs = 0;
1853     fl.currentLangs = NULL;
1854
1855     fl.currentSpecdFlags = 0;
1856     fl.defSpecdFlags = 0;
1857
1858     fl.docDirCount = 0;
1859     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/doc");
1860     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/man");
1861     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/info");
1862     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/X11R6/man");
1863     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/share/doc");
1864     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/share/man");
1865     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/share/info");
1866     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/share/gtk-doc/html");
1867     fl.docDirs[fl.docDirCount++] = rpmGetPath("%{_docdir}", NULL);
1868     fl.docDirs[fl.docDirCount++] = rpmGetPath("%{_mandir}", NULL);
1869     fl.docDirs[fl.docDirCount++] = rpmGetPath("%{_infodir}", NULL);
1870     fl.docDirs[fl.docDirCount++] = rpmGetPath("%{_javadocdir}", NULL);
1871     
1872     fl.fileList = NULL;
1873     fl.fileListRecsAlloced = 0;
1874     fl.fileListRecsUsed = 0;
1875
1876     s = getStringBuf(pkg->fileList);
1877     files = splitString(s, strlen(s), '\n');
1878
1879     for (fp = files; *fp != NULL; fp++) {
1880         s = *fp;
1881         SKIPSPACE(s);
1882         if (*s == '\0')
1883             continue;
1884         fileName = NULL;
1885         strcpy(buf, s);
1886         
1887         /* Reset for a new line in %files */
1888         fl.isDir = 0;
1889         fl.inFtw = 0;
1890         fl.currentFlags = 0;
1891         /* turn explicit flags into %def'd ones (gosh this is hacky...) */
1892         fl.currentSpecdFlags = ((unsigned)fl.defSpecdFlags) >> 8;
1893         fl.currentVerifyFlags = fl.defVerifyFlags;
1894         fl.isSpecialDoc = 0;
1895
1896         fl.noGlob = 0;
1897         fl.devtype = 0;
1898         fl.devmajor = 0;
1899         fl.devminor = 0;
1900
1901         /* XXX should reset to %deflang value */
1902         if (fl.currentLangs) {
1903             int i;
1904             for (i = 0; i < fl.nLangs; i++)
1905                 fl.currentLangs[i] = _free(fl.currentLangs[i]);
1906             fl.currentLangs = _free(fl.currentLangs);
1907         }
1908         fl.nLangs = 0;
1909
1910         dupAttrRec(&fl.def_ar, &fl.cur_ar);
1911
1912         if (parseForVerify(buf, &fl))
1913             continue;
1914         if (parseForAttr(buf, &fl))
1915             continue;
1916         if (parseForDev(buf, &fl))
1917             continue;
1918         if (parseForConfig(buf, &fl))
1919             continue;
1920         if (parseForLang(buf, &fl))
1921             continue;
1922         if (parseForSimple(spec, pkg, buf, &fl, &fileName))
1923             continue;
1924         if (fileName == NULL)
1925             continue;
1926
1927         if (fl.isSpecialDoc) {
1928             /* Save this stuff for last */
1929             specialDoc = _free(specialDoc);
1930             specialDoc = xstrdup(fileName);
1931             dupAttrRec(&fl.cur_ar, specialDocAttrRec);
1932         } else if (fl.currentFlags & RPMFILE_PUBKEY) {
1933             (void) processMetadataFile(pkg, &fl, fileName, RPMTAG_PUBKEYS);
1934         } else if (fl.currentFlags & RPMFILE_POLICY) {
1935             (void) processMetadataFile(pkg, &fl, fileName, RPMTAG_POLICIES);
1936         } else {
1937             (void) processBinaryFile(pkg, &fl, fileName);
1938         }
1939     }
1940
1941     /* Now process special doc, if there is one */
1942     if (specialDoc) {
1943         if (installSpecialDoc) {
1944             int _missing_doc_files_terminate_build =
1945                     rpmExpandNumeric("%{?_missing_doc_files_terminate_build}");
1946             rpmRC rc;
1947
1948             rc = doScript(spec, RPMBUILD_STRINGBUF, "%doc", pkg->specialDoc, test);
1949             if (rc != RPMRC_OK && _missing_doc_files_terminate_build)
1950                 fl.processingFailed = 1;
1951         }
1952
1953         /* Reset for %doc */
1954         fl.isDir = 0;
1955         fl.inFtw = 0;
1956         fl.currentFlags = 0;
1957         fl.currentVerifyFlags = fl.defVerifyFlags;
1958
1959         fl.noGlob = 0;
1960         fl.devtype = 0;
1961         fl.devmajor = 0;
1962         fl.devminor = 0;
1963
1964         /* XXX should reset to %deflang value */
1965         if (fl.currentLangs) {
1966             int i;
1967             for (i = 0; i < fl.nLangs; i++)
1968                 fl.currentLangs[i] = _free(fl.currentLangs[i]);
1969             fl.currentLangs = _free(fl.currentLangs);
1970         }
1971         fl.nLangs = 0;
1972
1973         dupAttrRec(specialDocAttrRec, &fl.cur_ar);
1974         freeAttrRec(specialDocAttrRec);
1975
1976         (void) processBinaryFile(pkg, &fl, specialDoc);
1977
1978         specialDoc = _free(specialDoc);
1979     }
1980     
1981     freeSplitString(files);
1982
1983     if (fl.processingFailed)
1984         goto exit;
1985
1986     /* Verify that file attributes scope over hardlinks correctly. */
1987     if (checkHardLinks(&fl))
1988         (void) rpmlibNeedsFeature(pkg->header,
1989                         "PartialHardlinkSets", "4.0.4-1");
1990
1991     genCpioListAndHeader(&fl, &pkg->cpioList, pkg->header, 0);
1992
1993     if (spec->timeCheck)
1994         timeCheck(spec->timeCheck, pkg->header);
1995     
1996 exit:
1997     fl.buildRootURL = _free(fl.buildRootURL);
1998     fl.prefix = _free(fl.prefix);
1999
2000     freeAttrRec(&fl.cur_ar);
2001     freeAttrRec(&fl.def_ar);
2002
2003     if (fl.currentLangs) {
2004         int i;
2005         for (i = 0; i < fl.nLangs; i++)
2006             fl.currentLangs[i] = _free(fl.currentLangs[i]);
2007         fl.currentLangs = _free(fl.currentLangs);
2008     }
2009
2010     fl.fileList = freeFileList(fl.fileList, fl.fileListRecsUsed);
2011     while (fl.docDirCount--)
2012         fl.docDirs[fl.docDirCount] = _free(fl.docDirs[fl.docDirCount]);
2013     return fl.processingFailed;
2014 }
2015
2016 void initSourceHeader(rpmSpec spec)
2017 {
2018     HeaderIterator hi;
2019     rpmTagType type;
2020     rpmTag tag;
2021     rpm_count_t count;
2022     rpm_data_t ptr;
2023
2024     spec->sourceHeader = headerNew();
2025     /* Only specific tags are added to the source package header */
2026     for (hi = headerInitIterator(spec->packages->header);
2027         headerNextIterator(hi, &tag, &type, &ptr, &count);
2028         ptr = headerFreeData(ptr, type))
2029     {
2030         switch (tag) {
2031         case RPMTAG_NAME:
2032         case RPMTAG_VERSION:
2033         case RPMTAG_RELEASE:
2034         case RPMTAG_EPOCH:
2035         case RPMTAG_SUMMARY:
2036         case RPMTAG_DESCRIPTION:
2037         case RPMTAG_PACKAGER:
2038         case RPMTAG_DISTRIBUTION:
2039         case RPMTAG_DISTURL:
2040         case RPMTAG_VENDOR:
2041         case RPMTAG_LICENSE:
2042         case RPMTAG_GROUP:
2043         case RPMTAG_OS:
2044         case RPMTAG_ARCH:
2045         case RPMTAG_CHANGELOGTIME:
2046         case RPMTAG_CHANGELOGNAME:
2047         case RPMTAG_CHANGELOGTEXT:
2048         case RPMTAG_URL:
2049         case HEADER_I18NTABLE:
2050             if (ptr)
2051                 (void)headerAddEntry(spec->sourceHeader, tag, type, ptr, count);
2052             break;
2053         default:
2054             /* do not copy */
2055             break;
2056         }
2057     }
2058     hi = headerFreeIterator(hi);
2059
2060     /* Add the build restrictions */
2061     for (hi = headerInitIterator(spec->buildRestrictions);
2062         headerNextIterator(hi, &tag, &type, &ptr, &count);
2063         ptr = headerFreeData(ptr, type))
2064     {
2065         if (ptr)
2066             (void) headerAddEntry(spec->sourceHeader, tag, type, ptr, count);
2067     }
2068     hi = headerFreeIterator(hi);
2069
2070     if (spec->BANames && spec->BACount > 0) {
2071         (void) headerAddEntry(spec->sourceHeader, RPMTAG_BUILDARCHS,
2072                        RPM_STRING_ARRAY_TYPE,
2073                        spec->BANames, (rpm_count_t) spec->BACount);
2074     }
2075 }
2076
2077 int processSourceFiles(rpmSpec spec)
2078 {
2079     struct Source *srcPtr;
2080     StringBuf sourceFiles;
2081     int x, isSpec = 1;
2082     struct FileList_s fl;
2083     char *s, **files, **fp;
2084     Package pkg;
2085     static char *_srcdefattr;
2086     static int oneshot;
2087
2088     if (!oneshot) {
2089         _srcdefattr = rpmExpand("%{?_srcdefattr}", NULL);
2090         if (_srcdefattr && !*_srcdefattr)
2091             _srcdefattr = _free(_srcdefattr);
2092         oneshot = 1;
2093     }
2094     sourceFiles = newStringBuf();
2095
2096     /* XXX
2097      * XXX This is where the source header for noarch packages needs
2098      * XXX to be initialized.
2099      */
2100     if (spec->sourceHeader == NULL)
2101         initSourceHeader(spec);
2102
2103     /* Construct the file list and source entries */
2104     appendLineStringBuf(sourceFiles, spec->specFile);
2105     if (spec->sourceHeader != NULL)
2106     for (srcPtr = spec->sources; srcPtr != NULL; srcPtr = srcPtr->next) {
2107         if (srcPtr->flags & RPMBUILD_ISSOURCE) {
2108             (void) headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_SOURCE,
2109                                    RPM_STRING_ARRAY_TYPE, &srcPtr->source, 1);
2110             if (srcPtr->flags & RPMBUILD_ISNO) {
2111                 (void) headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_NOSOURCE,
2112                                        RPM_INT32_TYPE, &srcPtr->num, 1);
2113             }
2114         }
2115         if (srcPtr->flags & RPMBUILD_ISPATCH) {
2116             (void) headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_PATCH,
2117                                    RPM_STRING_ARRAY_TYPE, &srcPtr->source, 1);
2118             if (srcPtr->flags & RPMBUILD_ISNO) {
2119                 (void) headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_NOPATCH,
2120                                        RPM_INT32_TYPE, &srcPtr->num, 1);
2121             }
2122         }
2123
2124       { char * sfn;
2125         sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
2126                 "%{_sourcedir}/", srcPtr->source, NULL);
2127         appendLineStringBuf(sourceFiles, sfn);
2128         sfn = _free(sfn);
2129       }
2130     }
2131
2132     for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
2133         for (srcPtr = pkg->icon; srcPtr != NULL; srcPtr = srcPtr->next) {
2134             char * sfn;
2135             sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
2136                 "%{_sourcedir}/", srcPtr->source, NULL);
2137             appendLineStringBuf(sourceFiles, sfn);
2138             sfn = _free(sfn);
2139         }
2140     }
2141
2142     spec->sourceCpioList = NULL;
2143
2144     /* Init the file list structure */
2145     memset(&fl, 0, sizeof(fl));
2146     if (_srcdefattr) {
2147         char *a = xmalloc(strlen(_srcdefattr) + 9 + 1);
2148         strcpy(a, "%defattr ");
2149         strcpy(a + 9, _srcdefattr);
2150         parseForAttr(a, &fl);
2151         a = _free(a);
2152     }
2153     fl.fileList = xcalloc((spec->numSources + 1), sizeof(*fl.fileList));
2154     fl.processingFailed = 0;
2155     fl.fileListRecsUsed = 0;
2156     fl.totalFileSize = 0;
2157     fl.prefix = NULL;
2158     fl.buildRootURL = NULL;
2159
2160     s = getStringBuf(sourceFiles);
2161     files = splitString(s, strlen(s), '\n');
2162
2163     /* The first source file is the spec file */
2164     x = 0;
2165     for (fp = files; *fp != NULL; fp++) {
2166         const char * diskURL, *diskPath;
2167         FileListRec flp;
2168
2169         diskURL = *fp;
2170         SKIPSPACE(diskURL);
2171         if (! *diskURL)
2172             continue;
2173
2174         flp = &fl.fileList[x];
2175
2176         flp->flags = isSpec ? RPMFILE_SPECFILE : 0;
2177         /* files with leading ! are no source files */
2178         if (*diskURL == '!') {
2179             flp->flags |= RPMFILE_GHOST;
2180             diskURL++;
2181         }
2182
2183         (void) urlPath(diskURL, &diskPath);
2184
2185         flp->diskURL = xstrdup(diskURL);
2186         diskPath = strrchr(diskPath, '/');
2187         if (diskPath)
2188             diskPath++;
2189         else
2190             diskPath = diskURL;
2191
2192         flp->fileURL = xstrdup(diskPath);
2193         flp->verifyFlags = RPMVERIFY_ALL;
2194
2195         if (stat(diskURL, &flp->fl_st)) {
2196             rpmlog(RPMLOG_ERR, _("Bad file: %s: %s\n"),
2197                 diskURL, strerror(errno));
2198             fl.processingFailed = 1;
2199         }
2200
2201         if (fl.def_ar.ar_fmodestr) {
2202             flp->fl_mode &= S_IFMT;
2203             flp->fl_mode |= fl.def_ar.ar_fmode;
2204         }
2205         if (fl.def_ar.ar_user) {
2206             flp->uname = getUnameS(fl.def_ar.ar_user);
2207         } else {
2208             flp->uname = getUname(flp->fl_uid);
2209         }
2210         if (fl.def_ar.ar_group) {
2211             flp->gname = getGnameS(fl.def_ar.ar_group);
2212         } else {
2213             flp->gname = getGname(flp->fl_gid);
2214         }
2215         flp->langs = xstrdup("");
2216         
2217         fl.totalFileSize += flp->fl_size;
2218         
2219         if (! (flp->uname && flp->gname)) {
2220             rpmlog(RPMLOG_ERR, _("Bad owner/group: %s\n"), diskURL);
2221             fl.processingFailed = 1;
2222         }
2223
2224         isSpec = 0;
2225         x++;
2226     }
2227     fl.fileListRecsUsed = x;
2228     freeSplitString(files);
2229
2230     if (! fl.processingFailed) {
2231         if (spec->sourceHeader != NULL)
2232             genCpioListAndHeader(&fl, &spec->sourceCpioList,
2233                         spec->sourceHeader, 1);
2234     }
2235
2236     sourceFiles = freeStringBuf(sourceFiles);
2237     fl.fileList = freeFileList(fl.fileList, fl.fileListRecsUsed);
2238     freeAttrRec(&fl.def_ar);
2239     return fl.processingFailed;
2240 }
2241
2242 /**
2243  * Check packaged file list against what's in the build root.
2244  * @param fileList      packaged file list
2245  * @return              -1 if skipped, 0 on OK, 1 on error
2246  */
2247 static int checkFiles(StringBuf fileList)
2248 {
2249     static const char * av_ckfile[] = { "%{?__check_files}", NULL };
2250     StringBuf sb_stdout = NULL;
2251     char * s;
2252     int rc;
2253     
2254     s = rpmExpand(av_ckfile[0], NULL);
2255     if (!(s && *s)) {
2256         rc = -1;
2257         goto exit;
2258     }
2259     rc = 0;
2260
2261     rpmlog(RPMLOG_NOTICE, _("Checking for unpackaged file(s): %s\n"), s);
2262
2263     rc = rpmfcExec(av_ckfile, fileList, &sb_stdout, 0);
2264     if (rc < 0)
2265         goto exit;
2266     
2267     if (sb_stdout) {
2268         int _unpackaged_files_terminate_build =
2269                 rpmExpandNumeric("%{?_unpackaged_files_terminate_build}");
2270         const char * t;
2271
2272         t = getStringBuf(sb_stdout);
2273         if ((*t != '\0') && (*t != '\n')) {
2274             rc = (_unpackaged_files_terminate_build) ? 1 : 0;
2275             rpmlog((rc ? RPMLOG_ERR : RPMLOG_WARNING),
2276                 _("Installed (but unpackaged) file(s) found:\n%s"), t);
2277         }
2278     }
2279     
2280 exit:
2281     sb_stdout = freeStringBuf(sb_stdout);
2282     s = _free(s);
2283     return rc;
2284 }
2285
2286 int processBinaryFiles(rpmSpec spec, int installSpecialDoc, int test)
2287 {
2288     Package pkg;
2289     int res = 0;
2290     
2291     check_fileList = newStringBuf();
2292     
2293     for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
2294         const char *n, *v, *r;
2295         int rc;
2296
2297         if (pkg->fileList == NULL)
2298             continue;
2299
2300         (void) headerNVR(pkg->header, &n, &v, &r);
2301         rpmlog(RPMLOG_NOTICE, _("Processing files: %s-%s-%s\n"), n, v, r);
2302                    
2303         if ((rc = processPackageFiles(spec, pkg, installSpecialDoc, test)))
2304             res = rc;
2305
2306         if ((rc = rpmfcGenerateDepends(spec, pkg)))
2307             res = rc;
2308     }
2309
2310     /* Now we have in fileList list of files from all packages.
2311      * We pass it to a script which does the work of finding missing
2312      * and duplicated files.
2313      */
2314     
2315     
2316     if (checkFiles(check_fileList) > 0) {
2317         if (res == 0)
2318             res = 1;
2319     }
2320     
2321     check_fileList = freeStringBuf(check_fileList);
2322     
2323     return res;
2324 }