c7214f92788eaf2011f0c66b2f6311f854c0a1f8
[platform/upstream/rpm.git] / build / files.c
1 /* RPM - Copyright (C) 1995 Red Hat Software
2  * 
3  * prepack.c - routines for packaging
4  */
5
6 #include "miscfn.h"
7
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <string.h>
11 #include <sys/stat.h>
12 #include <time.h>
13
14 #include "spec.h"
15 #include "specP.h"
16 #include "stringbuf.h"
17 #include "build.h"
18 #include "messages.h"
19 #include "md5.h"
20 #include "myftw.h"
21 #include "header.h"
22 #include "files.h"
23 #include "names.h"
24 #include "rpmlead.h"
25 #include "rpmlib.h"
26 #include "misc.h"
27 #include "macro.h"
28
29 #define BINARY_HEADER 0
30 #define SOURCE_HEADER 1
31
32 struct file_entry {
33     char file[1024];
34     int isdoc;
35     int conf;
36     int isspecfile;
37     int verify_flags;
38     char *uname;  /* reference -- do not free */
39     char *gname;  /* reference -- do not free */
40     struct stat statbuf;
41     struct file_entry *next;
42 };
43
44 static int add_file(struct file_entry **festack, const char *name,
45                     int isdoc, int isconf, int isdir, int verify_flags,
46                     char *Pmode, char *Uname, char *Gname, char *prefix);
47 static int compare_fe(const void *ap, const void *bp);
48 static int add_file_aux(const char *file, struct stat *sb, int flag);
49 static int glob_error(const char *foo, int bar);
50 static int glob_pattern_p (char *pattern);
51 static int parseForVerify(char *buf, int *verify_flags);
52 static int parseForConfig(char *buf, int *conf);
53 static int parseForAttr(char *origbuf, char **currPmode,
54                         char **currUname, char **currGname);
55
56 static void resetDocdir(void);
57 static void addDocdir(char *dirname);
58 static int isDoc(char *filename);
59
60 static int processFileListFailed;
61
62 static void parseForDocFiles(struct PackageRec *package, char *line)
63 {
64     if (! (line = strstr(line, "%doc"))) {
65         return;
66     }
67
68     line += 4;
69     if ((*line != ' ') && (*line != '\t')) {
70         return;
71     }
72     line += strspn(line, " \t\n");
73     if ((! *line) || (*line == '/')) {
74         return;
75     }
76     
77     appendLineStringBuf(package->doc, "mkdir -p $DOCDIR");
78     appendStringBuf(package->doc, "cp -pr ");
79     appendStringBuf(package->doc, line);
80     appendLineStringBuf(package->doc, " $DOCDIR");
81 }
82
83 int finish_filelists(Spec spec)
84 {
85     char buf[1024];
86     FILE *file;
87     struct PackageRec *pr = spec->packages;
88     char *s, **files, **line;
89     char *version, *release, *packageVersion, *packageRelease, *docs, *name;
90
91     headerGetEntry(spec->packages->header, RPMTAG_VERSION, NULL,
92              (void *) &version, NULL);
93     headerGetEntry(spec->packages->header, RPMTAG_RELEASE, NULL,
94              (void *) &release, NULL);
95     
96     while (pr) {
97         if (pr->fileFile) {
98             sprintf(buf, "%s/%s/%s", rpmGetVar(RPMVAR_BUILDDIR),
99                     build_subdir, pr->fileFile);
100             rpmMessage(RPMMESS_DEBUG, "Reading file names from: %s\n", buf);
101             if ((file = fopen(buf, "r")) == NULL) {
102                 rpmError(RPMERR_BADSPEC, "unable to open filelist: %s\n", buf);
103                 return(RPMERR_BADSPEC);
104             }
105             while (fgets(buf, sizeof(buf), file)) {
106                 expandMacros(buf);
107                 appendStringBuf(pr->filelist, buf);
108             }
109             fclose(file);
110         }
111
112         /* parse for %doc wierdness */
113         s = getStringBuf(pr->filelist);
114         files = splitString(s, strlen(s), '\n');
115         line = files;
116         while (*line) {
117             parseForDocFiles(pr, *line);
118             line++;
119         }
120         freeSplitString(files);
121
122         /* Handle subpackage version/release overrides */
123         if (!headerGetEntry(pr->header, RPMTAG_VERSION, NULL,
124                       (void *) &packageVersion, NULL)) {
125             packageVersion = version;
126         }
127         if (!headerGetEntry(pr->header, RPMTAG_RELEASE, NULL,
128                       (void *) &packageRelease, NULL)) {
129             packageRelease = release;
130         }
131
132         /* Generate the doc script */
133         appendStringBuf(spec->doc, "DOCDIR=$RPM_ROOT_DIR/$RPM_DOC_DIR/");
134         headerGetEntry(pr->header, RPMTAG_NAME, NULL, (void *) &name, NULL);
135         sprintf(buf, "%s-%s-%s", name, packageVersion, packageRelease);
136         appendLineStringBuf(spec->doc, buf);
137         docs = getStringBuf(pr->doc);
138         if (*docs) {
139             appendLineStringBuf(spec->doc, "rm -rf $DOCDIR");
140             appendLineStringBuf(spec->doc, docs);
141         }
142
143         pr = pr->next;
144     }
145
146     return 0;
147 }
148
149 int process_filelist(Header header, struct PackageRec *pr,
150                      StringBuf sb, int *size, char *name,
151                      char *version, char *release, int type,
152                      char *prefix, char *specFile)
153 {
154     char buf[1024];
155     char **files, **fp;
156     struct file_entry *fes, *fest;
157     struct file_entry **file_entry_array;
158     int isdoc, conf, isdir, verify_flags;
159     char *currPmode=NULL;       /* hold info from %attr() */
160     char *currUname=NULL;       /* hold info from %attr() */
161     char *currGname=NULL;       /* hold info from %attr() */
162     char *filename, *s;
163     char *str;
164     int count = 0;
165     int c, x;
166     glob_t glob_result;
167     int special_doc;
168     int passed_special_doc = 0;
169     int tc;
170     char *tcs;
171     int currentTime;
172
173     processFileListFailed = 0;
174     fes = NULL;
175     *size = 0;
176
177     resetDocdir();
178
179     str = getStringBuf(sb);
180     files = splitString(str, strlen(str), '\n');
181     fp = files;
182
183     while (*fp) {
184         strcpy(buf, *fp);  /* temp copy */
185         isdoc = 0;
186         special_doc = 0;
187         conf = 0;
188         isdir = 0;
189         if (currPmode) {
190             free (currPmode);
191             currPmode = NULL;
192         }
193         if (currUname) {
194             free (currUname);
195             currUname = NULL;
196         }
197         if (currGname) {
198             free (currGname);
199             currGname = NULL;
200         }
201         verify_flags = RPMVERIFY_ALL;
202         filename = NULL;
203
204         /* First preparse buf for %verify() */
205         if (parseForVerify(buf, &verify_flags)) {
206             processFileListFailed = 1;
207             fp++; continue;
208         }
209         
210         /* Next parse for %attr() */
211         if (parseForAttr(buf, &currPmode, &currUname, &currGname)) {
212             processFileListFailed = 1;
213             fp++; continue;
214         }
215
216         /* Then parse for %config or %config() */
217         if (parseForConfig(buf, &conf)) {
218             processFileListFailed = 1;
219             fp++; continue;
220         }
221
222         s = strtok(buf, " \t\n");
223         while (s) {
224             if (!strcmp(s, "%docdir")) {
225                 s = strtok(NULL, " \t\n");
226                 addDocdir(s);
227                 break;
228             } else if (!strcmp(s, "%doc")) {
229                 isdoc = 1;
230             } else if (!strcmp(s, "%dir")) {
231                 isdir = 1;
232             } else {
233                 if (filename) {
234                     /* We already got a file -- error */
235                     rpmError(RPMERR_BADSPEC,
236                           "Two files on one line: %s", filename);
237                     processFileListFailed = 1;
238                 }
239                 if (*s != '/') {
240                     if (isdoc) {
241                         special_doc = 1;
242                     } else {
243                         /* not in %doc, does not begin with / -- error */
244                         rpmError(RPMERR_BADSPEC,
245                               "File must begin with \"/\": %s", s);
246                         processFileListFailed = 1;
247                     }
248                 } else {
249                     filename = s;
250                 }
251             }
252             s = strtok(NULL, " \t\n");
253         }
254         if (special_doc) {
255             if (passed_special_doc) {
256                 fp++;
257                 continue;
258             } else {
259                 if (filename || conf || isdir) {
260                     rpmError(RPMERR_BADSPEC,
261                           "Can't mix special %%doc with other forms: %s", fp);
262                     processFileListFailed = 1;
263                     fp++; continue;
264                 }
265                 sprintf(buf, "%s/%s-%s-%s", rpmGetVar(RPMVAR_DEFAULTDOCDIR), 
266                         name, version, release);
267                 filename = buf;
268                 passed_special_doc = 1;
269             }
270         }
271         if (! filename) {
272             fp++;
273             continue;
274         }
275
276         if (type == RPMLEAD_BINARY) {
277             /* check that file starts with leading "/" */
278             if (*filename != '/') {
279                 rpmError(RPMERR_BADSPEC,
280                       "File needs leading \"/\": %s", filename);
281                 processFileListFailed = 1;
282                 fp++; continue;
283             }
284
285             if (glob_pattern_p(filename)) {
286                 char fullname[1024];
287
288                 if (rpmGetVar(RPMVAR_ROOT)) {
289                     sprintf(fullname, "%s%s", rpmGetVar(RPMVAR_ROOT), filename);
290                 } else {
291                     strcpy(fullname, filename);
292                 }
293
294                 if (glob(fullname, 0, glob_error, &glob_result) ||
295                     (glob_result.gl_pathc < 1)) {
296                     rpmError(RPMERR_BADSPEC, "No matches: %s", fullname);
297                     processFileListFailed = 1;
298                     globfree(&glob_result);
299                     fp++; continue;
300                 }
301                 
302                 c = 0;
303                 x = 0;
304                 while (x < glob_result.gl_pathc) {
305                     int offset = strlen(rpmGetVar(RPMVAR_ROOT) ? : "");
306                     c += add_file(&fes, &(glob_result.gl_pathv[x][offset]),
307                                   isdoc, conf, isdir, verify_flags,
308                                   currPmode, currUname, currGname, prefix);
309                     x++;
310                 }
311                 globfree(&glob_result);
312             } else {
313                 c = add_file(&fes, filename, isdoc, conf, isdir,
314                              verify_flags, currPmode, currUname,
315                              currGname, prefix);
316             }
317         } else {
318             /* Source package are the simple case */
319             fest = malloc(sizeof(struct file_entry));
320             fest->isdoc = 0;
321             fest->conf = 0;
322             if (!strcmp(filename, specFile)) {
323                 fest->isspecfile = 1;
324             } else {
325                 fest->isspecfile = 0;
326             }
327             fest->verify_flags = 0;  /* XXX - something else? */
328             stat(filename, &fest->statbuf);
329             fest->uname = getUname(fest->statbuf.st_uid);
330             fest->gname = getGname(fest->statbuf.st_gid);
331             if (! (fest->uname && fest->gname)) {
332                 rpmError(RPMERR_BADSPEC, "Bad owner/group: %s", filename);
333                 fest->uname = "";
334                 fest->gname = "";
335                 processFileListFailed = 1;
336             }
337             strcpy(fest->file, filename);
338             fest->next = fes;
339             fes = fest;
340             c = 1;
341         }
342             
343         if (! c) {
344             rpmError(RPMERR_BADSPEC, "File not found: %s", filename);
345             processFileListFailed = 1;
346         }
347         count += c;
348         
349         fp++;
350     }
351
352     /* If there are no files, don't add anything to the header */
353     if (count) {
354         char ** fileList;
355         char ** fileMD5List;
356         char ** fileLinktoList;
357         int_32 * fileSizeList;
358         int_32 * fileUIDList;
359         int_32 * fileGIDList;
360         char ** fileUnameList;
361         char ** fileGnameList;
362         int_32 * fileMtimesList;
363         int_32 * fileFlagsList;
364         int_16 * fileModesList;
365         int_16 * fileRDevsList;
366         int_32 * fileVerifyFlagsList;
367
368         fileList = malloc(sizeof(char *) * count);
369         fileLinktoList = malloc(sizeof(char *) * count);
370         fileMD5List = malloc(sizeof(char *) * count);
371         fileSizeList = malloc(sizeof(int_32) * count);
372         fileUIDList = malloc(sizeof(int_32) * count);
373         fileGIDList = malloc(sizeof(int_32) * count);
374         fileUnameList = malloc(sizeof(char *) * count);
375         fileGnameList = malloc(sizeof(char *) * count);
376         fileMtimesList = malloc(sizeof(int_32) * count);
377         fileFlagsList = malloc(sizeof(int_32) * count);
378         fileModesList = malloc(sizeof(int_16) * count);
379         fileRDevsList = malloc(sizeof(int_16) * count);
380         fileVerifyFlagsList = malloc(sizeof(int_32) * count);
381
382         /* Build a reverse sorted file array.  */
383         /* This makes uninstalls a lot easier. */
384         file_entry_array = malloc(sizeof(struct file_entry *) * count);
385         c = 0;
386         fest = fes;
387         while (fest) {
388             file_entry_array[c++] = fest;
389             fest = fest->next;
390         }
391         qsort(file_entry_array, count, sizeof(struct file_entry *),
392               compare_fe);
393                 
394         /* Do timecheck */
395         tc = 0;
396         currentTime = time(NULL);
397         if ((tcs = rpmGetVar(RPMVAR_TIMECHECK))) {
398             tc = strtoul(tcs, NULL, 10);
399         }
400     
401         c = 0;
402         while (c < count) {
403             fest = file_entry_array[c];
404             if (type == RPMLEAD_BINARY) {
405                 x = strlen(fest->file) - 1;
406                 if (x && fest->file[x] == '/') {
407                     fest->file[x] = '\0';
408                 }
409                 fileList[c] = fest->file;
410             } else {
411                 fileList[c] = strrchr(fest->file, '/') + 1;
412             }
413             if ((c > 0) && !strcmp(fileList[c], fileList[c-1])) {
414                 rpmError(RPMERR_BADSPEC, "File listed twice: %s", fileList[c]);
415                 processFileListFailed = 1;
416             }
417             
418             fileUnameList[c] = fest->uname;
419             fileGnameList[c] = fest->gname;
420             *size += fest->statbuf.st_size;
421             if (S_ISREG(fest->statbuf.st_mode)) {
422                 if ((type == RPMLEAD_BINARY) &&
423                     rpmGetVar(RPMVAR_ROOT)) {
424                     sprintf(buf, "%s%s", rpmGetVar(RPMVAR_ROOT), fest->file);
425                 } else {
426                     strcpy(buf, fest->file);
427                 }
428                 mdfile(buf, buf);
429                 fileMD5List[c] = strdup(buf);
430                 rpmMessage(RPMMESS_DEBUG, "md5(%s) = %s\n", fest->file, buf);
431             } else {
432                 /* This is stupid */
433                 fileMD5List[c] = strdup("");
434             }
435             fileSizeList[c] = fest->statbuf.st_size;
436             fileUIDList[c] = fest->statbuf.st_uid;
437             fileGIDList[c] = fest->statbuf.st_gid;
438             fileMtimesList[c] = fest->statbuf.st_mtime;
439
440             /* Do timecheck */
441             if (tc && (type == RPMLEAD_BINARY)) {
442                 if (currentTime - fest->statbuf.st_mtime > tc) {
443                     rpmMessage(RPMMESS_WARNING, "TIMECHECK failure: %s\n",
444                             fest->file);
445                 }
446             }
447     
448             fileFlagsList[c] = 0;
449             if (isDoc(fest->file))
450                 fileFlagsList[c] |= RPMFILE_DOC;
451             if (fest->isdoc) 
452                 fileFlagsList[c] |= RPMFILE_DOC;
453             if (fest->conf && !(fest->statbuf.st_mode & S_IFDIR))
454                 fileFlagsList[c] |= fest->conf;
455             if (fest->isspecfile)
456                 fileFlagsList[c] |= RPMFILE_SPECFILE;
457
458             fileModesList[c] = fest->statbuf.st_mode;
459             fileRDevsList[c] = fest->statbuf.st_rdev;
460             fileVerifyFlagsList[c] = fest->verify_flags;
461
462             if (S_ISLNK(fest->statbuf.st_mode)) {
463                 if (rpmGetVar(RPMVAR_ROOT)) {
464                     sprintf(buf, "%s%s", rpmGetVar(RPMVAR_ROOT), fest->file);
465                 } else {
466                     strcpy(buf, fest->file);
467                 }
468                 buf[readlink(buf, buf, 1024)] = '\0';
469                 fileLinktoList[c] = strdup(buf);
470             } else {
471                 /* This is stupid */
472                 fileLinktoList[c] = strdup("");
473             }
474             c++;
475         }
476
477         /* Add the header entries */
478         c = count;
479         headerAddEntry(header, RPMTAG_FILENAMES, RPM_STRING_ARRAY_TYPE, fileList, c);
480         headerAddEntry(header, RPMTAG_FILELINKTOS, RPM_STRING_ARRAY_TYPE,
481                  fileLinktoList, c);
482         headerAddEntry(header, RPMTAG_FILEMD5S, RPM_STRING_ARRAY_TYPE, fileMD5List, c);
483         headerAddEntry(header, RPMTAG_FILESIZES, RPM_INT32_TYPE, fileSizeList, c);
484         headerAddEntry(header, RPMTAG_FILEUIDS, RPM_INT32_TYPE, fileUIDList, c);
485         headerAddEntry(header, RPMTAG_FILEGIDS, RPM_INT32_TYPE, fileGIDList, c);
486         headerAddEntry(header, RPMTAG_FILEUSERNAME, RPM_STRING_ARRAY_TYPE,
487                  fileUnameList, c);
488         headerAddEntry(header, RPMTAG_FILEGROUPNAME, RPM_STRING_ARRAY_TYPE,
489                  fileGnameList, c);
490         headerAddEntry(header, RPMTAG_FILEMTIMES, RPM_INT32_TYPE, fileMtimesList, c);
491         headerAddEntry(header, RPMTAG_FILEFLAGS, RPM_INT32_TYPE, fileFlagsList, c);
492         headerAddEntry(header, RPMTAG_FILEMODES, RPM_INT16_TYPE, fileModesList, c);
493         headerAddEntry(header, RPMTAG_FILERDEVS, RPM_INT16_TYPE, fileRDevsList, c);
494         headerAddEntry(header, RPMTAG_FILEVERIFYFLAGS, RPM_INT32_TYPE,
495                  fileVerifyFlagsList, c);
496         
497         /* Free the allocated strings */
498         c = count;
499         while (c--) {
500             free(fileMD5List[c]);
501             free(fileLinktoList[c]);
502         }
503
504         /* Free all those lists */
505         free(fileList);
506         free(fileLinktoList);
507         free(fileMD5List);
508         free(fileSizeList);
509         free(fileUIDList);
510         free(fileGIDList);
511         free(fileUnameList);
512         free(fileGnameList);
513         free(fileMtimesList);
514         free(fileFlagsList);
515         free(fileModesList);
516         free(fileRDevsList);
517         free(fileVerifyFlagsList);
518         
519         /* Free the file entry array */
520         free(file_entry_array);
521         
522         /* Free the file entry stack */
523         fest = fes;
524         while (fest) {
525             fes = fest->next;
526             free(fest);
527             fest = fes;
528         }
529     }
530     
531     freeSplitString(files);
532     return processFileListFailed;
533 }
534
535 /*************************************************************/
536 /*                                                           */
537 /* misc                                                      */
538 /*                                                           */
539 /*************************************************************/
540
541 static int compare_fe(const void *ap, const void *bp)
542 {
543     char *a, *b;
544
545     a = (*(struct file_entry **)ap)->file;
546     b = (*(struct file_entry **)bp)->file;
547
548     return strcmp(a, b);
549 }
550
551 /*************************************************************/
552 /*                                                           */
553 /* Doc dir stuff                                             */
554 /*                                                           */
555 /*************************************************************/
556
557 /* XXX hard coded limit -- only 1024 %docdir allowed */
558 static char *docdirs[1024];
559 static int docdir_count;
560
561 static void resetDocdir(void)
562 {
563     while (docdir_count--) {
564         free(docdirs[docdir_count]);
565     }
566     docdir_count = 0;
567     docdirs[docdir_count++] = strdup("/usr/doc");
568     docdirs[docdir_count++] = strdup("/usr/man");
569     docdirs[docdir_count++] = strdup("/usr/info");
570     docdirs[docdir_count++] = strdup("/usr/X11R6/man");
571 }
572
573 static void addDocdir(char *dirname)
574 {
575     if (docdir_count == 1024) {
576         fprintf(stderr, "RPMERR_INTERNAL: Hit limit in addDocdir()\n");
577         exit(RPMERR_INTERNAL);
578     }
579     docdirs[docdir_count++] = strdup(dirname);
580 }
581
582 static int isDoc(char *filename)
583 {
584     int x = 0;
585
586     while (x < docdir_count) {
587         if (strstr(filename, docdirs[x]) == filename) {
588             return 1;
589         }
590         x++;
591     }
592     return 0;
593 }
594
595 /*************************************************************/
596 /*                                                           */
597 /* File stating / tree walk                                  */
598 /*                                                           */
599 /*************************************************************/
600
601 /* Need three globals to keep track of things in ftw() */
602 static int Gisdoc;
603 static int Gconf;
604 static int Gverify_flags;
605 static int Gcount;
606 static char *GPmode;
607 static char *GUname;
608 static char *GGname;
609 static struct file_entry **Gfestack;
610 static char *Gprefix;
611
612 static int add_file(struct file_entry **festack, const char *name,
613                     int isdoc, int conf, int isdir, int verify_flags,
614                     char *Pmode, char *Uname, char *Gname, char *prefix)
615 {
616     struct file_entry *p;
617     char *copyTo, copied;
618     const char *prefixTest, *prefixPtr;
619     const char *copyFrom;
620     char fullname[1024];
621     int mode;
622
623     /* Set these up for ftw() */
624     Gfestack = festack;
625     Gisdoc = isdoc;
626     Gconf = conf;
627     Gverify_flags = verify_flags;
628     GPmode = Pmode;
629     GUname = Uname;
630     GGname = Gname;
631     Gprefix = prefix;
632
633     p = malloc(sizeof(struct file_entry));
634
635     copyTo = p->file;
636     copied = '\0';
637     copyFrom = name;
638     while (*copyFrom) {
639         if (*copyFrom != '/' || copied != '/') {
640             *copyTo++ = copied = *copyFrom;
641         }
642         copyFrom++;
643     }
644     *copyTo = '\0';
645
646     p->isdoc = isdoc;
647     p->conf = conf;
648     p->isspecfile = 0;  /* source packages are done by hand */
649     p->verify_flags = verify_flags;
650     if (rpmGetVar(RPMVAR_ROOT)) {
651         sprintf(fullname, "%s%s", rpmGetVar(RPMVAR_ROOT), name);
652     } else {
653         strcpy(fullname, name);
654     }
655
656     /* If we are using a prefix, validate the file */
657     if (prefix) {
658         prefixTest = name;
659         prefixPtr = prefix;
660         while (*prefixTest == '/') {
661             prefixTest++;  /* Skip leading "/" */
662         }
663         while (*prefixPtr && *prefixTest && (*prefixTest == *prefixPtr)) {
664             prefixPtr++;
665             prefixTest++;
666         }
667         if (*prefixPtr) {
668             rpmError(RPMERR_BADSPEC, "File doesn't match prefix (%s): %s",
669                   prefix, name);
670             return 0;
671         }
672     }
673
674     /* OK, finally stat() the file */
675     if (lstat(fullname, &p->statbuf)) {
676         return 0;
677     }
678
679     /*
680      * If %attr() was specified, then use those values instead of
681      * what lstat() returned.
682      */
683     if (Pmode && strcmp(Pmode, "-")) {
684         sscanf(Pmode, "%o", &mode);
685         mode |= p->statbuf.st_mode & S_IFMT;
686         p->statbuf.st_mode = (unsigned short)mode;
687     }
688
689     if (Uname && strcmp(Uname, "-")) {
690         p->uname = getUnameS(Uname);
691     } else {
692         p->uname = getUname(p->statbuf.st_uid);
693     }
694     
695     if (Gname && strcmp(Gname, "-")) {
696         p->gname = getGnameS(Gname);
697     } else {
698         p->gname = getGname(p->statbuf.st_gid);
699     }
700     
701     if (! (p->uname && p->gname)) {
702         rpmError(RPMERR_BADSPEC, "Bad owner/group: %s\n", fullname);
703         p->uname = "";
704         p->gname = "";
705         return 0;
706     }
707     
708     if ((! isdir) && S_ISDIR(p->statbuf.st_mode)) {
709         /* This means we need to descend with ftw() */
710         Gcount = 0;
711         
712         /* We use our own ftw() call, because ftw() uses stat()    */
713         /* instead of lstat(), which causes it to follow symlinks! */
714         myftw(fullname, add_file_aux, 16);
715         
716         free(p);
717
718         return Gcount;
719     } else {
720         /* Link it in */
721         p->next = *festack;
722         *festack = p;
723
724         rpmMessage(RPMMESS_DEBUG, "ADDING: %s\n", name);
725
726         /* return number of entries added */
727         return 1;
728     }
729 }
730
731 static int add_file_aux(const char *file, struct stat *sb, int flag)
732 {
733     const char *name = file;
734
735     if (rpmGetVar(RPMVAR_ROOT)) {
736         name += strlen(rpmGetVar(RPMVAR_ROOT));
737     }
738
739     /* The 1 will cause add_file() to *not* descend */
740     /* directories -- ftw() is already doing it!    */
741     Gcount += add_file(Gfestack, name, Gisdoc, Gconf, 1, Gverify_flags,
742                         GPmode, GUname, GGname, Gprefix);
743
744     return 0; /* for ftw() */
745 }
746
747 /*************************************************************/
748 /*                                                           */
749 /* globbing                                                  */
750 /*                                                           */
751 /*************************************************************/
752
753 /* glob_pattern_p() taken from bash
754  * Copyright (C) 1985, 1988, 1989 Free Software Foundation, Inc.
755  */
756
757 /* Return nonzero if PATTERN has any special globbing chars in it.  */
758 static int glob_pattern_p (char *pattern)
759 {
760     register char *p = pattern;
761     register char c;
762     int open = 0;
763   
764     while ((c = *p++) != '\0')
765         switch (c) {
766         case '?':
767         case '*':
768             return (1);
769         case '[':      /* Only accept an open brace if there is a close */
770             open++;    /* brace to match it.  Bracket expressions must be */
771             continue;  /* complete, according to Posix.2 */
772         case ']':
773             if (open)
774                 return (1);
775             continue;      
776         case '\\':
777             if (*p++ == '\0')
778                 return (0);
779         }
780
781     return (0);
782 }
783
784 static int glob_error(const char *foo, int bar)
785 {
786     return 1;
787 }
788
789 /*************************************************************/
790 /*                                                           */
791 /* %attr parsing                                             */
792 /*                                                           */
793 /*************************************************************/
794
795 static int parseForAttr(char *buf, char **currPmode,
796                         char **currUname, char **currGname)
797 {
798     char *p, *start, *end;
799     char ourbuf[1024];
800     int mode, x;
801
802     if (!(p = start = strstr(buf, "%attr"))) {
803         return 0;
804     }
805
806     *currPmode = *currUname = *currGname = NULL;
807
808     p += 5;
809     while (*p && (*p == ' ' || *p == '\t')) {
810         p++;
811     }
812
813     if (*p != '(') {
814         rpmError(RPMERR_BADSPEC, "Bad %%attr() syntax: %s", buf);
815         return RPMERR_BADSPEC;
816     }
817     p++;
818
819     end = p;
820     while (*end && *end != ')') {
821         end++;
822     }
823
824     if (! *end) {
825         rpmError(RPMERR_BADSPEC, "Bad %%attr() syntax: %s", buf);
826         return RPMERR_BADSPEC;
827     }
828
829     strncpy(ourbuf, p, end-p);
830     ourbuf[end-p] = '\0';
831
832     *currPmode = strtok(ourbuf, ", \n\t");
833     *currUname = strtok(NULL, ", \n\t");
834     *currGname = strtok(NULL, ", \n\t");
835
836     if (! (*currPmode && *currUname && *currGname)) {
837         rpmError(RPMERR_BADSPEC, "Bad %%attr() syntax: %s", buf);
838         *currPmode = *currUname = *currGname = NULL;
839         return RPMERR_BADSPEC;
840     }
841
842     /* Do a quick test on the mode argument */
843     if (strcmp(*currPmode, "-")) {
844         x = sscanf(*currPmode, "%o", &mode);
845         if ((x == 0) || (mode >> 12)) {
846             rpmError(RPMERR_BADSPEC, "Bad %%attr() mode spec: %s", buf);
847             *currPmode = *currUname = *currGname = NULL;
848             return RPMERR_BADSPEC;
849         }
850     }
851     
852     *currPmode = strdup(*currPmode);
853     *currUname = strdup(*currUname);
854     *currGname = strdup(*currGname);
855     
856     /* Set everything we just parsed to blank spaces */
857     while (start <= end) {
858         *start++ = ' ';
859     }
860
861     return 0;
862 }
863
864 /*************************************************************/
865 /*                                                           */
866 /* %verify parsing                                           */
867 /*                                                           */
868 /*************************************************************/
869
870 static int parseForVerify(char *buf, int *verify_flags)
871 {
872     char *p, *start, *end;
873     char ourbuf[1024];
874     int not;
875
876     if (!(p = start = strstr(buf, "%verify"))) {
877         return 0;
878     }
879
880     p += 7;
881     while (*p && (*p == ' ' || *p == '\t')) {
882         p++;
883     }
884
885     if (*p != '(') {
886         rpmError(RPMERR_BADSPEC, "Bad %%verify() syntax: %s", buf);
887         return RPMERR_BADSPEC;
888     }
889     p++;
890
891     end = p;
892     while (*end && *end != ')') {
893         end++;
894     }
895
896     if (! *end) {
897         rpmError(RPMERR_BADSPEC, "Bad %%verify() syntax: %s", buf);
898         return RPMERR_BADSPEC;
899     }
900
901     strncpy(ourbuf, p, end-p);
902     ourbuf[end-p] = '\0';
903     while (start <= end) {
904         *start++ = ' ';
905     }
906
907     p = strtok(ourbuf, ", \n\t");
908     not = 0;
909     *verify_flags = RPMVERIFY_NONE;
910     while (p) {
911         if (!strcmp(p, "not")) {
912             not = 1;
913         } else if (!strcmp(p, "md5")) {
914             *verify_flags |= RPMVERIFY_MD5;
915         } else if (!strcmp(p, "size")) {
916             *verify_flags |= RPMVERIFY_FILESIZE;
917         } else if (!strcmp(p, "link")) {
918             *verify_flags |= RPMVERIFY_LINKTO;
919         } else if (!strcmp(p, "user")) {
920             *verify_flags |= RPMVERIFY_USER;
921         } else if (!strcmp(p, "group")) {
922             *verify_flags |= RPMVERIFY_GROUP;
923         } else if (!strcmp(p, "mtime")) {
924             *verify_flags |= RPMVERIFY_MTIME;
925         } else if (!strcmp(p, "mode")) {
926             *verify_flags |= RPMVERIFY_MODE;
927         } else if (!strcmp(p, "rdev")) {
928             *verify_flags |= RPMVERIFY_RDEV;
929         } else {
930             rpmError(RPMERR_BADSPEC, "Invalid %%verify token: %s", p);
931             return RPMERR_BADSPEC;
932         }
933         p = strtok(NULL, ", \n\t");
934     }
935
936     if (not) {
937         *verify_flags = ~(*verify_flags);
938     }
939
940     return 0;
941 }
942
943 static int parseForConfig(char *buf, int *conf)
944 {
945     char *p, *start, *end;
946     char ourbuf[1024];
947
948     if (!(p = start = strstr(buf, "%config"))) {
949         return 0;
950     }
951     *conf = RPMFILE_CONFIG;
952
953     p += 7;
954     while (*p && (*p == ' ' || *p == '\t')) {
955         p++;
956     }
957
958     if (*p != '(') {
959         while (start < p) {
960             *start++ = ' ';
961         }
962         return 0;
963     }
964     p++;
965
966     end = p;
967     while (*end && *end != ')') {
968         end++;
969     }
970
971     if (! *end) {
972         rpmError(RPMERR_BADSPEC, "Bad %%config() syntax: %s", buf);
973         return RPMERR_BADSPEC;
974     }
975
976     strncpy(ourbuf, p, end-p);
977     ourbuf[end-p] = '\0';
978     while (start <= end) {
979         *start++ = ' ';
980     }
981
982     p = strtok(ourbuf, ", \n\t");
983     while (p) {
984         if (!strcmp(p, "missingok")) {
985             *conf |= RPMFILE_MISSINGOK;
986         } else if (!strcmp(p, "noreplace")) {
987             *conf |= RPMFILE_NOREPLACE;
988         } else {
989             rpmError(RPMERR_BADSPEC, "Invalid %%config token: %s", p);
990             return RPMERR_BADSPEC;
991         }
992         p = strtok(NULL, ", \n\t");
993     }
994
995     return 0;
996 }