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