88885bf632de9c4bd71f4aebaa6ddf7fccdb5033
[platform/upstream/rpm.git] / build / files.c
1 #include "config.h"
2 #ifdef HAVE_GLOB_H
3 #  include <glob.h>
4 #else
5 #  include "misc-glob.h"
6 #endif
7
8 #include <string.h>
9 #include <malloc.h>
10 #include <stdlib.h>
11 #include <sys/types.h>
12 #include <regex.h>
13
14 #include "spec.h"
15 #include "package.h"
16 #include "rpmlib.h"
17 #include "misc.h"
18 #include "lib/misc.h"
19 #include "myftw.h"
20 #include "lib/cpio.h"
21 #include "header.h"
22 #include "md5.h"
23 #include "names.h"
24 #include "messages.h"
25 #include "macro.h"
26 #include "build.h"
27
28 #define MAXDOCDIR 1024
29
30 struct FileListRec {
31     char *diskName; /* get file from here       */
32     char *fileName; /* filename in cpio archive */
33     mode_t mode;
34     uid_t uid;
35     gid_t gid;
36     dev_t device;
37     ino_t inode;
38     char *uname;
39     char *gname;
40     int flags;
41     int verifyFlags;
42     int size;
43     int mtime;
44     dev_t rdev;
45     char *lang;
46 };
47
48 struct AttrRec {
49     char *PmodeString;
50     int Pmode;
51     char *PdirmodeString;
52     int Pdirmode;
53     char *Uname;
54     char *Gname;
55 };
56
57 struct FileList {
58     char *buildRoot;
59     char *prefix;
60
61     int fileCount;
62     int totalFileSize;
63     int processingFailed;
64
65     int passedSpecialDoc;
66     int isSpecialDoc;
67     
68     int isDir;
69     int inFtw;
70     int currentFlags;
71     int currentVerifyFlags;
72     struct AttrRec current;
73     struct AttrRec def;
74     int defVerifyFlags;
75     char *currentLang;
76
77     /* Hard coded limit of MAXDOCDIR docdirs.         */
78     /* If you break it you are doing something wrong. */
79     char *docDirs[MAXDOCDIR];
80     int docDirCount;
81     
82     struct FileListRec *fileList;
83     int fileListRecsAlloced;
84     int fileListRecsUsed;
85 };
86
87 static int processPackageFiles(Spec spec, Package pkg,
88                                int installSpecialDoc, int test);
89 static void freeFileList(struct FileListRec *fileList, int count);
90 static int compareFileListRecs(const void *ap, const void *bp);
91 static int isDoc(struct FileList *fl, char *fileName);
92 static int processBinaryFile(Package pkg, struct FileList *fl, char *fileName);
93 static int addFile(struct FileList *fl, char *name, struct stat *statp);
94 static int parseForSimple(Spec spec, Package pkg, char *buf,
95                           struct FileList *fl, char **fileName);
96 static int parseForVerify(char *buf, struct FileList *fl);
97 static int parseForLang(char *buf, struct FileList *fl);
98 static int parseForAttr(char *buf, struct FileList *fl);
99 static int parseForConfig(char *buf, struct FileList *fl);
100 static int parseForRegexLang(char *fileName, char **lang);
101 static int myGlobPatternP(char *pattern);
102 static int glob_error(const char *foo, int bar);
103 static void timeCheck(int tc, Header h);
104 static void genCpioListAndHeader(struct FileList *fl,
105                                  struct cpioFileMapping **cpioList,
106                                  int *cpioCount, Header h, int isSrc);
107 static char *strtokWithQuotes(char *s, char *delim);
108
109 int processSourceFiles(Spec spec)
110 {
111     struct Source *srcPtr;
112     char buf[BUFSIZ];
113     StringBuf sourceFiles;
114     int x, isSpec = 1;
115     struct FileList fl;
116     char *s, **files, **fp, *fn;
117     struct stat sb;
118     HeaderIterator hi;
119     int tag, type, count;
120     Package pkg;
121     void * ptr;
122
123     sourceFiles = newStringBuf();
124     spec->sourceHeader = headerNew();
125
126     /* Only specific tags are added to the source package header */
127     hi = headerInitIterator(spec->packages->header);
128     while (headerNextIterator(hi, &tag, &type, &ptr, &count)) {
129         switch (tag) {
130           case RPMTAG_NAME:
131           case RPMTAG_VERSION:
132           case RPMTAG_RELEASE:
133           case RPMTAG_SERIAL:
134           case RPMTAG_SUMMARY:
135           case RPMTAG_DESCRIPTION:
136           case RPMTAG_PACKAGER:
137           case RPMTAG_DISTRIBUTION:
138           case RPMTAG_VENDOR:
139           case RPMTAG_COPYRIGHT:
140           case RPMTAG_GROUP:
141           case RPMTAG_OS:
142           case RPMTAG_ARCH:
143           case RPMTAG_CHANGELOGTIME:
144           case RPMTAG_CHANGELOGNAME:
145           case RPMTAG_CHANGELOGTEXT:
146           case RPMTAG_URL:
147           case HEADER_I18NTABLE:
148             headerAddEntry(spec->sourceHeader, tag, type, ptr, count);
149             break;
150           default:
151             /* do not copy */
152             break;
153         }
154         if (type == RPM_STRING_ARRAY_TYPE || type == RPM_I18NSTRING_TYPE) {
155             FREE(ptr);
156         }
157     }
158     headerFreeIterator(hi);
159
160     /* Construct the file list and source entries */
161     appendLineStringBuf(sourceFiles, spec->specFile);
162     srcPtr = spec->sources;
163     while (srcPtr) {
164         if (srcPtr->flags & RPMBUILD_ISSOURCE) {
165             headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_SOURCE,
166                                    RPM_STRING_ARRAY_TYPE, &srcPtr->source, 1);
167             if (srcPtr->flags & RPMBUILD_ISNO) {
168                 headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_NOSOURCE,
169                                        RPM_INT32_TYPE, &srcPtr->num, 1);
170             }
171         }
172         if (srcPtr->flags & RPMBUILD_ISPATCH) {
173             headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_PATCH,
174                                    RPM_STRING_ARRAY_TYPE, &srcPtr->source, 1);
175             if (srcPtr->flags & RPMBUILD_ISNO) {
176                 headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_NOPATCH,
177                                        RPM_INT32_TYPE, &srcPtr->num, 1);
178             }
179         }
180         sprintf(buf, "%s%s/%s",
181                 srcPtr->flags & RPMBUILD_ISNO ? "!" : "",
182                 rpmGetVar(RPMVAR_SOURCEDIR), srcPtr->source);
183         appendLineStringBuf(sourceFiles, buf);
184         srcPtr = srcPtr->next;
185     }
186
187     pkg = spec->packages;
188     while (pkg) {
189         srcPtr = pkg->icon;
190         while (srcPtr) {
191             sprintf(buf, "%s%s/%s",
192                     srcPtr->flags & RPMBUILD_ISNO ? "!" : "",
193                     rpmGetVar(RPMVAR_SOURCEDIR), srcPtr->source);
194             appendLineStringBuf(sourceFiles, buf);
195             srcPtr = srcPtr->next;
196         }
197         pkg = pkg->next;
198     }
199
200     spec->sourceCpioList = NULL;
201     spec->sourceCpioCount = 0;
202
203     fl.fileList = malloc((spec->numSources + 1) * sizeof(struct FileListRec));
204     fl.processingFailed = 0;
205     fl.fileListRecsUsed = 0;
206     fl.totalFileSize = 0;
207     fl.prefix = NULL;
208
209     s = getStringBuf(sourceFiles);
210     files = splitString(s, strlen(s), '\n');
211     fp = files;
212
213     /* The first source file is the spec file */
214     x = 0;
215     while (*fp) {
216         s = *fp;
217         SKIPSPACE(s);
218         if (! *s) {
219             fp++; continue;
220         }
221
222         fl.fileList[x].flags = isSpec ? RPMFILE_SPECFILE : 0;
223         /* files with leading ! are no source files */
224         if (*s == '!') {
225             fl.fileList[x].flags |= RPMFILE_GHOST;
226             s++;
227         }
228         fl.fileList[x].diskName = strdup(s);
229         fn = strrchr(s, '/');
230         if (fn) {
231             fn++;
232         } else {
233             fn = s;
234         }
235         fl.fileList[x].fileName = strdup(fn);
236         fl.fileList[x].verifyFlags = RPMVERIFY_ALL;
237         lstat(s, &sb);
238         fl.fileList[x].mode = sb.st_mode;
239         fl.fileList[x].uid = sb.st_uid;
240         fl.fileList[x].gid = sb.st_gid;
241         fl.fileList[x].uname = getUname(sb.st_uid);
242         fl.fileList[x].gname = getGname(sb.st_gid);
243         fl.fileList[x].size = sb.st_size;
244         fl.fileList[x].mtime = sb.st_mtime;
245         fl.fileList[x].rdev = sb.st_rdev;
246         fl.fileList[x].device = sb.st_dev;
247         fl.fileList[x].inode = sb.st_ino;
248         fl.fileList[x].lang = strdup("");
249         
250         fl.totalFileSize += sb.st_size;
251         
252         if (! (fl.fileList[x].uname && fl.fileList[x].gname)) {
253             rpmError(RPMERR_BADSPEC, "Bad owner/group: %s", s);
254             fl.processingFailed = 1;
255         }
256
257         isSpec = 0;
258         fp++;
259         x++;
260     }
261     fl.fileListRecsUsed = x;
262     freeSplitString(files);
263
264     if (! fl.processingFailed) {
265         genCpioListAndHeader(&fl, &(spec->sourceCpioList),
266                              &(spec->sourceCpioCount), spec->sourceHeader, 1);
267     }
268
269     freeStringBuf(sourceFiles);
270     freeFileList(fl.fileList, fl.fileListRecsUsed);
271     return fl.processingFailed;
272 }
273
274 int processBinaryFiles(Spec spec, int installSpecialDoc, int test)
275 {
276     Package pkg;
277     int res, rc;
278     char *name;
279     
280     pkg = spec->packages;
281     res = 0;
282     while (pkg) {
283         if (!pkg->fileList) {
284             pkg = pkg->next;
285             continue;
286         }
287
288         headerGetEntry(pkg->header, RPMTAG_NAME, NULL, (void **)&name, NULL);
289         rpmMessage(RPMMESS_NORMAL, "Processing files: %s\n", name);
290                    
291         if ((rc = processPackageFiles(spec, pkg, installSpecialDoc, test))) {
292             res = rc;
293         }
294
295         generateAutoReqProv(spec, pkg, pkg->cpioList, pkg->cpioCount);
296         printReqs(spec, pkg);
297         
298         pkg = pkg->next;
299     }
300
301     return res;
302 }
303
304 static int processPackageFiles(Spec spec, Package pkg,
305                                int installSpecialDoc, int test)
306 {
307     struct FileList fl;
308     char *s, **files, **fp, *fileName;
309     char buf[BUFSIZ];
310     FILE *f;
311
312     struct AttrRec specialDocAttrRec;
313     char *specialDoc = NULL;
314     
315     pkg->cpioList = NULL;
316     pkg->cpioCount = 0;
317
318     if (pkg->fileFile) {
319         sprintf(buf, "%s/%s/%s", rpmGetVar(RPMVAR_BUILDDIR),
320                 spec->buildSubdir, pkg->fileFile);
321         if ((f = fopen(buf, "r")) == NULL) {
322             rpmError(RPMERR_BADFILENAME,
323                      "Could not open %%files file: %s", pkg->fileFile);
324             return RPMERR_BADFILENAME;
325         }
326         while (fgets(buf, sizeof(buf), f)) {
327             handleComments(buf);
328             if (expandMacros(&spec->macros, buf)) {
329                 rpmError(RPMERR_BADSPEC, "line: %s", buf);
330                 return RPMERR_BADSPEC;
331             }
332             appendStringBuf(pkg->fileList, buf);
333         }
334         fclose(f);
335     }
336     
337     /* Init the file list structure */
338     
339     fl.buildRoot = spec->buildRoot ? spec->buildRoot : "";
340     if (headerGetEntry(pkg->header, RPMTAG_DEFAULTPREFIX,
341                        NULL, (void *)&fl.prefix, NULL)) {
342         fl.prefix = strdup(fl.prefix);
343     } else {
344         fl.prefix = NULL;
345     }
346
347     fl.fileCount = 0;
348     fl.totalFileSize = 0;
349     fl.processingFailed = 0;
350
351     fl.passedSpecialDoc = 0;
352     
353     fl.current.PmodeString = NULL;
354     fl.current.PdirmodeString = NULL;
355     fl.current.Uname = NULL;
356     fl.current.Gname = NULL;
357     fl.def.PmodeString = NULL;
358     fl.def.PdirmodeString = NULL;
359     fl.def.Uname = NULL;
360     fl.def.Gname = NULL;
361     fl.def.Pmode = 0;
362     fl.def.Pdirmode = 0;
363     fl.currentLang = NULL;
364
365     fl.defVerifyFlags = RPMVERIFY_ALL;
366
367     fl.docDirCount = 0;
368     fl.docDirs[fl.docDirCount++] = strdup("/usr/doc");
369     fl.docDirs[fl.docDirCount++] = strdup("/usr/man");
370     fl.docDirs[fl.docDirCount++] = strdup("/usr/info");
371     fl.docDirs[fl.docDirCount++] = strdup("/usr/X11R6/man");
372     fl.docDirs[fl.docDirCount++] = strdup(spec->docDir);
373     
374     fl.fileList = NULL;
375     fl.fileListRecsAlloced = 0;
376     fl.fileListRecsUsed = 0;
377
378     s = getStringBuf(pkg->fileList);
379     files = splitString(s, strlen(s), '\n');
380     fp = files;
381
382     while (*fp) {
383         s = *fp;
384         SKIPSPACE(s);
385         if (! *s) {
386             fp++; continue;
387         }
388         fileName = NULL;
389         strcpy(buf, s);
390         
391         /* Reset for a new line in %files */
392         fl.isDir = 0;
393         fl.inFtw = 0;
394         fl.currentFlags = 0;
395         fl.currentVerifyFlags = fl.defVerifyFlags;
396         fl.current.Pmode = fl.def.Pmode;
397         fl.current.Pdirmode = fl.def.Pdirmode;
398         fl.isSpecialDoc = 0;
399         FREE(fl.current.PmodeString);
400         FREE(fl.current.PdirmodeString);
401         FREE(fl.current.Uname);
402         FREE(fl.current.Gname);
403         FREE(fl.currentLang);
404         if (fl.def.PmodeString) {
405             fl.current.PmodeString = strdup(fl.def.PmodeString);
406         }
407         if (fl.def.PdirmodeString) {
408             fl.current.PdirmodeString = strdup(fl.def.PdirmodeString);
409         }
410         if (fl.def.Uname) {
411             fl.current.Uname = strdup(fl.def.Uname);
412         }
413         if (fl.def.Gname) {
414             fl.current.Gname = strdup(fl.def.Gname);
415         }
416
417         if (parseForVerify(buf, &fl)) {
418             fp++; continue;
419         }
420         if (parseForAttr(buf, &fl)) {
421             fp++; continue;
422         }
423         if (parseForConfig(buf, &fl)) {
424             fp++; continue;
425         }
426         if (parseForLang(buf, &fl)) {
427             fp++; continue;
428         }
429         if (parseForSimple(spec, pkg, buf, &fl, &fileName)) {
430             fp++; continue;
431         }
432         if (! fileName) {
433             fp++; continue;
434         }
435
436         if (fl.isSpecialDoc) {
437             /* Save this stuff for last */
438             specialDoc = strdup(fileName);
439             specialDocAttrRec = fl.current;
440             if (specialDocAttrRec.PmodeString) {
441                 specialDocAttrRec.PmodeString =
442                     strdup(specialDocAttrRec.PmodeString);
443             }
444             if (specialDocAttrRec.PdirmodeString) {
445                 specialDocAttrRec.PdirmodeString =
446                     strdup(specialDocAttrRec.PdirmodeString);
447             }
448             if (specialDocAttrRec.Uname) {
449                 specialDocAttrRec.Uname = strdup(specialDocAttrRec.Uname);
450             }
451             if (specialDocAttrRec.Gname) {
452                 specialDocAttrRec.Gname = strdup(specialDocAttrRec.Gname);
453             }
454         } else {
455             processBinaryFile(pkg, &fl, fileName);
456         }
457         
458         fp++;
459     }
460
461     /* Now process special doc, if there is one */
462     if (specialDoc) {
463         if (installSpecialDoc) {
464             doScript(spec, RPMBUILD_STRINGBUF, "%doc", pkg->specialDoc, test);
465         }
466
467         /* fl.current now takes on "ownership" of the specialDocAttrRec */
468         /* allocated string data.                                       */
469         fl.current = specialDocAttrRec;
470         processBinaryFile(pkg, &fl, specialDoc);
471         FREE(specialDoc);
472     }
473     
474     freeSplitString(files);
475
476     if (! fl.processingFailed) {
477         genCpioListAndHeader(&fl, &(pkg->cpioList), &(pkg->cpioCount),
478                              pkg->header, 0);
479
480         if (spec->timeCheck) {
481             timeCheck(spec->timeCheck, pkg->header);
482         }
483     }
484     
485     /* Clean up */
486     FREE(fl.prefix);
487     FREE(fl.current.PmodeString);
488     FREE(fl.current.PdirmodeString);
489     FREE(fl.current.Uname);
490     FREE(fl.current.Gname);
491     FREE(fl.def.PmodeString);
492     FREE(fl.def.PdirmodeString);
493     FREE(fl.def.Uname);
494     FREE(fl.def.Gname);
495     FREE(fl.currentLang);
496     freeFileList(fl.fileList, fl.fileListRecsUsed);
497     while (fl.docDirCount--) {
498         FREE(fl.docDirs[fl.docDirCount]);
499     }
500     return fl.processingFailed;
501 }
502
503 static void timeCheck(int tc, Header h)
504 {
505     int *mtime;
506     char **file;
507     int count, x, currentTime;
508
509     headerGetEntry(h, RPMTAG_FILENAMES, NULL, (void **) &file, &count);
510     headerGetEntry(h, RPMTAG_FILEMTIMES, NULL, (void **) &mtime, NULL);
511
512     currentTime = time(NULL);
513     
514     x = 0;
515     while (x < count) {
516         if (currentTime - mtime[x] > tc) {
517             rpmMessage(RPMMESS_WARNING, "TIMECHECK failure: %s\n", file[x]);
518         }
519         x++;
520     }
521 }
522
523 static void genCpioListAndHeader(struct FileList *fl,
524                                  struct cpioFileMapping **cpioList,
525                                  int *cpioCount, Header h, int isSrc)
526 {
527     int skipLen = 0;
528     struct FileListRec *p;
529     int count;
530     struct cpioFileMapping *cpioListPtr;
531     char *s, buf[BUFSIZ];
532     
533     /* Sort the big list */
534     qsort(fl->fileList, fl->fileListRecsUsed,
535           sizeof(*(fl->fileList)), compareFileListRecs);
536     
537     /* Generate the cpio list and the header */
538     if (! isSrc) {
539         if (fl->prefix) {
540             skipLen = 1 + strlen(fl->prefix);
541         } else {
542             skipLen = 1;
543         }
544     }
545     *cpioList = malloc(sizeof(**cpioList) * fl->fileListRecsUsed);
546     *cpioCount = 0;
547     cpioListPtr = *cpioList;
548     p = fl->fileList;
549     count = fl->fileListRecsUsed;
550     while (count) {
551         if ((count > 1) && !strcmp(p->fileName, p[1].fileName)) {
552             rpmError(RPMERR_BADSPEC, "File listed twice: %s", p->fileName);
553             fl->processingFailed = 1;
554         }
555         
556         /* Make the cpio list */
557         if (! (p->flags & RPMFILE_GHOST)) {
558             cpioListPtr->fsPath = strdup(p->diskName);
559             cpioListPtr->archivePath = strdup(p->fileName + skipLen);
560             cpioListPtr->finalMode = p->mode;
561             cpioListPtr->finalUid = p->uid;
562             cpioListPtr->finalGid = p->gid;
563             cpioListPtr->mapFlags = CPIO_MAP_PATH | CPIO_MAP_MODE |
564                 CPIO_MAP_UID | CPIO_MAP_GID;
565             cpioListPtr++;
566             (*cpioCount)++;
567         }
568         
569         /* Make the header */
570         headerAddOrAppendEntry(h, RPMTAG_FILENAMES, RPM_STRING_ARRAY_TYPE,
571                                &(p->fileName), 1);
572         headerAddOrAppendEntry(h, RPMTAG_FILESIZES, RPM_INT32_TYPE,
573                                &(p->size), 1);
574         headerAddOrAppendEntry(h, RPMTAG_FILEUSERNAME, RPM_STRING_ARRAY_TYPE,
575                                &(p->uname), 1);
576         headerAddOrAppendEntry(h, RPMTAG_FILEGROUPNAME, RPM_STRING_ARRAY_TYPE,
577                                &(p->gname), 1);
578         headerAddOrAppendEntry(h, RPMTAG_FILEMTIMES, RPM_INT32_TYPE,
579                                &(p->mtime), 1);
580     if (sizeof(p->mode) != sizeof(uint_16)) {
581         uint_16 pmode = (uint_16)p->mode;
582         headerAddOrAppendEntry(h, RPMTAG_FILEMODES, RPM_INT16_TYPE,
583                                &(pmode), 1);
584     } else {
585         headerAddOrAppendEntry(h, RPMTAG_FILEMODES, RPM_INT16_TYPE,
586                                &(p->mode), 1);
587     }
588     if (sizeof(p->rdev) != sizeof(uint_16)) {
589         uint_16 prdev = (uint_16)p->rdev;
590         headerAddOrAppendEntry(h, RPMTAG_FILERDEVS, RPM_INT16_TYPE,
591                                &(prdev), 1);
592     } else {
593         headerAddOrAppendEntry(h, RPMTAG_FILERDEVS, RPM_INT16_TYPE,
594                                &(p->rdev), 1);
595     }
596     if (sizeof(p->device) != sizeof(uint_32)) {
597         uint_32 pdevice = (uint_32)p->device;
598         headerAddOrAppendEntry(h, RPMTAG_FILEDEVICES, RPM_INT32_TYPE,
599                                &(pdevice), 1);
600     } else {
601         headerAddOrAppendEntry(h, RPMTAG_FILEDEVICES, RPM_INT32_TYPE,
602                                &(p->device), 1);
603     }
604         headerAddOrAppendEntry(h, RPMTAG_FILEINODES, RPM_INT32_TYPE,
605                                &(p->inode), 1);
606         headerAddOrAppendEntry(h, RPMTAG_FILELANGS, RPM_STRING_ARRAY_TYPE,
607                                &(p->lang), 1);
608         
609         /* We used to add these, but they should not be needed */
610         /* headerAddOrAppendEntry(h, RPMTAG_FILEUIDS,
611          *                 RPM_INT32_TYPE, &(p->uid), 1);
612          * headerAddOrAppendEntry(h, RPMTAG_FILEGIDS,
613          *                 RPM_INT32_TYPE, &(p->gid), 1);
614          */
615         
616         buf[0] = '\0';
617         s = buf;
618         if (S_ISREG(p->mode)) {
619             mdfile(p->diskName, buf);
620         }
621         headerAddOrAppendEntry(h, RPMTAG_FILEMD5S, RPM_STRING_ARRAY_TYPE,
622                                &s, 1);
623         
624         buf[0] = '\0';
625         s = buf;
626         if (S_ISLNK(p->mode)) {
627             buf[readlink(p->diskName, buf, BUFSIZ)] = '\0';
628         }
629         headerAddOrAppendEntry(h, RPMTAG_FILELINKTOS, RPM_STRING_ARRAY_TYPE,
630                                &s, 1);
631         
632         if (p->flags & RPMFILE_GHOST) {
633             p->verifyFlags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE |
634                                 RPMVERIFY_LINKTO | RPMVERIFY_MTIME);
635         }
636         headerAddOrAppendEntry(h, RPMTAG_FILEVERIFYFLAGS, RPM_INT32_TYPE,
637                                &(p->verifyFlags), 1);
638         
639         if (!isSrc && isDoc(fl, p->fileName)) {
640             p->flags |= RPMFILE_DOC;
641         }
642         if (p->mode & S_IFDIR) {
643             p->flags &= ~RPMFILE_CONFIG;
644             p->flags &= ~RPMFILE_DOC;
645         }
646         headerAddOrAppendEntry(h, RPMTAG_FILEFLAGS, RPM_INT32_TYPE,
647                                &(p->flags), 1);
648         
649         p++;
650         count--;
651     }
652     headerAddEntry(h, RPMTAG_SIZE, RPM_INT32_TYPE,
653                    &(fl->totalFileSize), 1);
654 }
655
656 static void freeFileList(struct FileListRec *fileList, int count)
657 {
658     while (count--) {
659         FREE(fileList[count].diskName);
660         FREE(fileList[count].fileName);
661         FREE(fileList[count].lang);
662     }
663     FREE(fileList);
664 }
665
666 void freeCpioList(struct cpioFileMapping *cpioList, int cpioCount)
667 {
668     struct cpioFileMapping *p = cpioList;
669
670     while (cpioCount--) {
671         rpmMessage(RPMMESS_DEBUG, "archive = %s, fs = %s\n",
672                    p->archivePath, p->fsPath);
673         FREE(p->archivePath);
674         FREE(p->fsPath);
675         p++;
676     }
677     FREE(cpioList);
678 }
679
680 static int compareFileListRecs(const void *ap, const void *bp)
681 {
682     char *a, *b;
683
684     a = ((struct FileListRec *)ap)->fileName;
685     b = ((struct FileListRec *)bp)->fileName;
686
687     return strcmp(a, b);
688 }
689
690 static int isDoc(struct FileList *fl, char *fileName)
691 {
692     int x = fl->docDirCount;
693
694     while (x--) {
695         if (strstr(fileName, fl->docDirs[x]) == fileName) {
696             return 1;
697         }
698     }
699     return 0;
700 }
701
702 static int processBinaryFile(Package pkg, struct FileList *fl, char *fileName)
703 {
704     char fullname[BUFSIZ];
705     glob_t glob_result;
706     int x, offset, rc = 0;
707     
708     /* check that file starts with leading "/" */
709     if (*fileName != '/') {
710         rpmError(RPMERR_BADSPEC, "File needs leading \"/\": %s", *fileName);
711         fl->processingFailed = 1;
712         return 1;
713     }
714     
715     if (myGlobPatternP(fileName)) {
716         if (fl->buildRoot) {
717             sprintf(fullname, "%s%s", fl->buildRoot, fileName);
718             offset = strlen(fl->buildRoot);
719         } else {
720             strcpy(fullname, fileName);
721             offset = 0;
722         }
723         
724         if (glob(fullname, 0, glob_error, &glob_result) ||
725             (glob_result.gl_pathc < 1)) {
726             rpmError(RPMERR_BADSPEC, "File not found: %s", fullname);
727             fl->processingFailed = 1;
728             globfree(&glob_result);
729             return 1;
730         }
731         
732         x = 0;
733         while (x < glob_result.gl_pathc) {
734             rc = addFile(fl, &(glob_result.gl_pathv[x][offset]), NULL);
735             x++;
736         }
737         globfree(&glob_result);
738     } else {
739         rc = addFile(fl, fileName, NULL);
740     }
741
742     return rc;
743 }
744
745 static int addFile(struct FileList *fl, char *name, struct stat *statp)
746 {
747     char fileName[BUFSIZ];
748     char diskName[BUFSIZ];
749     char *prefixTest, *prefixPtr;
750     struct stat statbuf;
751     int_16 fileMode;
752     int fileUid, fileGid;
753     char *fileUname, *fileGname;
754     char *lang;
755     
756     strcpy(fileName, cleanFileName(name));
757
758     if (fl->inFtw) {
759         /* Any buildRoot is already prepended */
760         strcpy(diskName, fileName);
761         if (fl->buildRoot) {
762             strcpy(fileName, diskName + strlen(fl->buildRoot));
763         }
764     } else {
765         if (fl->buildRoot) {
766             sprintf(diskName, "%s%s", fl->buildRoot, fileName);
767         } else {
768             strcpy(diskName, fileName);
769         }
770     }
771
772     /* If we are using a prefix, validate the file */
773     if (!fl->inFtw && fl->prefix) {
774         prefixTest = fileName;
775         prefixPtr = fl->prefix;
776
777         while (*prefixPtr && *prefixTest && (*prefixTest == *prefixPtr)) {
778             prefixPtr++;
779             prefixTest++;
780         }
781         if (*prefixPtr || (*prefixTest && *prefixTest != '/')) {
782             rpmError(RPMERR_BADSPEC, "File doesn't match prefix (%s): %s",
783                      fl->prefix, fileName);
784             fl->processingFailed = 1;
785             return RPMERR_BADSPEC;
786         }
787     }
788
789     if (! statp) {
790         statp = &statbuf;
791         if (lstat(diskName, statp)) {
792             rpmError(RPMERR_BADSPEC, "File not found: %s", diskName);
793             fl->processingFailed = 1;
794             return RPMERR_BADSPEC;
795         }
796     }
797
798     if ((! fl->isDir) && S_ISDIR(statp->st_mode)) {
799         /* We use our own ftw() call, because ftw() uses stat()    */
800         /* instead of lstat(), which causes it to follow symlinks! */
801         /* It also has better callback support.                    */
802         
803         fl->inFtw = 1;  /* Flag to indicate file has buildRoot prefixed */
804         fl->isDir = 1;  /* Keep it from following myftw() again         */
805         myftw(diskName, 16, (myftwFunc) addFile, fl);
806         fl->isDir = 0;
807         fl->inFtw = 0;
808     } else {
809         fileMode = statp->st_mode;
810         fileUid = statp->st_uid;
811         fileGid = statp->st_gid;
812         
813         /* %attr ? */
814         if (S_ISDIR(fileMode) && fl->current.PdirmodeString) {
815             if (fl->current.PdirmodeString[0] != '-') {
816                 fileMode &= S_IFMT;
817                 fileMode |= fl->current.Pdirmode;
818             }
819         } else {
820             if (fl->current.PmodeString) {
821                 fileMode &= S_IFMT;
822                 fileMode |= fl->current.Pmode;
823             }
824         }
825         if (fl->current.Uname) {
826             fileUname = getUnameS(fl->current.Uname);
827         } else {
828             fileUname = getUname(fileUid);
829         }
830         if (fl->current.Gname) {
831             fileGname = getGnameS(fl->current.Gname);
832         } else {
833             fileGname = getGname(fileGid);
834         }
835         
836         if (! (fileUname && fileGname)) {
837             rpmError(RPMERR_BADSPEC, "Bad owner/group: %s\n", diskName);
838             fl->processingFailed = 1;
839             return RPMERR_BADSPEC;
840         }
841     
842         rpmMessage(RPMMESS_DEBUG, "File %d: %s\n", fl->fileCount, fileName);
843
844         /* Add to the file list */
845         if (fl->fileListRecsUsed == fl->fileListRecsAlloced) {
846             fl->fileListRecsAlloced += 128;
847             fl->fileList = realloc(fl->fileList,
848                                    fl->fileListRecsAlloced *
849                                    sizeof(*(fl->fileList)));
850         }
851             
852         fl->fileList[fl->fileListRecsUsed].fileName = strdup(fileName);
853         fl->fileList[fl->fileListRecsUsed].diskName = strdup(diskName);
854         fl->fileList[fl->fileListRecsUsed].mode = fileMode;
855         fl->fileList[fl->fileListRecsUsed].uid = fileUid;
856         fl->fileList[fl->fileListRecsUsed].gid = fileGid;
857         fl->fileList[fl->fileListRecsUsed].uname = fileUname;
858         fl->fileList[fl->fileListRecsUsed].gname = fileGname;
859
860         if (fl->currentLang) {
861             fl->fileList[fl->fileListRecsUsed].lang = strdup(fl->currentLang);
862         } else if (! parseForRegexLang(fileName, &lang)) {
863             fl->fileList[fl->fileListRecsUsed].lang = strdup(lang);
864         } else {
865             fl->fileList[fl->fileListRecsUsed].lang = strdup("");
866         }
867
868         fl->fileList[fl->fileListRecsUsed].flags = fl->currentFlags;
869         fl->fileList[fl->fileListRecsUsed].verifyFlags =
870             fl->currentVerifyFlags;
871         fl->fileList[fl->fileListRecsUsed].size = statp->st_size;
872         fl->fileList[fl->fileListRecsUsed].mtime = statp->st_mtime;
873         fl->fileList[fl->fileListRecsUsed].rdev = statp->st_rdev;
874         fl->fileList[fl->fileListRecsUsed].device = statp->st_dev;
875         fl->fileList[fl->fileListRecsUsed].inode = statp->st_ino;
876         fl->fileListRecsUsed++;
877
878         fl->totalFileSize += statp->st_size;
879         fl->fileCount++;
880     }
881
882     return 0;
883 }
884
885 static int parseForSimple(Spec spec, Package pkg, char *buf,
886                           struct FileList *fl, char **fileName)
887 {
888     char *s;
889     int res, specialDoc = 0;
890     char *name, *version;
891     char specialDocBuf[BUFSIZ];
892
893     specialDocBuf[0] = '\0';
894     *fileName = NULL;
895     res = 0;
896     s = strtokWithQuotes(buf, " \t\n");
897     while (s) {
898         if (!strcmp(s, "%docdir")) {
899             s = strtokWithQuotes(NULL, " \t\n");
900             if (fl->docDirCount == MAXDOCDIR) {
901                 rpmError(RPMERR_INTERNAL, "Hit limit for %%docdir");
902                 fl->processingFailed = 1;
903                 res = 1;
904             }
905             fl->docDirs[fl->docDirCount++] = strdup(s);
906             if (strtokWithQuotes(NULL, " \t\n")) {
907                 rpmError(RPMERR_INTERNAL, "Only one arg for %%docdir");
908                 fl->processingFailed = 1;
909                 res = 1;
910             }
911             break;
912         } else if (!strcmp(s, "%doc")) {
913             fl->currentFlags |= RPMFILE_DOC;
914         } else if (!strcmp(s, "%ghost")) {
915             fl->currentFlags |= RPMFILE_GHOST;
916         } else if (!strcmp(s, "%dir")) {
917             fl->isDir = 1;
918         } else {
919             if (*fileName) {
920                 /* We already got a file -- error */
921                 rpmError(RPMERR_BADSPEC,
922                          "Two files on one line: %s", *fileName);
923                 fl->processingFailed = 1;
924                 res = 1;
925             }
926             if (*s != '/') {
927                 if (fl->currentFlags & RPMFILE_DOC) {
928                     specialDoc = 1;
929                     strcat(specialDocBuf, " ");
930                     strcat(specialDocBuf, s);
931                 } else {
932                     /* not in %doc, does not begin with / -- error */
933                     rpmError(RPMERR_BADSPEC,
934                              "File must begin with \"/\": %s", s);
935                     fl->processingFailed = 1;
936                     res = 1;
937                 }
938             } else {
939                 *fileName = s;
940             }
941         }
942         s = strtokWithQuotes(NULL, " \t\n");
943     }
944
945     if (specialDoc) {
946         if (*fileName || (fl->currentFlags & ~(RPMFILE_DOC))) {
947             rpmError(RPMERR_BADSPEC,
948                      "Can't mix special %%doc with other forms: %s",
949                      *fileName);
950             fl->processingFailed = 1;
951             res = 1;
952         } else {
953             headerGetEntry(pkg->header, RPMTAG_NAME, NULL,
954                            (void *) &name, NULL);
955             headerGetEntry(pkg->header, RPMTAG_VERSION, NULL,
956                            (void *) &version, NULL);
957             sprintf(buf, "%s/%s-%s", spec->docDir, name, version);
958
959             if (! fl->passedSpecialDoc) {
960                 pkg->specialDoc = newStringBuf();
961                 appendStringBuf(pkg->specialDoc, "DOCDIR=$RPM_BUILD_ROOT");
962                 appendLineStringBuf(pkg->specialDoc, buf);
963                 appendLineStringBuf(pkg->specialDoc, "export DOCDIR");
964                 appendLineStringBuf(pkg->specialDoc, "rm -rf $DOCDIR");
965                 appendLineStringBuf(pkg->specialDoc, MKDIR_P " $DOCDIR");
966
967                 *fileName = buf;
968                 fl->passedSpecialDoc = 1;
969                 fl->isSpecialDoc = 1;
970             }
971
972             appendStringBuf(pkg->specialDoc, "cp -pr ");
973             appendStringBuf(pkg->specialDoc, specialDocBuf);
974             appendLineStringBuf(pkg->specialDoc, " $DOCDIR");
975         }
976     }
977
978     return res;
979 }
980
981 static int parseForVerify(char *buf, struct FileList *fl)
982 {
983     char *p, *start, *end, *name;
984     char ourbuf[BUFSIZ];
985     int not, verifyFlags;
986     int *resultVerify;
987
988     if (!(p = start = strstr(buf, "%verify"))) {
989         if (!(p = start = strstr(buf, "%defverify"))) {
990             return 0;
991         }
992         name = "%defverify";
993         resultVerify = &(fl->defVerifyFlags);
994         p += 10;
995     } else {
996         name = "%verify";
997         resultVerify = &(fl->currentVerifyFlags);
998         p += 7;
999     }
1000
1001     SKIPSPACE(p);
1002
1003     if (*p != '(') {
1004         rpmError(RPMERR_BADSPEC, "Bad %s() syntax: %s", name, buf);
1005         fl->processingFailed = 1;
1006         return RPMERR_BADSPEC;
1007     }
1008     p++;
1009
1010     end = p;
1011     while (*end && *end != ')') {
1012         end++;
1013     }
1014
1015     if (! *end) {
1016         rpmError(RPMERR_BADSPEC, "Bad %s() syntax: %s", name, buf);
1017         fl->processingFailed = 1;
1018         return RPMERR_BADSPEC;
1019     }
1020
1021     strncpy(ourbuf, p, end-p);
1022     ourbuf[end-p] = '\0';
1023     while (start <= end) {
1024         *start++ = ' ';
1025     }
1026
1027     p = strtok(ourbuf, ", \n\t");
1028     not = 0;
1029     verifyFlags = RPMVERIFY_NONE;
1030     while (p) {
1031         if (!strcmp(p, "not")) {
1032             not = 1;
1033         } else if (!strcmp(p, "md5")) {
1034             verifyFlags |= RPMVERIFY_MD5;
1035         } else if (!strcmp(p, "size")) {
1036             verifyFlags |= RPMVERIFY_FILESIZE;
1037         } else if (!strcmp(p, "link")) {
1038             verifyFlags |= RPMVERIFY_LINKTO;
1039         } else if (!strcmp(p, "user")) {
1040             verifyFlags |= RPMVERIFY_USER;
1041         } else if (!strcmp(p, "group")) {
1042             verifyFlags |= RPMVERIFY_GROUP;
1043         } else if (!strcmp(p, "mtime")) {
1044             verifyFlags |= RPMVERIFY_MTIME;
1045         } else if (!strcmp(p, "mode")) {
1046             verifyFlags |= RPMVERIFY_MODE;
1047         } else if (!strcmp(p, "rdev")) {
1048             verifyFlags |= RPMVERIFY_RDEV;
1049         } else {
1050             rpmError(RPMERR_BADSPEC, "Invalid %s token: %s", name, p);
1051             fl->processingFailed = 1;
1052             return RPMERR_BADSPEC;
1053         }
1054         p = strtok(NULL, ", \n\t");
1055     }
1056
1057     *resultVerify = not ? ~(verifyFlags) : verifyFlags;
1058
1059     return 0;
1060 }
1061
1062 static int parseForRegexLang(char *fileName, char **lang)
1063 {
1064     static int initialized = 0;
1065     static int hasRegex = 0;
1066     static regex_t compiledPatt;
1067     static char buf[BUFSIZ];
1068     int x;
1069     regmatch_t matches[2];
1070     char *patt, *s;
1071
1072     if (! initialized) {
1073         initialized = 1;
1074         patt = rpmGetVar(RPMVAR_LANGPATT);
1075         if (! patt) {
1076             return 1;
1077         }
1078         if (regcomp(&compiledPatt, patt, REG_EXTENDED)) {
1079             return -1;
1080         }
1081         hasRegex = 1;
1082     }
1083     
1084     if (! hasRegex) {
1085         return 1;
1086     }
1087
1088     if (! regexec(&compiledPatt, fileName, 2, matches, REG_NOTEOL)) {
1089         /* Got match */
1090         s = fileName + matches[1].rm_eo - 1;
1091         x = matches[1].rm_eo - matches[1].rm_so;
1092         buf[x] = '\0';
1093         while (x) {
1094             buf[--x] = *s--;
1095         }
1096         *lang = buf;
1097         return 0;
1098     }
1099
1100     return 1;
1101 }
1102
1103 static int parseForLang(char *buf, struct FileList *fl)
1104 {
1105     char *p, *start, *end;
1106     char ourbuf[1024];
1107
1108     if (!(p = start = strstr(buf, "%lang"))) {
1109         return 0;
1110     }
1111
1112     p += 5;
1113     SKIPSPACE(p);
1114
1115     if (*p != '(') {
1116         rpmError(RPMERR_BADSPEC, "Bad %%lang() syntax: %s", buf);
1117         fl->processingFailed = 1;
1118         return RPMERR_BADSPEC;
1119     }
1120     p++;
1121
1122     end = p;
1123     while (*end && *end != ')') {
1124         end++;
1125     }
1126
1127     if (! *end) {
1128         rpmError(RPMERR_BADSPEC, "Bad %%lang() syntax: %s", buf);
1129         fl->processingFailed = 1;
1130         return RPMERR_BADSPEC;
1131     }
1132
1133     strncpy(ourbuf, p, end-p);
1134     ourbuf[end-p] = '\0';
1135     while (start <= end) {
1136         *start++ = ' ';
1137     }
1138
1139     p = strtok(ourbuf, ", \n\t");
1140     if (!p) {
1141         rpmError(RPMERR_BADSPEC, "Bad %%lang() syntax: %s", buf);
1142         fl->processingFailed = 1;
1143         return RPMERR_BADSPEC;
1144     }
1145     if (strlen(p) != 2) {
1146         rpmError(RPMERR_BADSPEC, "%%lang() entries are 2 characters: %s", buf);
1147         fl->processingFailed = 1;
1148         return RPMERR_BADSPEC;
1149     }
1150     if (strtok(NULL, ", \n\t")) {
1151         rpmError(RPMERR_BADSPEC, "Only one entry in %%lang(): %s", buf);
1152         fl->processingFailed = 1;
1153         return RPMERR_BADSPEC;
1154     }
1155     fl->currentLang = strdup(p);
1156     
1157     return 0;
1158 }
1159
1160 static int parseForAttr(char *buf, struct FileList *fl)
1161 {
1162     char *p, *s, *start, *end, *name;
1163     char ourbuf[1024];
1164     int x, defattr = 0;
1165     struct AttrRec *resultAttr;
1166
1167     if (!(p = start = strstr(buf, "%attr"))) {
1168         if (!(p = start = strstr(buf, "%defattr"))) {
1169             return 0;
1170         }
1171         defattr = 1;
1172         name = "%defattr";
1173         resultAttr = &(fl->def);
1174         p += 8;
1175     } else {
1176         name = "%attr";
1177         resultAttr = &(fl->current);
1178         p += 5;
1179     }
1180
1181     resultAttr->PmodeString = resultAttr->Uname = resultAttr->Gname = NULL;
1182
1183     SKIPSPACE(p);
1184
1185     if (*p != '(') {
1186         rpmError(RPMERR_BADSPEC, "Bad %s() syntax: %s", name, buf);
1187         fl->processingFailed = 1;
1188         return RPMERR_BADSPEC;
1189     }
1190     p++;
1191
1192     end = p;
1193     while (*end && *end != ')') {
1194         end++;
1195     }
1196
1197     if (! *end) {
1198         rpmError(RPMERR_BADSPEC, "Bad %s() syntax: %s", name, buf);
1199         fl->processingFailed = 1;
1200         return RPMERR_BADSPEC;
1201     }
1202
1203     if (defattr) {
1204         s = end;
1205         s++;
1206         SKIPSPACE(s);
1207         if (*s) {
1208             rpmError(RPMERR_BADSPEC,
1209                      "No files after %%defattr(): %s", buf);
1210             fl->processingFailed = 1;
1211             return RPMERR_BADSPEC;
1212         }
1213     }
1214
1215     strncpy(ourbuf, p, end-p);
1216     ourbuf[end-p] = '\0';
1217
1218     resultAttr->PmodeString = strtok(ourbuf, ", \n\t");
1219     resultAttr->Uname = strtok(NULL, ", \n\t");
1220     resultAttr->Gname = strtok(NULL, ", \n\t");
1221     resultAttr->PdirmodeString = strtok(NULL, ", \n\t");
1222
1223     if (! (resultAttr->PmodeString &&
1224            resultAttr->Uname && resultAttr->Gname)) {
1225         rpmError(RPMERR_BADSPEC, "Bad %s() syntax: %s", name, buf);
1226         resultAttr->PmodeString = resultAttr->Uname = resultAttr->Gname = NULL;
1227         fl->processingFailed = 1;
1228         return RPMERR_BADSPEC;
1229     }
1230
1231     /* Do a quick test on the mode argument and adjust for "-" */
1232     if (!strcmp(resultAttr->PmodeString, "-")) {
1233         resultAttr->PmodeString = NULL;
1234     } else {
1235         x = sscanf(resultAttr->PmodeString, "%o", &(resultAttr->Pmode));
1236         if ((x == 0) || (resultAttr->Pmode >> 12)) {
1237             rpmError(RPMERR_BADSPEC, "Bad %s() mode spec: %s", name, buf);
1238             resultAttr->PmodeString = resultAttr->Uname =
1239                 resultAttr->Gname = NULL;
1240             fl->processingFailed = 1;
1241             return RPMERR_BADSPEC;
1242         }
1243         resultAttr->PmodeString = strdup(resultAttr->PmodeString);
1244     }
1245     if (resultAttr->PdirmodeString) {
1246         /* The processing here is slightly different to maintain */
1247         /* compatibility with old spec files.                    */
1248         if (!strcmp(resultAttr->PdirmodeString, "-")) {
1249             resultAttr->PdirmodeString = strdup(resultAttr->PdirmodeString);
1250         } else {
1251             x = sscanf(resultAttr->PdirmodeString, "%o",
1252                        &(resultAttr->Pdirmode));
1253             if ((x == 0) || (resultAttr->Pdirmode >> 12)) {
1254                 rpmError(RPMERR_BADSPEC,
1255                          "Bad %s() dirmode spec: %s", name, buf);
1256                 resultAttr->PmodeString = resultAttr->Uname =
1257                     resultAttr->Gname = resultAttr->PdirmodeString = NULL;
1258                 fl->processingFailed = 1;
1259                 return RPMERR_BADSPEC;
1260             }
1261             resultAttr->PdirmodeString = strdup(resultAttr->PdirmodeString);
1262         }
1263     }
1264     if (!strcmp(resultAttr->Uname, "-")) {
1265         resultAttr->Uname = NULL;
1266     } else {
1267         resultAttr->Uname = strdup(resultAttr->Uname);
1268     }
1269     if (!strcmp(resultAttr->Gname, "-")) {
1270         resultAttr->Gname = NULL;
1271     } else {
1272         resultAttr->Gname = strdup(resultAttr->Gname);
1273     }
1274     
1275     /* Set everything we just parsed to blank spaces */
1276     while (start <= end) {
1277         *start++ = ' ';
1278     }
1279
1280     return 0;
1281 }
1282
1283 static int parseForConfig(char *buf, struct FileList *fl)
1284 {
1285     char *p, *start, *end;
1286     char ourbuf[1024];
1287
1288     if (!(p = start = strstr(buf, "%config"))) {
1289         return 0;
1290     }
1291     fl->currentFlags = RPMFILE_CONFIG;
1292
1293     p += 7;
1294     SKIPSPACE(p);
1295
1296     if (*p != '(') {
1297         while (start < p) {
1298             *start++ = ' ';
1299         }
1300         return 0;
1301     }
1302     p++;
1303
1304     end = p;
1305     while (*end && *end != ')') {
1306         end++;
1307     }
1308
1309     if (! *end) {
1310         rpmError(RPMERR_BADSPEC, "Bad %%config() syntax: %s", buf);
1311         fl->processingFailed = 1;
1312         return RPMERR_BADSPEC;
1313     }
1314
1315     strncpy(ourbuf, p, end-p);
1316     ourbuf[end-p] = '\0';
1317     while (start <= end) {
1318         *start++ = ' ';
1319     }
1320
1321     p = strtok(ourbuf, ", \n\t");
1322     while (p) {
1323         if (!strcmp(p, "missingok")) {
1324             fl->currentFlags |= RPMFILE_MISSINGOK;
1325         } else if (!strcmp(p, "noreplace")) {
1326             fl->currentFlags |= RPMFILE_NOREPLACE;
1327         } else {
1328             rpmError(RPMERR_BADSPEC, "Invalid %%config token: %s", p);
1329             fl->processingFailed = 1;
1330             return RPMERR_BADSPEC;
1331         }
1332         p = strtok(NULL, ", \n\t");
1333     }
1334
1335     return 0;
1336 }
1337
1338 /* glob_pattern_p() taken from bash
1339  * Copyright (C) 1985, 1988, 1989 Free Software Foundation, Inc.
1340  *
1341  * Return nonzero if PATTERN has any special globbing chars in it.
1342  */
1343 static int myGlobPatternP (char *pattern)
1344 {
1345     register char *p = pattern;
1346     register char c;
1347     int open = 0;
1348   
1349     while ((c = *p++) != '\0')
1350         switch (c) {
1351         case '?':
1352         case '*':
1353             return (1);
1354         case '[':      /* Only accept an open brace if there is a close */
1355             open++;    /* brace to match it.  Bracket expressions must be */
1356             continue;  /* complete, according to Posix.2 */
1357         case ']':
1358             if (open)
1359                 return (1);
1360             continue;      
1361         case '\\':
1362             if (*p++ == '\0')
1363                 return (0);
1364         }
1365
1366     return (0);
1367 }
1368
1369 static int glob_error(const char *foo, int bar)
1370 {
1371     return 1;
1372 }
1373
1374 /* strtokWithQuotes() modified from glibc strtok() */
1375 /* Copyright (C) 1991, 1996 Free Software Foundation, Inc.
1376    This file is part of the GNU C Library.
1377
1378    The GNU C Library is free software; you can redistribute it and/or
1379    modify it under the terms of the GNU Library General Public License as
1380    published by the Free Software Foundation; either version 2 of the
1381    License, or (at your option) any later version.
1382
1383    The GNU C Library is distributed in the hope that it will be useful,
1384    but WITHOUT ANY WARRANTY; without even the implied warranty of
1385    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1386    Library General Public License for more details.
1387
1388    You should have received a copy of the GNU Library General Public
1389    License along with the GNU C Library; see the file COPYING.LIB.  If
1390    not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
1391    Boston, MA 02111-1307, USA.  */
1392
1393 static char *strtokWithQuotes(char *s, char *delim)
1394 {
1395     static char *olds = NULL;
1396     char *token;
1397
1398     if (s == NULL) {
1399         s = olds;
1400     }
1401
1402     /* Skip leading delimiters */
1403     s += strspn(s, delim);
1404     if (*s == '\0') {
1405         return NULL;
1406     }
1407
1408     /* Find the end of the token.  */
1409     token = s;
1410     if (*token == '"') {
1411         token++;
1412         /* Find next " char */
1413         s = strchr(token, '"');
1414     } else {
1415         s = strpbrk(token, delim);
1416     }
1417
1418     /* Terminate it */
1419     if (s == NULL) {
1420         /* This token finishes the string */
1421         olds = strchr(token, '\0');
1422     } else {
1423         /* Terminate the token and make olds point past it */
1424         *s = '\0';
1425         olds = s+1;
1426     }
1427
1428     return token;
1429 }