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