adbadf6b05c3e16c588ab00fe7f3ec30e7f0d331
[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 #include <signal.h>     /* getOutputFrom() */
13
14 #include <rpmio_internal.h>
15 #include <rpmbuild.h>
16 #include <rpmmacro.h>
17
18 #include "buildio.h"
19
20 #include "myftw.h"
21 #include "misc.h"
22 #include "debug.h"
23
24 /*@access Header @*/
25 /*@access TFI_t @*/
26 /*@access FD_t @*/
27 /*@access StringBuf @*/         /* compared with NULL */
28
29 #define SKIPWHITE(_x)   {while(*(_x) && (xisspace(*_x) || *(_x) == ',')) (_x)++;}
30 #define SKIPNONWHITE(_x){while(*(_x) &&!(xisspace(*_x) || *(_x) == ',')) (_x)++;}
31
32 #define MAXDOCDIR 1024
33
34 /*@-redecl@*/
35 /*@unchecked@*/
36 extern int _noDirTokens;
37 /*@=redecl@*/
38
39 /**
40  */
41 typedef enum specdFlags_e {
42     SPECD_DEFFILEMODE   = (1 << 0),
43     SPECD_DEFDIRMODE    = (1 << 1),
44     SPECD_DEFUID        = (1 << 2),
45     SPECD_DEFGID        = (1 << 3),
46     SPECD_DEFVERIFY     = (1 << 4),
47
48     SPECD_FILEMODE      = (1 << 8),
49     SPECD_DIRMODE       = (1 << 9),
50     SPECD_UID           = (1 << 10),
51     SPECD_GID           = (1 << 11),
52     SPECD_VERIFY        = (1 << 12)
53 } specdFlags;
54
55 /**
56  */
57 typedef struct FileListRec_s {
58     struct stat fl_st;
59 #define fl_dev  fl_st.st_dev
60 #define fl_ino  fl_st.st_ino
61 #define fl_mode fl_st.st_mode
62 #define fl_nlink fl_st.st_nlink
63 #define fl_uid  fl_st.st_uid
64 #define fl_gid  fl_st.st_gid
65 #define fl_rdev fl_st.st_rdev
66 #define fl_size fl_st.st_size
67 #define fl_mtime fl_st.st_mtime
68
69 /*@only@*/ const char * diskURL;        /* get file from here       */
70 /*@only@*/ const char * fileURL;        /* filename in cpio archive */
71 /*@observer@*/ const char * uname;
72 /*@observer@*/ const char * gname;
73     int         flags;
74     specdFlags  specdFlags;     /* which attributes have been explicitly specified. */
75     int         verifyFlags;
76 /*@only@*/ const char *langs;   /* XXX locales separated with | */
77 } * FileListRec;
78
79 /**
80  */
81 typedef struct AttrRec_s {
82     const char * ar_fmodestr;
83     const char * ar_dmodestr;
84     const char * ar_user;
85     const char * ar_group;
86     mode_t      ar_fmode;
87     mode_t      ar_dmode;
88 } * AttrRec;
89
90 /**
91  */
92 /*@unchecked@*/
93 static int multiLib = 0;        /* MULTILIB */
94
95 /**
96  * Package file tree walk data.
97  */
98 typedef struct FileList_s {
99 /*@only@*/ const char * buildRootURL;
100 /*@only@*/ const char * prefix;
101
102     int fileCount;
103     int totalFileSize;
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     int inFtw;
116     int currentFlags;
117     specdFlags currentSpecdFlags;
118     int currentVerifyFlags;
119     struct AttrRec_s cur_ar;
120     struct AttrRec_s def_ar;
121     specdFlags defSpecdFlags;
122     int defVerifyFlags;
123     int nLangs;
124 /*@only@*/ /*@null@*/ const char ** currentLangs;
125
126     /* Hard coded limit of MAXDOCDIR docdirs.         */
127     /* If you break it you are doing something wrong. */
128     const char * docDirs[MAXDOCDIR];
129     int docDirCount;
130     
131 /*@only@*/ FileListRec fileList;
132     int fileListRecsAlloced;
133     int fileListRecsUsed;
134 } * FileList;
135
136 /**
137  */
138 static void nullAttrRec(/*@out@*/ AttrRec ar)   /*@modifies ar @*/
139 {
140     ar->ar_fmodestr = NULL;
141     ar->ar_dmodestr = NULL;
142     ar->ar_user = NULL;
143     ar->ar_group = NULL;
144     ar->ar_fmode = 0;
145     ar->ar_dmode = 0;
146 }
147
148 /**
149  */
150 static void freeAttrRec(AttrRec ar)     /*@modifies ar @*/
151 {
152     ar->ar_fmodestr = _free(ar->ar_fmodestr);
153     ar->ar_dmodestr = _free(ar->ar_dmodestr);
154     ar->ar_user = _free(ar->ar_user);
155     ar->ar_group = _free(ar->ar_group);
156     /* XXX doesn't free ar (yet) */
157     /*@-nullstate@*/
158     return;
159     /*@=nullstate@*/
160 }
161
162 /**
163  */
164 static void dupAttrRec(const AttrRec oar, /*@in@*/ /*@out@*/ AttrRec nar)
165         /*@modifies nar @*/
166 {
167     if (oar == nar)
168         return;
169     freeAttrRec(nar);
170     nar->ar_fmodestr = (oar->ar_fmodestr ? xstrdup(oar->ar_fmodestr) : NULL);
171     nar->ar_dmodestr = (oar->ar_dmodestr ? xstrdup(oar->ar_dmodestr) : NULL);
172     nar->ar_user = (oar->ar_user ? xstrdup(oar->ar_user) : NULL);
173     nar->ar_group = (oar->ar_group ? xstrdup(oar->ar_group) : NULL);
174     nar->ar_fmode = oar->ar_fmode;
175     nar->ar_dmode = oar->ar_dmode;
176 }
177
178 #if 0
179 /**
180  */
181 static void dumpAttrRec(const char * msg, AttrRec ar)
182         /*@globals fileSystem@*/
183         /*@modifies fileSystem @*/
184 {
185     if (msg)
186         fprintf(stderr, "%s:\t", msg);
187     fprintf(stderr, "(%s, %s, %s, %s)\n",
188         ar->ar_fmodestr,
189         ar->ar_user,
190         ar->ar_group,
191         ar->ar_dmodestr);
192 }
193 #endif
194
195 /* strtokWithQuotes() modified from glibc strtok() */
196 /* Copyright (C) 1991, 1996 Free Software Foundation, Inc.
197    This file is part of the GNU C Library.
198
199    The GNU C Library is free software; you can redistribute it and/or
200    modify it under the terms of the GNU Library General Public License as
201    published by the Free Software Foundation; either version 2 of the
202    License, or (at your option) any later version.
203
204    The GNU C Library is distributed in the hope that it will be useful,
205    but WITHOUT ANY WARRANTY; without even the implied warranty of
206    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
207    Library General Public License for more details.
208
209    You should have received a copy of the GNU Library General Public
210    License along with the GNU C Library; see the file COPYING.LIB.  If
211    not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
212    Boston, MA 02111-1307, USA.  */
213
214 /**
215  */
216 static char *strtokWithQuotes(char *s, char *delim)
217         /*@*/
218 {
219     static char *olds = NULL;
220     char *token;
221
222     if (s == NULL) {
223         s = olds;
224     }
225
226     /* Skip leading delimiters */
227     s += strspn(s, delim);
228     if (*s == '\0') {
229         return NULL;
230     }
231
232     /* Find the end of the token.  */
233     token = s;
234     if (*token == '"') {
235         token++;
236         /* Find next " char */
237         s = strchr(token, '"');
238     } else {
239         s = strpbrk(token, delim);
240     }
241
242     /* Terminate it */
243     if (s == NULL) {
244         /* This token finishes the string */
245         olds = strchr(token, '\0');
246     } else {
247         /* Terminate the token and make olds point past it */
248         *s = '\0';
249         olds = s+1;
250     }
251
252     return token;
253 }
254
255 /**
256  */
257 static void timeCheck(int tc, Header h)
258         /*@globals internalState @*/
259         /*@modifies internalState @*/
260 {
261     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
262     HFD_t hfd = headerFreeData;
263     int * mtime;
264     const char ** files;
265     rpmTagType fnt;
266     int count, x;
267     time_t currentTime = time(NULL);
268
269     x = hge(h, RPMTAG_OLDFILENAMES, &fnt, (void **) &files, &count);
270     x = hge(h, RPMTAG_FILEMTIMES, NULL, (void **) &mtime, NULL);
271     
272     for (x = 0; x < count; x++) {
273         if ((currentTime - mtime[x]) > tc)
274             rpmMessage(RPMMESS_WARNING, _("TIMECHECK failure: %s\n"), files[x]);
275     }
276     files = hfd(files, fnt);
277 }
278
279 /**
280  */
281 typedef struct VFA {
282 /*@observer@*/ /*@null@*/ const char * attribute;
283     int flag;
284 } VFA_t;
285
286 /**
287  */
288 /*@-exportlocal -exportheadervar@*/
289 /*@unchecked@*/
290 VFA_t verifyAttrs[] = {
291     { "md5",    RPMVERIFY_MD5 },
292     { "size",   RPMVERIFY_FILESIZE },
293     { "link",   RPMVERIFY_LINKTO },
294     { "user",   RPMVERIFY_USER },
295     { "group",  RPMVERIFY_GROUP },
296     { "mtime",  RPMVERIFY_MTIME },
297     { "mode",   RPMVERIFY_MODE },
298     { "rdev",   RPMVERIFY_RDEV },
299     { NULL, 0 }
300 };
301 /*@=exportlocal =exportheadervar@*/
302
303 /**
304  * @param fl            package file tree walk data
305  */
306 static int parseForVerify(char * buf, FileList fl)
307         /*@modifies buf, fl->processingFailed,
308                 fl->currentVerifyFlags, fl->defVerifyFlags,
309                 fl->currentSpecdFlags, fl->defSpecdFlags @*/
310 {
311     char *p, *pe, *q;
312     const char *name;
313     int *resultVerify;
314     int negated;
315     int verifyFlags;
316     specdFlags * specdFlags;
317
318     if ((p = strstr(buf, (name = "%verify"))) != NULL) {
319         resultVerify = &(fl->currentVerifyFlags);
320         specdFlags = &fl->currentSpecdFlags;
321     } else if ((p = strstr(buf, (name = "%defverify"))) != NULL) {
322         resultVerify = &(fl->defVerifyFlags);
323         specdFlags = &fl->defSpecdFlags;
324     } else
325         return 0;
326
327     for (pe = p; (pe-p) < strlen(name); pe++)
328         *pe = ' ';
329
330     SKIPSPACE(pe);
331
332     if (*pe != '(') {
333         rpmError(RPMERR_BADSPEC, _("Missing '(' in %s %s\n"), name, pe);
334         fl->processingFailed = 1;
335         return RPMERR_BADSPEC;
336     }
337
338     /* Bracket %*verify args */
339     *pe++ = ' ';
340     for (p = pe; *pe && *pe != ')'; pe++)
341         {};
342
343     if (*pe == '\0') {
344         rpmError(RPMERR_BADSPEC, _("Missing ')' in %s(%s\n"), name, p);
345         fl->processingFailed = 1;
346         return RPMERR_BADSPEC;
347     }
348
349     /* Localize. Erase parsed string */
350     q = alloca((pe-p) + 1);
351     strncpy(q, p, pe-p);
352     q[pe-p] = '\0';
353     while (p <= pe)
354         *p++ = ' ';
355
356     negated = 0;
357     verifyFlags = RPMVERIFY_NONE;
358
359     for (p = q; *p != '\0'; p = pe) {
360         SKIPWHITE(p);
361         if (*p == '\0')
362             break;
363         pe = p;
364         SKIPNONWHITE(pe);
365         if (*pe != '\0')
366             *pe++ = '\0';
367
368         {   VFA_t *vfa;
369             for (vfa = verifyAttrs; vfa->attribute != NULL; vfa++) {
370                 if (strcmp(p, vfa->attribute))
371                     /*@innercontinue@*/ continue;
372                 verifyFlags |= vfa->flag;
373                 /*@innerbreak@*/ break;
374             }
375             if (vfa->attribute)
376                 continue;
377         }
378
379         if (!strcmp(p, "not")) {
380             negated ^= 1;
381         } else {
382             rpmError(RPMERR_BADSPEC, _("Invalid %s token: %s\n"), name, p);
383             fl->processingFailed = 1;
384             return RPMERR_BADSPEC;
385         }
386     }
387
388     *resultVerify = negated ? ~(verifyFlags) : verifyFlags;
389     *specdFlags |= SPECD_VERIFY;
390
391     return 0;
392 }
393
394 #define isAttrDefault(_ars)     ((_ars)[0] == '-' && (_ars)[1] == '\0')
395
396 /**
397  * Parse %dev from file manifest.
398  * @param fl            package file tree walk data
399  */
400 static int parseForDev(char * buf, FileList fl)
401         /*@modifies buf, fl->processingFailed,
402                 fl->noGlob, fl->devtype, fl->devmajor, fl->devminor @*/
403 {
404     const char * name;
405     const char * errstr = NULL;
406     char *p, *pe, *q;
407     int rc = RPMERR_BADSPEC;    /* assume error */
408
409     if ((p = strstr(buf, (name = "%dev"))) == NULL)
410         return 0;
411
412     for (pe = p; (pe-p) < strlen(name); pe++)
413         *pe = ' ';
414     SKIPSPACE(pe);
415
416     if (*pe != '(') {
417         errstr = "'('";
418         goto exit;
419     }
420
421     /* Bracket %dev args */
422     *pe++ = ' ';
423     for (p = pe; *pe && *pe != ')'; pe++)
424         {};
425     if (*pe != ')') {
426         errstr = "')'";
427         goto exit;
428     }
429
430     /* Localize. Erase parsed string */
431     q = alloca((pe-p) + 1);
432     strncpy(q, p, pe-p);
433     q[pe-p] = '\0';
434     while (p <= pe)
435         *p++ = ' ';
436
437     p = q; SKIPWHITE(p);
438     pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
439     if (*p == 'b')
440         fl->devtype = 'b';
441     else if (*p == 'c')
442         fl->devtype = 'c';
443     else {
444         errstr = "devtype";
445         goto exit;
446     }
447
448     p = pe; SKIPWHITE(p);
449     pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
450     for (pe = p; *pe && xisdigit(*pe); pe++)
451         {} ;
452     if (*pe == '\0') {
453         fl->devmajor = atoi(p);
454         /*@-unsignedcompare @*/ /* LCL: ge is ok */
455         if (!(fl->devmajor >= 0 && fl->devmajor < 256)) {
456             errstr = "devmajor";
457             goto exit;
458         }
459         /*@=unsignedcompare @*/
460         pe++;
461     } else {
462         errstr = "devmajor";
463         goto exit;
464     }
465
466     p = pe; SKIPWHITE(p);
467     pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
468     for (pe = p; *pe && xisdigit(*pe); pe++)
469         {} ;
470     if (*pe == '\0') {
471         fl->devminor = atoi(p);
472         if (!(fl->devminor >= 0 && fl->devminor < 256)) {
473             errstr = "devminor";
474             goto exit;
475         }
476         pe++;
477     } else {
478         errstr = "devminor";
479         goto exit;
480     }
481
482     fl->noGlob = 1;
483
484     rc = 0;
485
486 exit:
487     if (rc) {
488         rpmError(RPMERR_BADSPEC, _("Missing %s in %s %s\n"), errstr, name, p);
489         fl->processingFailed = 1;
490     }
491     return rc;
492 }
493
494 /**
495  * Parse %attr and %defattr from file manifest.
496  * @param fl            package file tree walk data
497  */
498 static int parseForAttr(char * buf, FileList fl)
499         /*@modifies buf, fl->processingFailed,
500                 fl->cur_ar, fl->def_ar,
501                 fl->currentSpecdFlags, fl->defSpecdFlags @*/
502 {
503     const char *name;
504     char *p, *pe, *q;
505     int x;
506     struct AttrRec_s arbuf;
507     AttrRec ar = &arbuf, ret_ar;
508     specdFlags * specdFlags;
509
510     if ((p = strstr(buf, (name = "%attr"))) != NULL) {
511         ret_ar = &(fl->cur_ar);
512         specdFlags = &fl->currentSpecdFlags;
513     } else if ((p = strstr(buf, (name = "%defattr"))) != NULL) {
514         ret_ar = &(fl->def_ar);
515         specdFlags = &fl->defSpecdFlags;
516     } else
517         return 0;
518
519     for (pe = p; (pe-p) < strlen(name); pe++)
520         *pe = ' ';
521
522     SKIPSPACE(pe);
523
524     if (*pe != '(') {
525         rpmError(RPMERR_BADSPEC, _("Missing '(' in %s %s\n"), name, pe);
526         fl->processingFailed = 1;
527         return RPMERR_BADSPEC;
528     }
529
530     /* Bracket %*attr args */
531     *pe++ = ' ';
532     for (p = pe; *pe && *pe != ')'; pe++)
533         {};
534
535     if (ret_ar == &(fl->def_ar)) {      /* %defattr */
536         q = pe;
537         q++;
538         SKIPSPACE(q);
539         if (*q != '\0') {
540             rpmError(RPMERR_BADSPEC,
541                      _("Non-white space follows %s(): %s\n"), name, q);
542             fl->processingFailed = 1;
543             return RPMERR_BADSPEC;
544         }
545     }
546
547     /* Localize. Erase parsed string */
548     q = alloca((pe-p) + 1);
549     strncpy(q, p, pe-p);
550     q[pe-p] = '\0';
551     while (p <= pe)
552         *p++ = ' ';
553
554     nullAttrRec(ar);
555
556     p = q; SKIPWHITE(p);
557     if (*p != '\0') {
558         pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
559         ar->ar_fmodestr = p;
560         p = pe; SKIPWHITE(p);
561     }
562     if (*p != '\0') {
563         pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
564         ar->ar_user = p;
565         p = pe; SKIPWHITE(p);
566     }
567     if (*p != '\0') {
568         pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
569         ar->ar_group = p;
570         p = pe; SKIPWHITE(p);
571     }
572     if (*p != '\0' && ret_ar == &(fl->def_ar)) {        /* %defattr */
573         pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
574         ar->ar_dmodestr = p;
575         p = pe; SKIPWHITE(p);
576     }
577
578     if (!(ar->ar_fmodestr && ar->ar_user && ar->ar_group) || *p != '\0') {
579         rpmError(RPMERR_BADSPEC, _("Bad syntax: %s(%s)\n"), name, q);
580         fl->processingFailed = 1;
581         return RPMERR_BADSPEC;
582     }
583
584     /* Do a quick test on the mode argument and adjust for "-" */
585     if (ar->ar_fmodestr && !isAttrDefault(ar->ar_fmodestr)) {
586         unsigned int ui;
587         x = sscanf(ar->ar_fmodestr, "%o", &ui);
588         if ((x == 0) || (ar->ar_fmode & ~MYALLPERMS)) {
589             rpmError(RPMERR_BADSPEC, _("Bad mode spec: %s(%s)\n"), name, q);
590             fl->processingFailed = 1;
591             return RPMERR_BADSPEC;
592         }
593         ar->ar_fmode = ui;
594     } else
595         ar->ar_fmodestr = NULL;
596
597     if (ar->ar_dmodestr && !isAttrDefault(ar->ar_dmodestr)) {
598         unsigned int ui;
599         x = sscanf(ar->ar_dmodestr, "%o", &ui);
600         if ((x == 0) || (ar->ar_dmode & ~MYALLPERMS)) {
601             rpmError(RPMERR_BADSPEC, _("Bad dirmode spec: %s(%s)\n"), name, q);
602             fl->processingFailed = 1;
603             return RPMERR_BADSPEC;
604         }
605         ar->ar_dmode = ui;
606     } else
607         ar->ar_dmodestr = NULL;
608
609     if (!(ar->ar_user && !isAttrDefault(ar->ar_user)))
610         ar->ar_user = NULL;
611
612     if (!(ar->ar_group && !isAttrDefault(ar->ar_group)))
613         ar->ar_group = NULL;
614
615     dupAttrRec(ar, ret_ar);
616
617     /* XXX fix all this */
618     *specdFlags |= SPECD_UID | SPECD_GID | SPECD_FILEMODE | SPECD_DIRMODE;
619     
620     return 0;
621 }
622
623 /**
624  * @param fl            package file tree walk data
625  */
626 static int parseForConfig(char * buf, FileList fl)
627         /*@modifies buf, fl->processingFailed,
628                 fl->currentFlags @*/
629 {
630     char *p, *pe, *q;
631     const char *name;
632
633     if ((p = strstr(buf, (name = "%config"))) == NULL)
634         return 0;
635
636     fl->currentFlags = RPMFILE_CONFIG;
637
638     for (pe = p; (pe-p) < strlen(name); pe++)
639         *pe = ' ';
640     SKIPSPACE(pe);
641     if (*pe != '(')
642         return 0;
643
644     /* Bracket %config args */
645     *pe++ = ' ';
646     for (p = pe; *pe && *pe != ')'; pe++)
647         {};
648
649     if (*pe == '\0') {
650         rpmError(RPMERR_BADSPEC, _("Missing ')' in %s(%s\n"), name, p);
651         fl->processingFailed = 1;
652         return RPMERR_BADSPEC;
653     }
654
655     /* Localize. Erase parsed string */
656     q = alloca((pe-p) + 1);
657     strncpy(q, p, pe-p);
658     q[pe-p] = '\0';
659     while (p <= pe)
660         *p++ = ' ';
661
662     for (p = q; *p != '\0'; p = pe) {
663         SKIPWHITE(p);
664         if (*p == '\0')
665             break;
666         pe = p;
667         SKIPNONWHITE(pe);
668         if (*pe != '\0')
669             *pe++ = '\0';
670         if (!strcmp(p, "missingok")) {
671             fl->currentFlags |= RPMFILE_MISSINGOK;
672         } else if (!strcmp(p, "noreplace")) {
673             fl->currentFlags |= RPMFILE_NOREPLACE;
674         } else {
675             rpmError(RPMERR_BADSPEC, _("Invalid %s token: %s\n"), name, p);
676             fl->processingFailed = 1;
677             return RPMERR_BADSPEC;
678         }
679     }
680
681     return 0;
682 }
683
684 /**
685  */
686 static int langCmp(const void * ap, const void * bp)    /*@*/
687 {
688     return strcmp(*(const char **)ap, *(const char **)bp);
689 }
690
691 /**
692  * @param fl            package file tree walk data
693  */
694 static int parseForLang(char * buf, FileList fl)
695         /*@modifies buf, fl->processingFailed,
696                 fl->currentLangs, fl->nLangs @*/
697 {
698     char *p, *pe, *q;
699     const char *name;
700
701   while ((p = strstr(buf, (name = "%lang"))) != NULL) {
702
703     for (pe = p; (pe-p) < strlen(name); pe++)
704         *pe = ' ';
705     SKIPSPACE(pe);
706
707     if (*pe != '(') {
708         rpmError(RPMERR_BADSPEC, _("Missing '(' in %s %s\n"), name, pe);
709         fl->processingFailed = 1;
710         return RPMERR_BADSPEC;
711     }
712
713     /* Bracket %lang args */
714     *pe++ = ' ';
715     for (pe = p; *pe && *pe != ')'; pe++)
716         {};
717
718     if (*pe == '\0') {
719         rpmError(RPMERR_BADSPEC, _("Missing ')' in %s(%s\n"), name, p);
720         fl->processingFailed = 1;
721         return RPMERR_BADSPEC;
722     }
723
724     /* Localize. Erase parsed string */
725     q = alloca((pe-p) + 1);
726     strncpy(q, p, pe-p);
727     q[pe-p] = '\0';
728     while (p <= pe)
729         *p++ = ' ';
730
731     /* Parse multiple arguments from %lang */
732     for (p = q; *p != '\0'; p = pe) {
733         char *newp;
734         size_t np;
735         int i;
736
737         SKIPWHITE(p);
738         pe = p;
739         SKIPNONWHITE(pe);
740
741         np = pe - p;
742         
743         /* Sanity check on locale lengths */
744         if (np < 1 || (np == 1 && *p != 'C') || np >= 32) {
745             rpmError(RPMERR_BADSPEC,
746                 _("Unusual locale length: \"%.*s\" in %%lang(%s)\n"),
747                 (int)np, p, q);
748             fl->processingFailed = 1;
749             return RPMERR_BADSPEC;
750         }
751
752         /* Check for duplicate locales */
753         if (fl->currentLangs != NULL)
754         for (i = 0; i < fl->nLangs; i++) {
755             if (strncmp(fl->currentLangs[i], p, np))
756                 /*@innercontinue@*/ continue;
757             rpmError(RPMERR_BADSPEC, _("Duplicate locale %.*s in %%lang(%s)\n"),
758                 (int)np, p, q);
759             fl->processingFailed = 1;
760             return RPMERR_BADSPEC;
761         }
762
763         /* Add new locale */
764         fl->currentLangs = xrealloc(fl->currentLangs,
765                                 (fl->nLangs + 1) * sizeof(*fl->currentLangs));
766         newp = xmalloc( np+1 );
767         strncpy(newp, p, np);
768         newp[np] = '\0';
769         fl->currentLangs[fl->nLangs++] = newp;
770         if (*pe == ',') pe++;   /* skip , if present */
771     }
772   }
773
774     /* Insure that locales are sorted. */
775     if (fl->currentLangs)
776         qsort(fl->currentLangs, fl->nLangs, sizeof(*fl->currentLangs), langCmp);
777
778     return 0;
779 }
780
781 /**
782  */
783 static int parseForRegexLang(const char * fileName, /*@out@*/ char ** lang)
784         /*@globals rpmGlobalMacroContext @*/
785         /*@modifies *lang, rpmGlobalMacroContext @*/
786 {
787     static int initialized = 0;
788     static int hasRegex = 0;
789     static regex_t compiledPatt;
790     static char buf[BUFSIZ];
791     int x;
792     regmatch_t matches[2];
793     const char *s;
794
795     if (! initialized) {
796         const char *patt = rpmExpand("%{_langpatt}", NULL);
797         int rc = 0;
798         if (!(patt && *patt != '%'))
799             rc = 1;
800         else if (regcomp(&compiledPatt, patt, REG_EXTENDED))
801             rc = -1;
802         patt = _free(patt);
803         if (rc)
804             return rc;
805         hasRegex = 1;
806         initialized = 1;
807     }
808     
809     memset(matches, 0, sizeof(matches));
810     if (! hasRegex || regexec(&compiledPatt, fileName, 2, matches, REG_NOTEOL))
811         return 1;
812
813     /* Got match */
814     s = fileName + matches[1].rm_eo - 1;
815     x = matches[1].rm_eo - matches[1].rm_so;
816     buf[x] = '\0';
817     while (x) {
818         buf[--x] = *s--;
819     }
820     if (lang)
821         *lang = buf;
822     return 0;
823 }
824
825 /**
826  */
827 static int parseForRegexMultiLib(const char *fileName)
828         /*@globals rpmGlobalMacroContext @*/
829         /*@modifies rpmGlobalMacroContext @*/
830 {
831     static int initialized = 0;
832     static int hasRegex = 0;
833     static regex_t compiledPatt;
834
835     if (! initialized) {
836         const char *patt;
837         int rc = 0;
838
839         initialized = 1;
840         patt = rpmExpand("%{_multilibpatt}", NULL);
841         if (!(patt && *patt != '%'))
842             rc = 1;
843         else if (regcomp(&compiledPatt, patt, REG_EXTENDED | REG_NOSUB))
844             rc = -1;
845         patt = _free(patt);
846         if (rc)
847             return rc;
848         hasRegex = 1;
849     }
850
851     if (! hasRegex || regexec(&compiledPatt, fileName, 0, NULL, 0))
852         return 1;
853
854     return 0;
855 }
856
857 /**
858  */
859 /*@-exportlocal -exportheadervar@*/
860 /*@unchecked@*/
861 VFA_t virtualFileAttributes[] = {
862         { "%dir",       0 },    /* XXX why not RPMFILE_DIR? */
863         { "%doc",       RPMFILE_DOC },
864         { "%ghost",     RPMFILE_GHOST },
865         { "%exclude",   RPMFILE_EXCLUDE },
866         { "%readme",    RPMFILE_README },
867         { "%license",   RPMFILE_LICENSE },
868         { "%multilib",  0 },
869
870 #if WHY_NOT
871         { "%spec",      RPMFILE_SPEC },
872         { "%config",    RPMFILE_CONFIG },
873         { "%donotuse",  RPMFILE_DONOTUSE },     /* XXX WTFO? */
874         { "%missingok", RPMFILE_CONFIG|RPMFILE_MISSINGOK },
875         { "%noreplace", RPMFILE_CONFIG|RPMFILE_NOREPLACE },
876 #endif
877
878         { NULL, 0 }
879 };
880 /*@=exportlocal =exportheadervar@*/
881
882 /**
883  * @param fl            package file tree walk data
884  */
885 static int parseForSimple(/*@unused@*/Spec spec, Package pkg, char * buf,
886                           FileList fl, /*@out@*/ const char ** fileName)
887         /*@globals rpmGlobalMacroContext @*/
888         /*@modifies buf, fl->processingFailed, *fileName,
889                 fl->currentFlags,
890                 fl->docDirs, fl->docDirCount, fl->isDir,
891                 fl->passedSpecialDoc, fl->isSpecialDoc,
892                 pkg->specialDoc, rpmGlobalMacroContext @*/
893 {
894     char *s, *t;
895     int res, specialDoc = 0;
896     char specialDocBuf[BUFSIZ];
897
898     specialDocBuf[0] = '\0';
899     *fileName = NULL;
900     res = 0;
901
902     t = buf;
903     while ((s = strtokWithQuotes(t, " \t\n")) != NULL) {
904         t = NULL;
905         if (!strcmp(s, "%docdir")) {
906             s = strtokWithQuotes(NULL, " \t\n");
907             if (fl->docDirCount == MAXDOCDIR) {
908                 rpmError(RPMERR_INTERNAL, _("Hit limit for %%docdir\n"));
909                 fl->processingFailed = 1;
910                 res = 1;
911             }
912             fl->docDirs[fl->docDirCount++] = xstrdup(s);
913             if (strtokWithQuotes(NULL, " \t\n")) {
914                 rpmError(RPMERR_INTERNAL, _("Only one arg for %%docdir\n"));
915                 fl->processingFailed = 1;
916                 res = 1;
917             }
918             break;
919         }
920
921     /* Set flags for virtual file attributes */
922     {   VFA_t *vfa;
923         for (vfa = virtualFileAttributes; vfa->attribute != NULL; vfa++) {
924             if (strcmp(s, vfa->attribute))
925                 /*@innercontinue@*/ continue;
926             if (!vfa->flag) {
927                 if (!strcmp(s, "%dir"))
928                     fl->isDir = 1;      /* XXX why not RPMFILE_DIR? */
929                 else if (!strcmp(s, "%multilib"))
930                     fl->currentFlags |= multiLib;
931             } else
932                 fl->currentFlags |= vfa->flag;
933             /*@innerbreak@*/ break;
934         }
935         /* if we got an attribute, continue with next token */
936         if (vfa->attribute != NULL)
937             continue;
938     }
939
940         if (*fileName) {
941             /* We already got a file -- error */
942             rpmError(RPMERR_BADSPEC, _("Two files on one line: %s\n"),
943                 *fileName);
944             fl->processingFailed = 1;
945             res = 1;
946         }
947
948         /*@-branchstate@*/
949         if (*s != '/') {
950             if (fl->currentFlags & RPMFILE_DOC) {
951                 specialDoc = 1;
952                 strcat(specialDocBuf, " ");
953                 strcat(specialDocBuf, s);
954             } else {
955                 /* not in %doc, does not begin with / -- error */
956                 rpmError(RPMERR_BADSPEC,
957                     _("File must begin with \"/\": %s\n"), s);
958                 fl->processingFailed = 1;
959                 res = 1;
960             }
961         } else {
962             *fileName = s;
963         }
964         /*@=branchstate@*/
965     }
966
967     if (specialDoc) {
968         if (*fileName || (fl->currentFlags & ~(RPMFILE_DOC))) {
969             rpmError(RPMERR_BADSPEC,
970                      _("Can't mix special %%doc with other forms: %s\n"),
971                      (*fileName ? *fileName : ""));
972             fl->processingFailed = 1;
973             res = 1;
974         } else {
975         /* XXX WATCHOUT: buf is an arg */
976             {   const char *ddir, *n, *v;
977
978                 (void) headerNVR(pkg->header, &n, &v, NULL);
979
980                 ddir = rpmGetPath("%{_docdir}/", n, "-", v, NULL);
981                 strcpy(buf, ddir);
982                 ddir = _free(ddir);
983             }
984
985         /* XXX FIXME: this is easy to do as macro expansion */
986
987             if (! fl->passedSpecialDoc) {
988                 pkg->specialDoc = newStringBuf();
989                 appendStringBuf(pkg->specialDoc, "DOCDIR=$RPM_BUILD_ROOT");
990                 appendLineStringBuf(pkg->specialDoc, buf);
991                 appendLineStringBuf(pkg->specialDoc, "export DOCDIR");
992                 appendLineStringBuf(pkg->specialDoc, "rm -rf $DOCDIR");
993                 appendLineStringBuf(pkg->specialDoc, MKDIR_P " $DOCDIR");
994
995                 /*@-temptrans@*/
996                 *fileName = buf;
997                 /*@=temptrans@*/
998                 fl->passedSpecialDoc = 1;
999                 fl->isSpecialDoc = 1;
1000             }
1001
1002             appendStringBuf(pkg->specialDoc, "cp -pr ");
1003             appendStringBuf(pkg->specialDoc, specialDocBuf);
1004             appendLineStringBuf(pkg->specialDoc, " $DOCDIR");
1005         }
1006     }
1007
1008     return res;
1009 }
1010
1011 /**
1012  */
1013 static int compareFileListRecs(const void * ap, const void * bp)        /*@*/
1014 {
1015     const char *a = ((FileListRec)ap)->fileURL;
1016     const char *b = ((FileListRec)bp)->fileURL;
1017     return strcmp(a, b);
1018 }
1019
1020 /**
1021  * @param fl            package file tree walk data
1022  */
1023 static int isDoc(FileList fl, const char * fileName)    /*@*/
1024 {
1025     int x = fl->docDirCount;
1026
1027     while (x--) {
1028         if (strstr(fileName, fl->docDirs[x]) == fileName)
1029             return 1;
1030     }
1031     return 0;
1032 }
1033
1034 /**
1035  * Verify that file attributes scope over hardlinks correctly.
1036  * @todo only %lang for now, finish other attributes later.
1037  * @param fl            package file tree walk data
1038  */
1039 static void checkHardLinks(FileList fl)
1040         /*@modifies fl->fileList->flags, fl->fileList->langs @*/
1041 {
1042     char nlangs[BUFSIZ];
1043     FileListRec ilp, jlp;
1044     int i, j;
1045
1046     nlangs[0] = '\0';
1047     for (i = 0;  i < fl->fileListRecsUsed; i++) {
1048         char *te;
1049
1050         ilp = fl->fileList + i;
1051         if (!(S_ISREG(ilp->fl_mode) && ilp->fl_nlink > 1))
1052             continue;
1053         if (ilp->flags & RPMFILE_SPECFILE)
1054             continue;
1055
1056         te = nlangs;
1057         *te = '\0';
1058         for (j = i + 1; j < fl->fileListRecsUsed; j++) {
1059             jlp = fl->fileList + j;
1060             if (!S_ISREG(jlp->fl_mode))
1061                 /*@innercontinue@*/ continue;
1062             if (ilp->fl_nlink != jlp->fl_nlink)
1063                 /*@innercontinue@*/ continue;
1064             if (ilp->fl_ino != jlp->fl_ino)
1065                 /*@innercontinue@*/ continue;
1066             if (ilp->fl_dev != jlp->fl_dev)
1067                 /*@innercontinue@*/ continue;
1068             if (!strcmp(ilp->langs, jlp->langs)) {
1069                 jlp->flags |= RPMFILE_SPECFILE;
1070                 /*@innercontinue@*/ continue;
1071             }
1072             if (te == nlangs)
1073                 te = stpcpy(te, ilp->langs);
1074             *te++ = '|';
1075             te = stpcpy(te, jlp->langs);
1076         }
1077
1078         /* Are locales distributed over hard links correctly? */
1079         if (te == nlangs)
1080             continue;
1081
1082         ilp->langs = _free(ilp->langs);
1083         ilp->langs = xstrdup(nlangs);
1084         for (j = i + 1; j < fl->fileListRecsUsed; j++) {
1085             jlp = fl->fileList + j;
1086             if (!S_ISREG(jlp->fl_mode))
1087                 /*@innercontinue@*/ continue;
1088             if (ilp->fl_nlink != jlp->fl_nlink)
1089                 /*@innercontinue@*/ continue;
1090             if (ilp->fl_ino != jlp->fl_ino)
1091                 /*@innercontinue@*/ continue;
1092             if (ilp->fl_dev != jlp->fl_dev)
1093                 /*@innercontinue@*/ continue;
1094             jlp->flags |= RPMFILE_SPECFILE;
1095             jlp->langs = _free(jlp->langs);
1096             jlp->langs = xstrdup(nlangs);
1097         }
1098     }
1099
1100     for (i = 0;  i < fl->fileListRecsUsed; i++) {
1101         ilp = fl->fileList + i;
1102         ilp->flags &= ~RPMFILE_SPECFILE;
1103     }
1104 }
1105
1106 /**
1107  * @todo Should directories have %doc/%config attributes? (#14531)
1108  * @todo Remove RPMTAG_OLDFILENAMES, add dirname/basename instead.
1109  * @param fl            package file tree walk data
1110  */
1111 static void genCpioListAndHeader(/*@partial@*/ FileList fl,
1112                 TFI_t * cpioList, Header h, int isSrc)
1113         /*@globals rpmGlobalMacroContext,
1114                 fileSystem @*/
1115         /*@modifies h, *cpioList, fl->processingFailed, fl->fileList,
1116                 rpmGlobalMacroContext, fileSystem @*/
1117 {
1118     int _addDotSlash = !(isSrc || rpmExpandNumeric("%{_noPayloadPrefix}"));
1119     uint_32 multiLibMask = 0;
1120     int apathlen = 0;
1121     int dpathlen = 0;
1122     int skipLen = 0;
1123     FileListRec flp;
1124     char buf[BUFSIZ];
1125     int i;
1126     
1127     /* Sort the big list */
1128     qsort(fl->fileList, fl->fileListRecsUsed,
1129           sizeof(*(fl->fileList)), compareFileListRecs);
1130     
1131     /* Generate the header. */
1132     if (! isSrc) {
1133         skipLen = 1;
1134         if (fl->prefix)
1135             skipLen += strlen(fl->prefix);
1136     }
1137
1138     for (i = 0, flp = fl->fileList; i < fl->fileListRecsUsed; i++, flp++) {
1139         char *s;
1140
1141         /* Merge duplicate entries. */
1142         while (i < (fl->fileListRecsUsed - 1) &&
1143             !strcmp(flp->fileURL, flp[1].fileURL)) {
1144
1145             /* Two entries for the same file found, merge the entries. */
1146
1147             rpmMessage(RPMMESS_WARNING, _("File listed twice: %s\n"),
1148                 flp->fileURL);
1149
1150             /* file flags */
1151             flp[1].flags |= flp->flags; 
1152    
1153             /* file mode */
1154             if (S_ISDIR(flp->fl_mode)) {
1155                 if ((flp[1].specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)) <
1156                     (flp->specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)))
1157                         flp[1].fl_mode = flp->fl_mode;
1158             } else {
1159                 if ((flp[1].specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)) <
1160                     (flp->specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)))
1161                         flp[1].fl_mode = flp->fl_mode;
1162             }
1163
1164             /* uid */
1165             if ((flp[1].specdFlags & (SPECD_UID | SPECD_DEFUID)) <
1166                 (flp->specdFlags & (SPECD_UID | SPECD_DEFUID)))
1167             {
1168                 flp[1].fl_uid = flp->fl_uid;
1169                 flp[1].uname = flp->uname;
1170             }
1171
1172             /* gid */
1173             if ((flp[1].specdFlags & (SPECD_GID | SPECD_DEFGID)) <
1174                 (flp->specdFlags & (SPECD_GID | SPECD_DEFGID)))
1175             {
1176                 flp[1].fl_gid = flp->fl_gid;
1177                 flp[1].gname = flp->gname;
1178             }
1179
1180             /* verify flags */
1181             if ((flp[1].specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)) <
1182                 (flp->specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)))
1183                     flp[1].verifyFlags = flp->verifyFlags;
1184
1185             /* XXX to-do: language */
1186
1187             flp++; i++;
1188         }
1189
1190         /* Skip files that were marked with %exclude. */
1191         if (flp->flags & RPMFILE_EXCLUDE) continue;
1192
1193         /* Omit '/' and/or URL prefix, leave room for "./" prefix */
1194         apathlen += (strlen(flp->fileURL) - skipLen + (_addDotSlash ? 3 : 1));
1195
1196         /* Leave room for both dirname and basename NUL's */
1197         dpathlen += (strlen(flp->diskURL) + 2);
1198
1199         if (flp->flags & RPMFILE_MULTILIB_MASK)
1200             multiLibMask |=
1201                 (1u << ((flp->flags & RPMFILE_MULTILIB_MASK))
1202                       >> RPMFILE_MULTILIB_SHIFT);
1203
1204         /*
1205          * Make the header, the OLDFILENAMES will get converted to a 
1206          * compressed file list write before we write the actual package to
1207          * disk.
1208          */
1209         (void) headerAddOrAppendEntry(h, RPMTAG_OLDFILENAMES, RPM_STRING_ARRAY_TYPE,
1210                                &(flp->fileURL), 1);
1211
1212 /*@-sizeoftype@*/
1213       if (sizeof(flp->fl_size) != sizeof(uint_32)) {
1214         uint_32 psize = (uint_32)flp->fl_size;
1215         (void) headerAddOrAppendEntry(h, RPMTAG_FILESIZES, RPM_INT32_TYPE,
1216                                &(psize), 1);
1217       } else {
1218         (void) headerAddOrAppendEntry(h, RPMTAG_FILESIZES, RPM_INT32_TYPE,
1219                                &(flp->fl_size), 1);
1220       }
1221         (void) headerAddOrAppendEntry(h, RPMTAG_FILEUSERNAME, RPM_STRING_ARRAY_TYPE,
1222                                &(flp->uname), 1);
1223         (void) headerAddOrAppendEntry(h, RPMTAG_FILEGROUPNAME, RPM_STRING_ARRAY_TYPE,
1224                                &(flp->gname), 1);
1225         (void) headerAddOrAppendEntry(h, RPMTAG_FILEMTIMES, RPM_INT32_TYPE,
1226                                &(flp->fl_mtime), 1);
1227       if (sizeof(flp->fl_mode) != sizeof(uint_16)) {
1228         uint_16 pmode = (uint_16)flp->fl_mode;
1229         (void) headerAddOrAppendEntry(h, RPMTAG_FILEMODES, RPM_INT16_TYPE,
1230                                &(pmode), 1);
1231       } else {
1232         (void) headerAddOrAppendEntry(h, RPMTAG_FILEMODES, RPM_INT16_TYPE,
1233                                &(flp->fl_mode), 1);
1234       }
1235       if (sizeof(flp->fl_rdev) != sizeof(uint_16)) {
1236         uint_16 prdev = (uint_16)flp->fl_rdev;
1237         (void) headerAddOrAppendEntry(h, RPMTAG_FILERDEVS, RPM_INT16_TYPE,
1238                                &(prdev), 1);
1239       } else {
1240         (void) headerAddOrAppendEntry(h, RPMTAG_FILERDEVS, RPM_INT16_TYPE,
1241                                &(flp->fl_rdev), 1);
1242       }
1243       if (sizeof(flp->fl_dev) != sizeof(uint_32)) {
1244         uint_32 pdevice = (uint_32)flp->fl_dev;
1245         (void) headerAddOrAppendEntry(h, RPMTAG_FILEDEVICES, RPM_INT32_TYPE,
1246                                &(pdevice), 1);
1247       } else {
1248         (void) headerAddOrAppendEntry(h, RPMTAG_FILEDEVICES, RPM_INT32_TYPE,
1249                                &(flp->fl_dev), 1);
1250       }
1251 /*@=sizeoftype@*/
1252         (void) headerAddOrAppendEntry(h, RPMTAG_FILEINODES, RPM_INT32_TYPE,
1253                                &(flp->fl_ino), 1);
1254
1255         (void) headerAddOrAppendEntry(h, RPMTAG_FILELANGS, RPM_STRING_ARRAY_TYPE,
1256                                &(flp->langs),  1);
1257         
1258         /* We used to add these, but they should not be needed */
1259         /* (void) headerAddOrAppendEntry(h, RPMTAG_FILEUIDS,
1260          *                 RPM_INT32_TYPE, &(flp->fl_uid), 1);
1261          * (void) headerAddOrAppendEntry(h, RPMTAG_FILEGIDS,
1262          *                 RPM_INT32_TYPE, &(flp->fl_gid), 1);
1263          */
1264         
1265         buf[0] = '\0';
1266         if (S_ISREG(flp->fl_mode))
1267             (void) mdfile(flp->diskURL, buf);
1268         s = buf;
1269         (void) headerAddOrAppendEntry(h, RPMTAG_FILEMD5S, RPM_STRING_ARRAY_TYPE,
1270                                &s, 1);
1271         
1272         buf[0] = '\0';
1273         if (S_ISLNK(flp->fl_mode)) {
1274             buf[Readlink(flp->diskURL, buf, BUFSIZ)] = '\0';
1275             if (fl->buildRootURL) {
1276                 const char * buildRoot;
1277                 (void) urlPath(fl->buildRootURL, &buildRoot);
1278
1279                 if (buf[0] == '/' && strcmp(buildRoot, "/") &&
1280                     !strncmp(buf, buildRoot, strlen(buildRoot))) {
1281                      rpmError(RPMERR_BADSPEC,
1282                                 _("Symlink points to BuildRoot: %s -> %s\n"),
1283                                 flp->fileURL, buf);
1284                     fl->processingFailed = 1;
1285                 }
1286             }
1287         }
1288         s = buf;
1289         (void) headerAddOrAppendEntry(h, RPMTAG_FILELINKTOS, RPM_STRING_ARRAY_TYPE,
1290                                &s, 1);
1291         
1292         if (flp->flags & RPMFILE_GHOST) {
1293             flp->verifyFlags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE |
1294                                 RPMVERIFY_LINKTO | RPMVERIFY_MTIME);
1295         }
1296         (void) headerAddOrAppendEntry(h, RPMTAG_FILEVERIFYFLAGS, RPM_INT32_TYPE,
1297                                &(flp->verifyFlags), 1);
1298         
1299         if (!isSrc && isDoc(fl, flp->fileURL))
1300             flp->flags |= RPMFILE_DOC;
1301         /* XXX Should directories have %doc/%config attributes? (#14531) */
1302         if (S_ISDIR(flp->fl_mode))
1303             flp->flags &= ~(RPMFILE_CONFIG|RPMFILE_DOC);
1304
1305         (void) headerAddOrAppendEntry(h, RPMTAG_FILEFLAGS, RPM_INT32_TYPE,
1306                                &(flp->flags), 1);
1307
1308     }
1309     (void) headerAddEntry(h, RPMTAG_SIZE, RPM_INT32_TYPE,
1310                    &(fl->totalFileSize), 1);
1311
1312     /* XXX This should be added always so that packages look alike.
1313      * XXX However, there is logic in files.c/depends.c that checks for
1314      * XXX existence (rather than value) that will need to change as well.
1315      */
1316     if (multiLibMask)
1317         (void) headerAddEntry(h, RPMTAG_MULTILIBS, RPM_INT32_TYPE,
1318                        &multiLibMask, 1);
1319
1320     if (_addDotSlash)
1321         (void) rpmlibNeedsFeature(h, "PayloadFilesHavePrefix", "4.0-1");
1322
1323     /* Choose how filenames are represented. */
1324     if (_noDirTokens)
1325         expandFilelist(h);
1326     else {
1327         compressFilelist(h);
1328         /* Binary packages with dirNames cannot be installed by legacy rpm. */
1329         (void) rpmlibNeedsFeature(h, "CompressedFileNames", "3.0.4-1");
1330     }
1331
1332   { TFI_t fi = xcalloc(sizeof(*fi), 1);
1333     char * a, * d;
1334
1335     fi->type = TR_ADDED;
1336     loadFi(h, fi);
1337     fi->dnl = _free(fi->dnl);
1338     fi->bnl = _free(fi->bnl);
1339
1340     fi->dnl = xmalloc(fi->fc * sizeof(*fi->dnl) + dpathlen);
1341     d = (char *)(fi->dnl + fi->fc);
1342     *d = '\0';
1343
1344     fi->bnl = xmalloc(fi->fc * (sizeof(*fi->bnl) + sizeof(*fi->dil)));
1345     fi->dil = (int *)(fi->bnl + fi->fc);
1346
1347     fi->apath = xmalloc(fi->fc * sizeof(*fi->apath) + apathlen);
1348     a = (char *)(fi->apath + fi->fc);
1349     *a = '\0';
1350
1351     fi->actions = xcalloc(sizeof(*fi->actions), fi->fc);
1352     fi->fmapflags = xcalloc(sizeof(*fi->fmapflags), fi->fc);
1353     fi->astriplen = 0;
1354     if (fl->buildRootURL)
1355         fi->astriplen = strlen(fl->buildRootURL);
1356     fi->striplen = 0;
1357     fi->fuser = NULL;
1358     fi->fuids = xcalloc(sizeof(*fi->fuids), fi->fc);
1359     fi->fgroup = NULL;
1360     fi->fgids = xcalloc(sizeof(*fi->fgids), fi->fc);
1361
1362     /* Make the cpio list */
1363     for (i = 0, flp = fl->fileList; i < fi->fc; i++, flp++) {
1364         char * b;
1365
1366         /* Skip (possible) duplicate file entries, use last entry info. */
1367         while (((flp - fl->fileList) < (fl->fileListRecsUsed - 1)) &&
1368                 !strcmp(flp->fileURL, flp[1].fileURL))
1369             flp++;
1370
1371         /* Create disk directory and base name. */
1372         fi->dil[i] = i;
1373         fi->dnl[fi->dil[i]] = d;
1374         d = stpcpy(d, flp->diskURL);
1375
1376         /* Make room for the dirName NUL, find start of baseName. */
1377         for (b = d; b > fi->dnl[fi->dil[i]] && *b != '/'; b--)
1378             b[1] = b[0];
1379         b++;            /* dirname's end in '/' */
1380         *b++ = '\0';    /* terminate dirname, b points to basename */
1381         fi->bnl[i] = b;
1382         d += 2;         /* skip both dirname and basename NUL's */
1383
1384         /* Create archive path, normally adding "./" */
1385         /*@-dependenttrans@*/   /* FIX: xstrdup? nah ... */
1386         fi->apath[i] = a;
1387         /*@=dependenttrans@*/
1388         if (_addDotSlash) a = stpcpy(a, "./");
1389         a = stpcpy(a, (flp->fileURL + skipLen));
1390         a++;            /* skip apath NUL */
1391
1392         if (flp->flags & RPMFILE_GHOST) {
1393             fi->actions[i] = FA_SKIP;
1394             continue;
1395         }
1396         fi->actions[i] = FA_COPYOUT;
1397         fi->fuids[i] = getUidS(flp->uname);
1398         fi->fgids[i] = getGidS(flp->gname);
1399         if (fi->fuids[i] == (uid_t)-1) fi->fuids[i] = 0;
1400         if (fi->fgids[i] == (gid_t)-1) fi->fgids[i] = 0;
1401         fi->fmapflags[i] = CPIO_MAP_PATH |
1402                 CPIO_MAP_TYPE | CPIO_MAP_MODE | CPIO_MAP_UID | CPIO_MAP_GID;
1403         if (isSrc)
1404             fi->fmapflags[i] |= CPIO_FOLLOW_SYMLINKS;
1405         if (flp->flags & RPMFILE_MULTILIB_MASK)
1406             fi->fmapflags[i] |= CPIO_MULTILIB;
1407
1408     }
1409     /*@-branchstate@*/
1410     if (cpioList)
1411         *cpioList = fi;
1412     else
1413         fi = _free(fi);
1414     /*@=branchstate@*/
1415   }
1416 }
1417
1418 /**
1419  */
1420 static /*@null@*/ FileListRec freeFileList(/*@only@*/ FileListRec fileList,
1421                         int count)
1422         /*@*/
1423 {
1424     while (count--) {
1425         fileList[count].diskURL = _free(fileList[count].diskURL);
1426         fileList[count].fileURL = _free(fileList[count].fileURL);
1427         fileList[count].langs = _free(fileList[count].langs);
1428     }
1429     fileList = _free(fileList);
1430     return NULL;
1431 }
1432
1433 /**
1434  * @param fl            package file tree walk data
1435  */
1436 static int addFile(FileList fl, const char * diskURL, struct stat * statp)
1437         /*@globals rpmGlobalMacroContext,
1438                 fileSystem@*/
1439         /*@modifies *statp, *fl, fl->processingFailed,
1440                 fl->fileList, fl->fileListRecsAlloced, fl->fileListRecsUsed,
1441                 fl->totalFileSize, fl->fileCount, fl->inFtw, fl->isDir,
1442                 rpmGlobalMacroContext, fileSystem @*/
1443 {
1444     const char *fileURL = diskURL;
1445     struct stat statbuf;
1446     mode_t fileMode;
1447     uid_t fileUid;
1448     gid_t fileGid;
1449     const char *fileUname;
1450     const char *fileGname;
1451     char *lang;
1452     
1453     /* Path may have prepended buildRootURL, so locate the original filename. */
1454     /*
1455      * XXX There are 3 types of entry into addFile:
1456      *
1457      *  From                    diskUrl                 statp
1458      *  =====================================================
1459      *  processBinaryFile       path                    NULL
1460      *  processBinaryFile       glob result path        NULL
1461      *  myftw                   path                    stat
1462      *
1463      */
1464     {   const char *fileName;
1465         (void) urlPath(fileURL, &fileName);
1466         if (fl->buildRootURL && strcmp(fl->buildRootURL, "/"))
1467             fileURL += strlen(fl->buildRootURL);
1468     }
1469
1470     /* XXX make sure '/' can be packaged also */
1471     /*@-branchstate@*/
1472     if (*fileURL == '\0')
1473         fileURL = "/";
1474     /*@=branchstate@*/
1475
1476     /* If we are using a prefix, validate the file */
1477     if (!fl->inFtw && fl->prefix) {
1478         const char *prefixTest;
1479         const char *prefixPtr = fl->prefix;
1480
1481         (void) urlPath(fileURL, &prefixTest);
1482         while (*prefixPtr && *prefixTest && (*prefixTest == *prefixPtr)) {
1483             prefixPtr++;
1484             prefixTest++;
1485         }
1486         if (*prefixPtr || (*prefixTest && *prefixTest != '/')) {
1487             rpmError(RPMERR_BADSPEC, _("File doesn't match prefix (%s): %s\n"),
1488                      fl->prefix, fileURL);
1489             fl->processingFailed = 1;
1490             return RPMERR_BADSPEC;
1491         }
1492     }
1493
1494     if (statp == NULL) {
1495         statp = &statbuf;
1496         memset(statp, 0, sizeof(*statp));
1497         if (fl->devtype) {
1498             time_t now = time(NULL);
1499
1500             /* XXX hack up a stat structure for a %dev(...) directive. */
1501             statp->st_nlink = 1;
1502             statp->st_rdev =
1503                 ((fl->devmajor & 0xff) << 8) | (fl->devminor & 0xff);
1504             statp->st_dev = statp->st_rdev;
1505             statp->st_mode = (fl->devtype == 'b' ? S_IFBLK : S_IFCHR);
1506             statp->st_mode |= (fl->cur_ar.ar_fmode & 0777);
1507             statp->st_atime = now;
1508             statp->st_mtime = now;
1509             statp->st_ctime = now;
1510         } else if (Lstat(diskURL, statp)) {
1511             rpmError(RPMERR_BADSPEC, _("File not found: %s\n"), diskURL);
1512             fl->processingFailed = 1;
1513             return RPMERR_BADSPEC;
1514         }
1515     }
1516
1517     if ((! fl->isDir) && S_ISDIR(statp->st_mode)) {
1518         /* We use our own ftw() call, because ftw() uses stat()    */
1519         /* instead of lstat(), which causes it to follow symlinks! */
1520         /* It also has better callback support.                    */
1521         
1522         fl->inFtw = 1;  /* Flag to indicate file has buildRootURL prefixed */
1523         fl->isDir = 1;  /* Keep it from following myftw() again         */
1524         (void) myftw(diskURL, 16, (myftwFunc) addFile, fl);
1525         fl->isDir = 0;
1526         fl->inFtw = 0;
1527         return 0;
1528     }
1529
1530     fileMode = statp->st_mode;
1531     fileUid = statp->st_uid;
1532     fileGid = statp->st_gid;
1533
1534     if (S_ISDIR(fileMode) && fl->cur_ar.ar_dmodestr) {
1535         fileMode &= S_IFMT;
1536         fileMode |= fl->cur_ar.ar_dmode;
1537     } else if (fl->cur_ar.ar_fmodestr != NULL) {
1538         fileMode &= S_IFMT;
1539         fileMode |= fl->cur_ar.ar_fmode;
1540     }
1541     if (fl->cur_ar.ar_user) {
1542         fileUname = getUnameS(fl->cur_ar.ar_user);
1543     } else {
1544         fileUname = getUname(fileUid);
1545     }
1546     if (fl->cur_ar.ar_group) {
1547         fileGname = getGnameS(fl->cur_ar.ar_group);
1548     } else {
1549         fileGname = getGname(fileGid);
1550     }
1551         
1552 #if 0   /* XXX this looks dumb to me */
1553     if (! (fileUname && fileGname)) {
1554         rpmError(RPMERR_BADSPEC, _("Bad owner/group: %s\n"), diskName);
1555         fl->processingFailed = 1;
1556         return RPMERR_BADSPEC;
1557     }
1558 #else
1559     /* Default user/group to builder's user/group */
1560     if (fileUname == NULL)
1561         fileUname = getUname(getuid());
1562     if (fileGname == NULL)
1563         fileGname = getGname(getgid());
1564 #endif
1565     
1566     rpmMessage(RPMMESS_DEBUG, _("File %4d: %07o %s.%s\t %s\n"), fl->fileCount,
1567         (unsigned)fileMode, fileUname, fileGname, fileURL);
1568
1569     /* Add to the file list */
1570     if (fl->fileListRecsUsed == fl->fileListRecsAlloced) {
1571         fl->fileListRecsAlloced += 128;
1572         fl->fileList = xrealloc(fl->fileList,
1573                         fl->fileListRecsAlloced * sizeof(*(fl->fileList)));
1574     }
1575             
1576     {   FileListRec flp = &fl->fileList[fl->fileListRecsUsed];
1577         int i;
1578
1579         flp->fl_st = *statp;    /* structure assignment */
1580         flp->fl_mode = fileMode;
1581         flp->fl_uid = fileUid;
1582         flp->fl_gid = fileGid;
1583
1584         flp->fileURL = xstrdup(fileURL);
1585         flp->diskURL = xstrdup(diskURL);
1586         flp->uname = fileUname;
1587         flp->gname = fileGname;
1588
1589         if (fl->currentLangs && fl->nLangs > 0) {
1590             char * ncl;
1591             size_t nl = 0;
1592             
1593             for (i = 0; i < fl->nLangs; i++)
1594                 nl += strlen(fl->currentLangs[i]) + 1;
1595
1596             flp->langs = ncl = xmalloc(nl);
1597             for (i = 0; i < fl->nLangs; i++) {
1598                 const char *ocl;
1599                 if (i)  *ncl++ = '|';
1600                 for (ocl = fl->currentLangs[i]; *ocl != '\0'; ocl++)
1601                         *ncl++ = *ocl;
1602                 *ncl = '\0';
1603             }
1604         } else if (! parseForRegexLang(fileURL, &lang)) {
1605             flp->langs = xstrdup(lang);
1606         } else {
1607             flp->langs = xstrdup("");
1608         }
1609
1610         flp->flags = fl->currentFlags;
1611         flp->specdFlags = fl->currentSpecdFlags;
1612         flp->verifyFlags = fl->currentVerifyFlags;
1613
1614         if (multiLib
1615             && !(flp->flags & RPMFILE_MULTILIB_MASK)
1616             && !parseForRegexMultiLib(fileURL))
1617             flp->flags |= multiLib;
1618
1619
1620         /* Hard links need be counted only once. */
1621         if (S_ISREG(flp->fl_mode) && flp->fl_nlink > 1) {
1622             FileListRec ilp;
1623             for (i = 0;  i < fl->fileListRecsUsed; i++) {
1624                 ilp = fl->fileList + i;
1625                 if (!S_ISREG(ilp->fl_mode))
1626                     continue;
1627                 if (flp->fl_nlink != ilp->fl_nlink)
1628                     continue;
1629                 if (flp->fl_ino != ilp->fl_ino)
1630                     continue;
1631                 if (flp->fl_dev != ilp->fl_dev)
1632                     continue;
1633                 break;
1634             }
1635         } else
1636             i = fl->fileListRecsUsed;
1637
1638         if (S_ISREG(flp->fl_mode) && i >= fl->fileListRecsUsed)
1639             fl->totalFileSize += flp->fl_size;
1640     }
1641
1642     fl->fileListRecsUsed++;
1643     fl->fileCount++;
1644
1645     return 0;
1646 }
1647
1648 /**
1649  * @param fl            package file tree walk data
1650  */
1651 static int processBinaryFile(/*@unused@*/ Package pkg, FileList fl,
1652                 const char * fileURL)
1653         /*@globals rpmGlobalMacroContext,
1654                 fileSystem@*/
1655         /*@modifies *fl, fl->processingFailed,
1656                 fl->fileList, fl->fileListRecsAlloced, fl->fileListRecsUsed,
1657                 fl->totalFileSize, fl->fileCount, fl->inFtw, fl->isDir,
1658                 rpmGlobalMacroContext, fileSystem @*/
1659 {
1660     int doGlob;
1661     const char *diskURL = NULL;
1662     int rc = 0;
1663     
1664     doGlob = myGlobPatternP(fileURL);
1665
1666     /* Check that file starts with leading "/" */
1667     {   const char * fileName;
1668         (void) urlPath(fileURL, &fileName);
1669         if (*fileName != '/') {
1670             rpmError(RPMERR_BADSPEC, _("File needs leading \"/\": %s\n"),
1671                         fileName);
1672             rc = 1;
1673             goto exit;
1674         }
1675     }
1676     
1677     /* Copy file name or glob pattern removing multiple "/" chars. */
1678     /*
1679      * Note: rpmGetPath should guarantee a "canonical" path. That means
1680      * that the following pathologies should be weeded out:
1681      *          //bin//sh
1682      *          //usr//bin/
1683      *          /.././../usr/../bin//./sh
1684      */
1685     diskURL = rpmGenPath(fl->buildRootURL, NULL, fileURL);
1686
1687     if (doGlob) {
1688         const char ** argv = NULL;
1689         int argc = 0;
1690         int i;
1691
1692         if (fl->noGlob) {
1693             rpmError(RPMERR_BADSPEC, _("Glob not permitted: %s\n"),
1694                         diskURL);
1695             rc = 1;
1696             goto exit;
1697         }
1698
1699         /*@-branchstate@*/
1700         rc = rpmGlob(diskURL, &argc, &argv);
1701         if (rc == 0 && argc >= 1 && !myGlobPatternP(argv[0])) {
1702             for (i = 0; i < argc; i++) {
1703                 rc = addFile(fl, argv[i], NULL);
1704                 argv[i] = _free(argv[i]);
1705             }
1706             argv = _free(argv);
1707         } else {
1708             rpmError(RPMERR_BADSPEC, _("File not found by glob: %s\n"),
1709                         diskURL);
1710             rc = 1;
1711         }
1712         /*@=branchstate@*/
1713     } else {
1714         rc = addFile(fl, diskURL, NULL);
1715     }
1716
1717 exit:
1718     diskURL = _free(diskURL);
1719     if (rc)
1720         fl->processingFailed = 1;
1721     return rc;
1722 }
1723
1724 /**
1725  */
1726 static int processPackageFiles(Spec spec, Package pkg,
1727                                int installSpecialDoc, int test)
1728         /*@globals rpmGlobalMacroContext,
1729                 fileSystem, internalState@*/
1730         /*@modifies spec->macros,
1731                 pkg->cpioList, pkg->fileList, pkg->specialDoc, pkg->header,
1732                 rpmGlobalMacroContext, fileSystem, internalState @*/
1733 {
1734     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
1735     struct FileList_s fl;
1736     char *s, **files, **fp;
1737     const char *fileName;
1738     char buf[BUFSIZ];
1739     struct AttrRec_s arbuf;
1740     AttrRec specialDocAttrRec = &arbuf;
1741     char *specialDoc = NULL;
1742
1743 #ifdef MULTILIB
1744     multiLib = rpmExpandNumeric("%{_multilibno}");
1745     if (multiLib)
1746         multiLib = RPMFILE_MULTILIB(multiLib);
1747 #endif /* MULTILIB */
1748     
1749     nullAttrRec(specialDocAttrRec);
1750     pkg->cpioList = NULL;
1751
1752     if (pkg->fileFile) {
1753         const char *ffn;
1754         FILE * f;
1755         FD_t fd;
1756
1757         /* XXX W2DO? urlPath might be useful here. */
1758         if (*pkg->fileFile == '/') {
1759             ffn = rpmGetPath(pkg->fileFile, NULL);
1760         } else {
1761             /* XXX FIXME: add %{_buildsubdir} */
1762             ffn = rpmGetPath("%{_builddir}/",
1763                 (spec->buildSubdir ? spec->buildSubdir : "") ,
1764                 "/", pkg->fileFile, NULL);
1765         }
1766         fd = Fopen(ffn, "r.fpio");
1767
1768         if (fd == NULL || Ferror(fd)) {
1769             rpmError(RPMERR_BADFILENAME,
1770                 _("Could not open %%files file %s: %s\n"),
1771                 ffn, Fstrerror(fd));
1772             return RPMERR_BADFILENAME;
1773         }
1774         ffn = _free(ffn);
1775
1776         /*@+voidabstract@*/ f = fdGetFp(fd); /*@=voidabstract@*/
1777         if (f != NULL)
1778         while (fgets(buf, sizeof(buf), f)) {
1779             handleComments(buf);
1780             if (expandMacros(spec, spec->macros, buf, sizeof(buf))) {
1781                 rpmError(RPMERR_BADSPEC, _("line: %s\n"), buf);
1782                 return RPMERR_BADSPEC;
1783             }
1784             appendStringBuf(pkg->fileList, buf);
1785         }
1786         (void) Fclose(fd);
1787     }
1788     
1789     /* Init the file list structure */
1790     memset(&fl, 0, sizeof(fl));
1791
1792     /* XXX spec->buildRootURL == NULL, then xstrdup("") is returned */
1793     fl.buildRootURL = rpmGenPath(spec->rootURL, spec->buildRootURL, NULL);
1794
1795     if (hge(pkg->header, RPMTAG_DEFAULTPREFIX, NULL, (void **)&fl.prefix, NULL))
1796         fl.prefix = xstrdup(fl.prefix);
1797     else
1798         fl.prefix = NULL;
1799
1800     fl.fileCount = 0;
1801     fl.totalFileSize = 0;
1802     fl.processingFailed = 0;
1803
1804     fl.passedSpecialDoc = 0;
1805     fl.isSpecialDoc = 0;
1806
1807     fl.isDir = 0;
1808     fl.inFtw = 0;
1809     fl.currentFlags = 0;
1810     fl.currentVerifyFlags = 0;
1811     
1812     fl.noGlob = 0;
1813     fl.devtype = 0;
1814     fl.devmajor = 0;
1815     fl.devminor = 0;
1816
1817     nullAttrRec(&fl.cur_ar);
1818     nullAttrRec(&fl.def_ar);
1819
1820     fl.defVerifyFlags = RPMVERIFY_ALL;
1821     fl.nLangs = 0;
1822     fl.currentLangs = NULL;
1823
1824     fl.currentSpecdFlags = 0;
1825     fl.defSpecdFlags = 0;
1826
1827     fl.docDirCount = 0;
1828     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/doc");
1829     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/man");
1830     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/info");
1831     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/X11R6/man");
1832     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/share/doc");
1833     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/share/man");
1834     fl.docDirs[fl.docDirCount++] = xstrdup("/usr/share/info");
1835     fl.docDirs[fl.docDirCount++] = rpmGetPath("%{_docdir}", NULL);
1836     fl.docDirs[fl.docDirCount++] = rpmGetPath("%{_mandir}", NULL);
1837     fl.docDirs[fl.docDirCount++] = rpmGetPath("%{_infodir}", NULL);
1838     
1839     fl.fileList = NULL;
1840     fl.fileListRecsAlloced = 0;
1841     fl.fileListRecsUsed = 0;
1842
1843     s = getStringBuf(pkg->fileList);
1844     files = splitString(s, strlen(s), '\n');
1845
1846     for (fp = files; *fp != NULL; fp++) {
1847         s = *fp;
1848         SKIPSPACE(s);
1849         if (*s == '\0')
1850             continue;
1851         fileName = NULL;
1852         /*@-nullpass@*/ /* LCL: buf is NULL ?!? */
1853         strcpy(buf, s);
1854         /*@=nullpass@*/
1855         
1856         /* Reset for a new line in %files */
1857         fl.isDir = 0;
1858         fl.inFtw = 0;
1859         fl.currentFlags = 0;
1860         /* turn explicit flags into %def'd ones (gosh this is hacky...) */
1861         fl.currentSpecdFlags = ((unsigned)fl.defSpecdFlags) >> 8;
1862         fl.currentVerifyFlags = fl.defVerifyFlags;
1863         fl.isSpecialDoc = 0;
1864
1865         fl.noGlob = 0;
1866         fl.devtype = 0;
1867         fl.devmajor = 0;
1868         fl.devminor = 0;
1869
1870         /* XXX should reset to %deflang value */
1871         if (fl.currentLangs) {
1872             int i;
1873             for (i = 0; i < fl.nLangs; i++)
1874                 /*@-unqualifiedtrans@*/
1875                 fl.currentLangs[i] = _free(fl.currentLangs[i]);
1876                 /*@=unqualifiedtrans@*/
1877             fl.currentLangs = _free(fl.currentLangs);
1878         }
1879         fl.nLangs = 0;
1880
1881         dupAttrRec(&fl.def_ar, &fl.cur_ar);
1882
1883         /*@-nullpass@*/ /* LCL: buf is NULL ?!? */
1884         if (parseForVerify(buf, &fl))
1885             continue;
1886         if (parseForAttr(buf, &fl))
1887             continue;
1888         if (parseForDev(buf, &fl))
1889             continue;
1890         if (parseForConfig(buf, &fl))
1891             continue;
1892         if (parseForLang(buf, &fl))
1893             continue;
1894         /*@-nullstate@*/        /* FIX: pkg->fileFile might be NULL */
1895         if (parseForSimple(spec, pkg, buf, &fl, &fileName))
1896         /*@=nullstate@*/
1897             continue;
1898         /*@=nullpass@*/
1899         if (fileName == NULL)
1900             continue;
1901
1902         /*@-branchstate@*/
1903         if (fl.isSpecialDoc) {
1904             /* Save this stuff for last */
1905             specialDoc = _free(specialDoc);
1906             specialDoc = xstrdup(fileName);
1907             dupAttrRec(&fl.cur_ar, specialDocAttrRec);
1908         } else {
1909             /*@-nullstate@*/    /* FIX: pkg->fileFile might be NULL */
1910             (void) processBinaryFile(pkg, &fl, fileName);
1911             /*@=nullstate@*/
1912         }
1913         /*@=branchstate@*/
1914     }
1915
1916     /* Now process special doc, if there is one */
1917     if (specialDoc) {
1918         if (installSpecialDoc) {
1919             (void) doScript(spec, RPMBUILD_STRINGBUF, "%doc", pkg->specialDoc, test);
1920         }
1921
1922         /* Reset for %doc */
1923         fl.isDir = 0;
1924         fl.inFtw = 0;
1925         fl.currentFlags = 0;
1926         fl.currentVerifyFlags = 0;
1927
1928         fl.noGlob = 0;
1929         fl.devtype = 0;
1930         fl.devmajor = 0;
1931         fl.devminor = 0;
1932
1933         /* XXX should reset to %deflang value */
1934         if (fl.currentLangs) {
1935             int i;
1936             for (i = 0; i < fl.nLangs; i++)
1937                 /*@-unqualifiedtrans@*/
1938                 fl.currentLangs[i] = _free(fl.currentLangs[i]);
1939                 /*@=unqualifiedtrans@*/
1940             fl.currentLangs = _free(fl.currentLangs);
1941         }
1942         fl.nLangs = 0;
1943
1944         dupAttrRec(specialDocAttrRec, &fl.cur_ar);
1945         freeAttrRec(specialDocAttrRec);
1946
1947         /*@-nullstate@*/        /* FIX: pkg->fileFile might be NULL */
1948         (void) processBinaryFile(pkg, &fl, specialDoc);
1949         /*@=nullstate@*/
1950
1951         specialDoc = _free(specialDoc);
1952     }
1953     
1954     freeSplitString(files);
1955
1956     if (fl.processingFailed)
1957         goto exit;
1958
1959     /* Verify that file attributes scope over hardlinks correctly. */
1960     checkHardLinks(&fl);
1961
1962     genCpioListAndHeader(&fl, (TFI_t *)&pkg->cpioList, pkg->header, 0);
1963
1964     if (spec->timeCheck)
1965         timeCheck(spec->timeCheck, pkg->header);
1966     
1967 exit:
1968     fl.buildRootURL = _free(fl.buildRootURL);
1969     fl.prefix = _free(fl.prefix);
1970
1971     freeAttrRec(&fl.cur_ar);
1972     freeAttrRec(&fl.def_ar);
1973
1974     if (fl.currentLangs) {
1975         int i;
1976         for (i = 0; i < fl.nLangs; i++)
1977             /*@-unqualifiedtrans@*/
1978             fl.currentLangs[i] = _free(fl.currentLangs[i]);
1979             /*@=unqualifiedtrans@*/
1980         fl.currentLangs = _free(fl.currentLangs);
1981     }
1982
1983     fl.fileList = freeFileList(fl.fileList, fl.fileListRecsUsed);
1984     while (fl.docDirCount--)
1985         fl.docDirs[fl.docDirCount] = _free(fl.docDirs[fl.docDirCount]);
1986     return fl.processingFailed;
1987 }
1988
1989 void initSourceHeader(Spec spec)
1990 {
1991     HeaderIterator hi;
1992     int_32 tag, type, count;
1993     const void * ptr;
1994
1995     spec->sourceHeader = headerNew();
1996     /* Only specific tags are added to the source package header */
1997     /*@-branchstate@*/
1998     for (hi = headerInitIterator(spec->packages->header);
1999         headerNextIterator(hi, &tag, &type, &ptr, &count);
2000         ptr = headerFreeData(ptr, type))
2001     {
2002         switch (tag) {
2003         case RPMTAG_NAME:
2004         case RPMTAG_VERSION:
2005         case RPMTAG_RELEASE:
2006         case RPMTAG_EPOCH:
2007         case RPMTAG_SUMMARY:
2008         case RPMTAG_DESCRIPTION:
2009         case RPMTAG_PACKAGER:
2010         case RPMTAG_DISTRIBUTION:
2011         case RPMTAG_DISTURL:
2012         case RPMTAG_VENDOR:
2013         case RPMTAG_LICENSE:
2014         case RPMTAG_GROUP:
2015         case RPMTAG_OS:
2016         case RPMTAG_ARCH:
2017         case RPMTAG_CHANGELOGTIME:
2018         case RPMTAG_CHANGELOGNAME:
2019         case RPMTAG_CHANGELOGTEXT:
2020         case RPMTAG_URL:
2021         case HEADER_I18NTABLE:
2022             if (ptr)
2023                 (void)headerAddEntry(spec->sourceHeader, tag, type, ptr, count);
2024             /*@switchbreak@*/ break;
2025         default:
2026             /* do not copy */
2027             /*@switchbreak@*/ break;
2028         }
2029     }
2030     hi = headerFreeIterator(hi);
2031     /*@=branchstate@*/
2032
2033     /* Add the build restrictions */
2034     /*@-branchstate@*/
2035     for (hi = headerInitIterator(spec->buildRestrictions);
2036         headerNextIterator(hi, &tag, &type, &ptr, &count);
2037         ptr = headerFreeData(ptr, type))
2038     {
2039         if (ptr)
2040             (void) headerAddEntry(spec->sourceHeader, tag, type, ptr, count);
2041     }
2042     hi = headerFreeIterator(hi);
2043     /*@=branchstate@*/
2044
2045     if (spec->BANames && spec->BACount > 0) {
2046         (void) headerAddEntry(spec->sourceHeader, RPMTAG_BUILDARCHS,
2047                        RPM_STRING_ARRAY_TYPE,
2048                        spec->BANames, spec->BACount);
2049     }
2050 }
2051
2052 int processSourceFiles(Spec spec)
2053 {
2054     struct Source *srcPtr;
2055     StringBuf sourceFiles;
2056     int x, isSpec = 1;
2057     struct FileList_s fl;
2058     char *s, **files, **fp;
2059     Package pkg;
2060
2061     sourceFiles = newStringBuf();
2062
2063     /* XXX
2064      * XXX This is where the source header for noarch packages needs
2065      * XXX to be initialized.
2066      */
2067     if (spec->sourceHeader == NULL)
2068         initSourceHeader(spec);
2069
2070     /* Construct the file list and source entries */
2071     appendLineStringBuf(sourceFiles, spec->specFile);
2072     if (spec->sourceHeader != NULL)
2073     for (srcPtr = spec->sources; srcPtr != NULL; srcPtr = srcPtr->next) {
2074         if (srcPtr->flags & RPMBUILD_ISSOURCE) {
2075             (void) headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_SOURCE,
2076                                    RPM_STRING_ARRAY_TYPE, &srcPtr->source, 1);
2077             if (srcPtr->flags & RPMBUILD_ISNO) {
2078                 (void) headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_NOSOURCE,
2079                                        RPM_INT32_TYPE, &srcPtr->num, 1);
2080             }
2081         }
2082         if (srcPtr->flags & RPMBUILD_ISPATCH) {
2083             (void) headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_PATCH,
2084                                    RPM_STRING_ARRAY_TYPE, &srcPtr->source, 1);
2085             if (srcPtr->flags & RPMBUILD_ISNO) {
2086                 (void) headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_NOPATCH,
2087                                        RPM_INT32_TYPE, &srcPtr->num, 1);
2088             }
2089         }
2090
2091       { const char * sfn;
2092         sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
2093                 "%{_sourcedir}/", srcPtr->source, NULL);
2094         appendLineStringBuf(sourceFiles, sfn);
2095         sfn = _free(sfn);
2096       }
2097     }
2098
2099     for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
2100         for (srcPtr = pkg->icon; srcPtr != NULL; srcPtr = srcPtr->next) {
2101             const char * sfn;
2102             sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
2103                 "%{_sourcedir}/", srcPtr->source, NULL);
2104             appendLineStringBuf(sourceFiles, sfn);
2105             sfn = _free(sfn);
2106         }
2107     }
2108
2109     spec->sourceCpioList = NULL;
2110
2111     fl.fileList = xcalloc((spec->numSources + 1), sizeof(*fl.fileList));
2112     fl.processingFailed = 0;
2113     fl.fileListRecsUsed = 0;
2114     fl.totalFileSize = 0;
2115     fl.prefix = NULL;
2116     fl.buildRootURL = NULL;
2117
2118     s = getStringBuf(sourceFiles);
2119     files = splitString(s, strlen(s), '\n');
2120
2121     /* The first source file is the spec file */
2122     x = 0;
2123     for (fp = files; *fp != NULL; fp++) {
2124         const char * diskURL, *diskPath;
2125         FileListRec flp;
2126
2127         diskURL = *fp;
2128         SKIPSPACE(diskURL);
2129         if (! *diskURL)
2130             continue;
2131
2132         flp = &fl.fileList[x];
2133
2134         flp->flags = isSpec ? RPMFILE_SPECFILE : 0;
2135         /* files with leading ! are no source files */
2136         if (*diskURL == '!') {
2137             flp->flags |= RPMFILE_GHOST;
2138             diskURL++;
2139         }
2140
2141         (void) urlPath(diskURL, &diskPath);
2142
2143         flp->diskURL = xstrdup(diskURL);
2144         diskPath = strrchr(diskPath, '/');
2145         if (diskPath)
2146             diskPath++;
2147         else
2148             diskPath = diskURL;
2149
2150         flp->fileURL = xstrdup(diskPath);
2151         flp->verifyFlags = RPMVERIFY_ALL;
2152
2153         if (Stat(diskURL, &flp->fl_st)) {
2154             rpmError(RPMERR_BADSPEC, _("Bad file: %s: %s\n"),
2155                 diskURL, strerror(errno));
2156             fl.processingFailed = 1;
2157         }
2158
2159         flp->uname = getUname(flp->fl_uid);
2160         flp->gname = getGname(flp->fl_gid);
2161         flp->langs = xstrdup("");
2162         
2163         fl.totalFileSize += flp->fl_size;
2164         
2165         if (! (flp->uname && flp->gname)) {
2166             rpmError(RPMERR_BADSPEC, _("Bad owner/group: %s\n"), diskURL);
2167             fl.processingFailed = 1;
2168         }
2169
2170         isSpec = 0;
2171         x++;
2172     }
2173     fl.fileListRecsUsed = x;
2174     freeSplitString(files);
2175
2176     if (! fl.processingFailed) {
2177         if (spec->sourceHeader != NULL)
2178             genCpioListAndHeader(&fl, (TFI_t *)&spec->sourceCpioList,
2179                         spec->sourceHeader, 1);
2180     }
2181
2182     sourceFiles = freeStringBuf(sourceFiles);
2183     fl.fileList = freeFileList(fl.fileList, fl.fileListRecsUsed);
2184     return fl.processingFailed;
2185 }
2186
2187 /**
2188  */
2189 static StringBuf getOutputFrom(char * dir, char * argv[],
2190                         const char * writePtr, int writeBytesLeft,
2191                         int failNonZero)
2192         /*@globals fileSystem, internalState@*/
2193         /*@modifies fileSystem, internalState@*/
2194 {
2195     int progPID;
2196     int toProg[2];
2197     int fromProg[2];
2198     int status;
2199     void *oldhandler;
2200     StringBuf readBuff;
2201     int done;
2202
2203     /*@-type@*/ /* FIX: cast? */
2204     oldhandler = signal(SIGPIPE, SIG_IGN);
2205     /*@=type@*/
2206
2207     toProg[0] = toProg[1] = 0;
2208     (void) pipe(toProg);
2209     fromProg[0] = fromProg[1] = 0;
2210     (void) pipe(fromProg);
2211     
2212     if (!(progPID = fork())) {
2213         (void) close(toProg[1]);
2214         (void) close(fromProg[0]);
2215         
2216         (void) dup2(toProg[0], STDIN_FILENO);   /* Make stdin the in pipe */
2217         (void) dup2(fromProg[1], STDOUT_FILENO); /* Make stdout the out pipe */
2218
2219         (void) close(toProg[0]);
2220         (void) close(fromProg[1]);
2221
2222         if (dir) {
2223             (void) chdir(dir);
2224         }
2225         
2226         (void) execvp(argv[0], argv);
2227         /* XXX this error message is probably not seen. */
2228         rpmError(RPMERR_EXEC, _("Couldn't exec %s: %s\n"),
2229                 argv[0], strerror(errno));
2230         _exit(RPMERR_EXEC);
2231     }
2232     if (progPID < 0) {
2233         rpmError(RPMERR_FORK, _("Couldn't fork %s: %s\n"),
2234                 argv[0], strerror(errno));
2235         return NULL;
2236     }
2237
2238     (void) close(toProg[0]);
2239     (void) close(fromProg[1]);
2240
2241     /* Do not block reading or writing from/to prog. */
2242     (void) fcntl(fromProg[0], F_SETFL, O_NONBLOCK);
2243     (void) fcntl(toProg[1], F_SETFL, O_NONBLOCK);
2244     
2245     readBuff = newStringBuf();
2246
2247     do {
2248         fd_set ibits, obits;
2249         struct timeval tv;
2250         int nfd, nbw, nbr;
2251         int rc;
2252
2253         done = 0;
2254 top:
2255         /* XXX the select is mainly a timer since all I/O is non-blocking */
2256         FD_ZERO(&ibits);
2257         FD_ZERO(&obits);
2258         if (fromProg[0] >= 0) {
2259             FD_SET(fromProg[0], &ibits);
2260         }
2261         if (toProg[1] >= 0) {
2262             FD_SET(toProg[1], &obits);
2263         }
2264         tv.tv_sec = 1;
2265         tv.tv_usec = 0;
2266         nfd = ((fromProg[0] > toProg[1]) ? fromProg[0] : toProg[1]);
2267         if ((rc = select(nfd, &ibits, &obits, NULL, &tv)) < 0) {
2268             if (errno == EINTR)
2269                 goto top;
2270             break;
2271         }
2272
2273         /* Write any data to program */
2274         if (toProg[1] >= 0 && FD_ISSET(toProg[1], &obits)) {
2275           if (writeBytesLeft) {
2276             if ((nbw = write(toProg[1], writePtr,
2277                     (1024<writeBytesLeft) ? 1024 : writeBytesLeft)) < 0) {
2278                 if (errno != EAGAIN) {
2279                     perror("getOutputFrom()");
2280                     exit(EXIT_FAILURE);
2281                 }
2282                 nbw = 0;
2283             }
2284             writeBytesLeft -= nbw;
2285             writePtr += nbw;
2286           } else if (toProg[1] >= 0) {  /* close write fd */
2287             (void) close(toProg[1]);
2288             toProg[1] = -1;
2289           }
2290         }
2291         
2292         /* Read any data from prog */
2293         {   char buf[BUFSIZ+1];
2294             while ((nbr = read(fromProg[0], buf, sizeof(buf)-1)) > 0) {
2295                 buf[nbr] = '\0';
2296                 appendStringBuf(readBuff, buf);
2297             }
2298         }
2299
2300         /* terminate on (non-blocking) EOF or error */
2301         done = (nbr == 0 || (nbr < 0 && errno != EAGAIN));
2302
2303     } while (!done);
2304
2305     /* Clean up */
2306     if (toProg[1] >= 0)
2307         (void) close(toProg[1]);
2308     if (fromProg[0] >= 0)
2309         (void) close(fromProg[0]);
2310     /*@-type@*/ /* FIX: cast? */
2311     (void) signal(SIGPIPE, oldhandler);
2312     /*@=type@*/
2313
2314     /* Collect status from prog */
2315     (void)waitpid(progPID, &status, 0);
2316     if (failNonZero && (!WIFEXITED(status) || WEXITSTATUS(status))) {
2317         rpmError(RPMERR_EXEC, _("%s failed\n"), argv[0]);
2318         return NULL;
2319     }
2320     if (writeBytesLeft) {
2321         rpmError(RPMERR_EXEC, _("failed to write all data to %s\n"), argv[0]);
2322         return NULL;
2323     }
2324     return readBuff;
2325 }
2326
2327 /**
2328  */
2329 typedef struct {
2330 /*@observer@*/ /*@null@*/ const char * msg;
2331 /*@observer@*/ const char * argv[4];
2332     rpmTag ntag;
2333     rpmTag vtag;
2334     rpmTag ftag;
2335     int mask;
2336     int xor;
2337 } DepMsg_t;
2338
2339 /**
2340  */
2341 /*@-exportlocal -exportheadervar@*/
2342 /*@unchecked@*/
2343 DepMsg_t depMsgs[] = {
2344   { "Provides",         { "%{__find_provides}", NULL, NULL, NULL },
2345         RPMTAG_PROVIDENAME, RPMTAG_PROVIDEVERSION, RPMTAG_PROVIDEFLAGS,
2346         0, -1 },
2347   { "PreReq",           { NULL, NULL, NULL, NULL },
2348         RPMTAG_REQUIRENAME, RPMTAG_REQUIREVERSION, RPMTAG_REQUIREFLAGS,
2349         RPMSENSE_PREREQ, 0 },
2350   { "Requires(interp)", { NULL, "interp", NULL, NULL },
2351         -1, -1, RPMTAG_REQUIREFLAGS,
2352         _notpre(RPMSENSE_INTERP), 0 },
2353   { "Requires(rpmlib)", { NULL, "rpmlib", NULL, NULL },
2354         -1, -1, RPMTAG_REQUIREFLAGS,
2355         _notpre(RPMSENSE_RPMLIB), 0 },
2356   { "Requires(verify)", { NULL, "verify", NULL, NULL },
2357         -1, -1, RPMTAG_REQUIREFLAGS,
2358         RPMSENSE_SCRIPT_VERIFY, 0 },
2359   { "Requires(pre)",    { NULL, "pre", NULL, NULL },
2360         -1, -1, RPMTAG_REQUIREFLAGS,
2361         _notpre(RPMSENSE_SCRIPT_PRE), 0 },
2362   { "Requires(post)",   { NULL, "post", NULL, NULL },
2363         -1, -1, RPMTAG_REQUIREFLAGS,
2364         _notpre(RPMSENSE_SCRIPT_POST), 0 },
2365   { "Requires(preun)",  { NULL, "preun", NULL, NULL },
2366         -1, -1, RPMTAG_REQUIREFLAGS,
2367         _notpre(RPMSENSE_SCRIPT_PREUN), 0 },
2368   { "Requires(postun)", { NULL, "postun", NULL, NULL },
2369         -1, -1, RPMTAG_REQUIREFLAGS,
2370         _notpre(RPMSENSE_SCRIPT_POSTUN), 0 },
2371   { "Requires",         { "%{__find_requires}", NULL, NULL, NULL },
2372         -1, -1, RPMTAG_REQUIREFLAGS,    /* XXX inherit name/version arrays */
2373         RPMSENSE_PREREQ, RPMSENSE_PREREQ },
2374   { "Conflicts",        { "%{__find_conflicts}", NULL, NULL, NULL },
2375         RPMTAG_CONFLICTNAME, RPMTAG_CONFLICTVERSION, RPMTAG_CONFLICTFLAGS,
2376         0, -1 },
2377   { "Obsoletes",        { "%{__find_obsoletes}", NULL, NULL, NULL },
2378         RPMTAG_OBSOLETENAME, RPMTAG_OBSOLETEVERSION, RPMTAG_OBSOLETEFLAGS,
2379         0, -1 },
2380   { NULL,               { NULL, NULL, NULL, NULL },     0, 0, 0, 0, 0 }
2381 };
2382 /*@=exportlocal =exportheadervar@*/
2383
2384 /**
2385  */
2386 static int generateDepends(Spec spec, Package pkg, TFI_t cpioList, int multiLib)
2387         /*@globals rpmGlobalMacroContext,
2388                 fileSystem, internalState @*/
2389         /*@modifies cpioList, rpmGlobalMacroContext,
2390                 fileSystem, internalState @*/
2391 {
2392     TFI_t fi = cpioList;
2393     StringBuf writeBuf;
2394     int writeBytes;
2395     StringBuf readBuf;
2396     DepMsg_t *dm;
2397     char *myargv[4];
2398     int failnonzero = 0;
2399     int rc = 0;
2400     int i;
2401
2402     if (!(fi && fi->fc > 0))
2403         return 0;
2404
2405     if (! (pkg->autoReq || pkg->autoProv))
2406         return 0;
2407     
2408     writeBuf = newStringBuf();
2409     for (i = 0, writeBytes = 0; i < fi->fc; i++) {
2410
2411         if (fi->fmapflags && multiLib == 2) {
2412             if (!(fi->fmapflags[i] & CPIO_MULTILIB))
2413                 continue;
2414             fi->fmapflags[i] &= ~CPIO_MULTILIB;
2415         }
2416
2417         appendStringBuf(writeBuf, fi->dnl[fi->dil[i]]);
2418         writeBytes += strlen(fi->dnl[fi->dil[i]]);
2419         appendLineStringBuf(writeBuf, fi->bnl[i]);
2420         writeBytes += strlen(fi->bnl[i]) + 1;
2421     }
2422
2423     for (dm = depMsgs; dm->msg != NULL; dm++) {
2424         int tag, tagflags;
2425
2426         tag = (dm->ftag > 0) ? dm->ftag : dm->ntag;
2427         tagflags = 0;
2428
2429         switch(tag) {
2430         case RPMTAG_PROVIDEFLAGS:
2431             if (!pkg->autoProv)
2432                 continue;
2433             failnonzero = 1;
2434             tagflags = RPMSENSE_FIND_PROVIDES;
2435             /*@switchbreak@*/ break;
2436         case RPMTAG_REQUIREFLAGS:
2437             if (!pkg->autoReq)
2438                 continue;
2439             failnonzero = 0;
2440             tagflags = RPMSENSE_FIND_REQUIRES;
2441             /*@switchbreak@*/ break;
2442         default:
2443             continue;
2444             /*@notreached@*/ /*@switchbreak@*/ break;
2445         }
2446
2447         /* Get the script name to run */
2448         /*@-nullderef@*/        /* FIX: double indirection. @*/
2449         myargv[0] = (dm->argv[0] ? rpmExpand(dm->argv[0], NULL) : NULL);
2450         /*@=nullderef@*/
2451
2452         if (!(myargv[0] && *myargv[0] != '%')) {
2453             myargv[0] = _free(myargv[0]);
2454             continue;
2455         }
2456
2457         rpmMessage(RPMMESS_NORMAL, _("Finding  %s: (using %s)...\n"),
2458                 dm->msg, myargv[0]);
2459
2460 #if 0
2461         if (*myargv[0] != '/') {        /* XXX FIXME: stat script here */
2462             myargv[0] = _free(myargv[0]);
2463             continue;
2464         }
2465 #endif
2466
2467         /* Expand rest of script arguments (if any) */
2468         for (i = 1; i < 4; i++) {
2469             /*@-nullderef@*/    /* FIX: double indirection. @*/
2470             myargv[i] = dm->argv[i] ? rpmExpand(dm->argv[i], NULL) : NULL;
2471             /*@=nullderef@*/
2472         }
2473
2474         readBuf = getOutputFrom(NULL, myargv,
2475                         getStringBuf(writeBuf), writeBytes, failnonzero);
2476
2477         /* Free expanded args */
2478         for (i = 0; i < 4; i++)
2479             myargv[i] = _free(myargv[i]);
2480
2481         if (readBuf == NULL) {
2482             rc = RPMERR_EXEC;
2483             rpmError(rc, _("Failed to find %s:\n"), dm->msg);
2484             break;
2485         }
2486
2487         /* Parse dependencies into header */
2488         tagflags &= ~RPMSENSE_MULTILIB;
2489         if (multiLib > 1)
2490             tagflags |=  RPMSENSE_MULTILIB;
2491         else
2492             tagflags &= ~RPMSENSE_MULTILIB;
2493         rc = parseRCPOT(spec, pkg, getStringBuf(readBuf), tag, 0, tagflags);
2494         readBuf = freeStringBuf(readBuf);
2495
2496         if (rc) {
2497             rpmError(rc, _("Failed to find %s:\n"), dm->msg);
2498             break;
2499         }
2500     }
2501
2502     writeBuf = freeStringBuf(writeBuf);
2503     return rc;
2504 }
2505
2506 /**
2507  */
2508 static void printDepMsg(DepMsg_t * dm, int count, const char ** names,
2509                 const char ** versions, int *flags)
2510         /*@*/
2511 {
2512     int hasVersions = (versions != NULL);
2513     int hasFlags = (flags != NULL);
2514     int bingo = 0;
2515     int i;
2516
2517     for (i = 0; i < count; i++, names++, versions++, flags++) {
2518         if (hasFlags && !((*flags & dm->mask) ^ dm->xor))
2519             continue;
2520         if (bingo == 0) {
2521             rpmMessage(RPMMESS_NORMAL, "%s:", (dm->msg ? dm->msg : ""));
2522             bingo = 1;
2523         }
2524         rpmMessage(RPMMESS_NORMAL, " %s", *names);
2525
2526         if (hasFlags && isDependsMULTILIB(*flags))
2527             rpmMessage(RPMMESS_NORMAL, " (multilib)");
2528
2529         if (hasVersions && !(*versions != NULL && **versions != '\0'))
2530             continue;
2531         if (!(hasFlags && (*flags && RPMSENSE_SENSEMASK)))
2532             continue;
2533
2534         rpmMessage(RPMMESS_NORMAL, " ");
2535         if (*flags & RPMSENSE_LESS)
2536             rpmMessage(RPMMESS_NORMAL, "<");
2537         if (*flags & RPMSENSE_GREATER)
2538             rpmMessage(RPMMESS_NORMAL, ">");
2539         if (*flags & RPMSENSE_EQUAL)
2540             rpmMessage(RPMMESS_NORMAL, "=");
2541
2542         rpmMessage(RPMMESS_NORMAL, " %s", *versions);
2543     }
2544     if (bingo)
2545         rpmMessage(RPMMESS_NORMAL, "\n");
2546 }
2547
2548 /**
2549  */
2550 static void printDeps(Header h)
2551         /*@*/
2552 {
2553     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
2554     HFD_t hfd = headerFreeData;
2555     const char ** names = NULL;
2556     rpmTagType dnt = -1;
2557     const char ** versions = NULL;
2558     rpmTagType dvt = -1;
2559     int * flags = NULL;
2560     DepMsg_t * dm;
2561     int count, xx;
2562
2563     for (dm = depMsgs; dm->msg != NULL; dm++) {
2564         switch (dm->ntag) {
2565         case 0:
2566             names = hfd(names, dnt);
2567             /*@switchbreak@*/ break;
2568         case -1:
2569             /*@switchbreak@*/ break;
2570         default:
2571             names = hfd(names, dnt);
2572             if (!hge(h, dm->ntag, &dnt, (void **) &names, &count))
2573                 continue;
2574             /*@switchbreak@*/ break;
2575         }
2576         switch (dm->vtag) {
2577         case 0:
2578             versions = hfd(versions, dvt);
2579             /*@switchbreak@*/ break;
2580         case -1:
2581             /*@switchbreak@*/ break;
2582         default:
2583             versions = hfd(versions, dvt);
2584             xx = hge(h, dm->vtag, &dvt, (void **) &versions, NULL);
2585             /*@switchbreak@*/ break;
2586         }
2587         switch (dm->ftag) {
2588         case 0:
2589             flags = NULL;
2590             /*@switchbreak@*/ break;
2591         case -1:
2592             /*@switchbreak@*/ break;
2593         default:
2594             xx = hge(h, dm->ftag, NULL, (void **) &flags, NULL);
2595             /*@switchbreak@*/ break;
2596         }
2597         /*@-noeffect@*/
2598         printDepMsg(dm, count, names, versions, flags);
2599         /*@=noeffect@*/
2600     }
2601     names = hfd(names, dnt);
2602     versions = hfd(versions, dvt);
2603 }
2604
2605 int processBinaryFiles(Spec spec, int installSpecialDoc, int test)
2606 {
2607     Package pkg;
2608     int res = 0;
2609     
2610     for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
2611         const char *n, *v, *r;
2612         int rc;
2613
2614         if (pkg->fileList == NULL)
2615             continue;
2616
2617         (void) headerNVR(pkg->header, &n, &v, &r);
2618         rpmMessage(RPMMESS_NORMAL, _("Processing files: %s-%s-%s\n"), n, v, r);
2619                    
2620         if ((rc = processPackageFiles(spec, pkg, installSpecialDoc, test)))
2621             res = rc;
2622
2623     /* XXX This should be added always so that packages look alike.
2624      * XXX However, there is logic in files.c/depends.c that checks for
2625      * XXX existence (rather than value) that will need to change as well.
2626      */
2627         if (headerIsEntry(pkg->header, RPMTAG_MULTILIBS)) {
2628             (void) generateDepends(spec, pkg, pkg->cpioList, 1);
2629             (void) generateDepends(spec, pkg, pkg->cpioList, 2);
2630         } else
2631             (void) generateDepends(spec, pkg, pkg->cpioList, 0);
2632         /*@-noeffect@*/
2633         printDeps(pkg->header);
2634         /*@=noeffect@*/
2635     }
2636
2637     return res;
2638 }