1 /* RPM - Copyright (C) 1995 Red Hat Software
3 * prepack.c - routines for packaging
16 #include "stringbuf.h"
29 #define BINARY_HEADER 0
30 #define SOURCE_HEADER 1
38 char *uname; /* reference -- do not free */
39 char *gname; /* reference -- do not free */
41 struct file_entry *next;
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);
56 static void resetDocdir(void);
57 static void addDocdir(char *dirname);
58 static int isDoc(char *filename);
60 static int processFileListFailed;
62 static void parseForDocFiles(struct PackageRec *package, char *line)
64 if (! (line = strstr(line, "%doc"))) {
69 if ((*line != ' ') && (*line != '\t')) {
72 line += strspn(line, " \t\n");
73 if ((! *line) || (*line == '/')) {
77 appendLineStringBuf(package->doc, "mkdir -p $DOCDIR");
78 appendStringBuf(package->doc, "cp -pr ");
79 appendStringBuf(package->doc, line);
80 appendLineStringBuf(package->doc, " $DOCDIR");
83 int finish_filelists(Spec spec)
87 struct PackageRec *pr = spec->packages;
88 char *s, **files, **line;
89 char *version, *release, *packageVersion, *packageRelease, *docs, *name;
91 headerGetEntry(spec->packages->header, RPMTAG_VERSION, NULL,
92 (void *) &version, NULL);
93 headerGetEntry(spec->packages->header, RPMTAG_RELEASE, NULL,
94 (void *) &release, NULL);
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);
105 while (fgets(buf, sizeof(buf), file)) {
107 appendStringBuf(pr->filelist, buf);
112 /* parse for %doc wierdness */
113 s = getStringBuf(pr->filelist);
114 files = splitString(s, strlen(s), '\n');
117 parseForDocFiles(pr, *line);
120 freeSplitString(files);
122 /* Handle subpackage version/release overrides */
123 if (!headerGetEntry(pr->header, RPMTAG_VERSION, NULL,
124 (void *) &packageVersion, NULL)) {
125 packageVersion = version;
127 if (!headerGetEntry(pr->header, RPMTAG_RELEASE, NULL,
128 (void *) &packageRelease, NULL)) {
129 packageRelease = release;
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);
139 appendLineStringBuf(spec->doc, "rm -rf $DOCDIR");
140 appendLineStringBuf(spec->doc, docs);
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)
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() */
168 int passed_special_doc = 0;
173 processFileListFailed = 0;
179 str = getStringBuf(sb);
180 files = splitString(str, strlen(str), '\n');
184 strcpy(buf, *fp); /* temp copy */
201 verify_flags = RPMVERIFY_ALL;
204 /* First preparse buf for %verify() */
205 if (parseForVerify(buf, &verify_flags)) {
206 processFileListFailed = 1;
210 /* Next parse for %attr() */
211 if (parseForAttr(buf, &currPmode, &currUname, &currGname)) {
212 processFileListFailed = 1;
216 /* Then parse for %config or %config() */
217 if (parseForConfig(buf, &conf)) {
218 processFileListFailed = 1;
222 s = strtok(buf, " \t\n");
224 if (!strcmp(s, "%docdir")) {
225 s = strtok(NULL, " \t\n");
228 } else if (!strcmp(s, "%doc")) {
230 } else if (!strcmp(s, "%dir")) {
234 /* We already got a file -- error */
235 rpmError(RPMERR_BADSPEC,
236 "Two files on one line: %s", filename);
237 processFileListFailed = 1;
243 /* not in %doc, does not begin with / -- error */
244 rpmError(RPMERR_BADSPEC,
245 "File must begin with \"/\": %s", s);
246 processFileListFailed = 1;
252 s = strtok(NULL, " \t\n");
255 if (passed_special_doc) {
259 if (filename || conf || isdir) {
260 rpmError(RPMERR_BADSPEC,
261 "Can't mix special %%doc with other forms: %s", fp);
262 processFileListFailed = 1;
265 sprintf(buf, "%s/%s-%s-%s", rpmGetVar(RPMVAR_DEFAULTDOCDIR),
266 name, version, release);
268 passed_special_doc = 1;
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;
285 if (glob_pattern_p(filename)) {
288 if (rpmGetVar(RPMVAR_ROOT)) {
289 sprintf(fullname, "%s%s", rpmGetVar(RPMVAR_ROOT), filename);
291 strcpy(fullname, filename);
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);
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);
311 globfree(&glob_result);
313 c = add_file(&fes, filename, isdoc, conf, isdir,
314 verify_flags, currPmode, currUname,
318 /* Source package are the simple case */
319 fest = malloc(sizeof(struct file_entry));
322 if (!strcmp(filename, specFile)) {
323 fest->isspecfile = 1;
325 fest->isspecfile = 0;
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);
335 processFileListFailed = 1;
337 strcpy(fest->file, filename);
344 rpmError(RPMERR_BADSPEC, "File not found: %s", filename);
345 processFileListFailed = 1;
352 /* If there are no files, don't add anything to the header */
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;
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);
382 /* Build a reverse sorted file array. */
383 /* This makes uninstalls a lot easier. */
384 file_entry_array = malloc(sizeof(struct file_entry *) * count);
388 file_entry_array[c++] = fest;
391 qsort(file_entry_array, count, sizeof(struct file_entry *),
396 currentTime = time(NULL);
397 if ((tcs = rpmGetVar(RPMVAR_TIMECHECK))) {
398 tc = strtoul(tcs, NULL, 10);
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';
409 fileList[c] = fest->file;
411 fileList[c] = strrchr(fest->file, '/') + 1;
413 if ((c > 0) && !strcmp(fileList[c], fileList[c-1])) {
414 rpmError(RPMERR_BADSPEC, "File listed twice: %s", fileList[c]);
415 processFileListFailed = 1;
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);
426 strcpy(buf, fest->file);
429 fileMD5List[c] = strdup(buf);
430 rpmMessage(RPMMESS_DEBUG, "md5(%s) = %s\n", fest->file, buf);
433 fileMD5List[c] = strdup("");
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;
441 if (tc && (type == RPMLEAD_BINARY)) {
442 if (currentTime - fest->statbuf.st_mtime > tc) {
443 rpmMessage(RPMMESS_WARNING, "TIMECHECK failure: %s\n",
448 fileFlagsList[c] = 0;
449 if (isDoc(fest->file))
450 fileFlagsList[c] |= RPMFILE_DOC;
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;
458 fileModesList[c] = fest->statbuf.st_mode;
459 fileRDevsList[c] = fest->statbuf.st_rdev;
460 fileVerifyFlagsList[c] = fest->verify_flags;
462 if (S_ISLNK(fest->statbuf.st_mode)) {
463 if (rpmGetVar(RPMVAR_ROOT)) {
464 sprintf(buf, "%s%s", rpmGetVar(RPMVAR_ROOT), fest->file);
466 strcpy(buf, fest->file);
468 buf[readlink(buf, buf, 1024)] = '\0';
469 fileLinktoList[c] = strdup(buf);
472 fileLinktoList[c] = strdup("");
477 /* Add the header entries */
479 headerAddEntry(header, RPMTAG_FILENAMES, RPM_STRING_ARRAY_TYPE, fileList, c);
480 headerAddEntry(header, RPMTAG_FILELINKTOS, RPM_STRING_ARRAY_TYPE,
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,
488 headerAddEntry(header, RPMTAG_FILEGROUPNAME, RPM_STRING_ARRAY_TYPE,
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);
497 /* Free the allocated strings */
500 free(fileMD5List[c]);
501 free(fileLinktoList[c]);
504 /* Free all those lists */
506 free(fileLinktoList);
513 free(fileMtimesList);
517 free(fileVerifyFlagsList);
519 /* Free the file entry array */
520 free(file_entry_array);
522 /* Free the file entry stack */
531 freeSplitString(files);
532 return processFileListFailed;
535 /*************************************************************/
539 /*************************************************************/
541 static int compare_fe(const void *ap, const void *bp)
545 a = (*(struct file_entry **)ap)->file;
546 b = (*(struct file_entry **)bp)->file;
551 /*************************************************************/
555 /*************************************************************/
557 /* XXX hard coded limit -- only 1024 %docdir allowed */
558 static char *docdirs[1024];
559 static int docdir_count;
561 static void resetDocdir(void)
563 while (docdir_count--) {
564 free(docdirs[docdir_count]);
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");
573 static void addDocdir(char *dirname)
575 if (docdir_count == 1024) {
576 fprintf(stderr, "RPMERR_INTERNAL: Hit limit in addDocdir()\n");
577 exit(RPMERR_INTERNAL);
579 docdirs[docdir_count++] = strdup(dirname);
582 static int isDoc(char *filename)
586 while (x < docdir_count) {
587 if (strstr(filename, docdirs[x]) == filename) {
595 /*************************************************************/
597 /* File stating / tree walk */
599 /*************************************************************/
601 /* Need three globals to keep track of things in ftw() */
604 static int Gverify_flags;
609 static struct file_entry **Gfestack;
610 static char *Gprefix;
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)
616 struct file_entry *p;
617 char *copyTo, copied;
618 const char *prefixTest, *prefixPtr;
619 const char *copyFrom;
623 /* Set these up for ftw() */
627 Gverify_flags = verify_flags;
633 p = malloc(sizeof(struct file_entry));
639 if (*copyFrom != '/' || copied != '/') {
640 *copyTo++ = copied = *copyFrom;
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);
653 strcpy(fullname, name);
656 /* If we are using a prefix, validate the file */
660 while (*prefixTest == '/') {
661 prefixTest++; /* Skip leading "/" */
663 while (*prefixPtr && *prefixTest && (*prefixTest == *prefixPtr)) {
668 rpmError(RPMERR_BADSPEC, "File doesn't match prefix (%s): %s",
674 /* OK, finally stat() the file */
675 if (lstat(fullname, &p->statbuf)) {
680 * If %attr() was specified, then use those values instead of
681 * what lstat() returned.
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;
689 if (Uname && strcmp(Uname, "-")) {
690 p->uname = getUnameS(Uname);
692 p->uname = getUname(p->statbuf.st_uid);
695 if (Gname && strcmp(Gname, "-")) {
696 p->gname = getGnameS(Gname);
698 p->gname = getGname(p->statbuf.st_gid);
701 if (! (p->uname && p->gname)) {
702 rpmError(RPMERR_BADSPEC, "Bad owner/group: %s\n", fullname);
708 if ((! isdir) && S_ISDIR(p->statbuf.st_mode)) {
709 /* This means we need to descend with ftw() */
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);
724 rpmMessage(RPMMESS_DEBUG, "ADDING: %s\n", name);
726 /* return number of entries added */
731 static int add_file_aux(const char *file, struct stat *sb, int flag)
733 const char *name = file;
735 if (rpmGetVar(RPMVAR_ROOT)) {
736 name += strlen(rpmGetVar(RPMVAR_ROOT));
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);
744 return 0; /* for ftw() */
747 /*************************************************************/
751 /*************************************************************/
753 /* glob_pattern_p() taken from bash
754 * Copyright (C) 1985, 1988, 1989 Free Software Foundation, Inc.
757 /* Return nonzero if PATTERN has any special globbing chars in it. */
758 static int glob_pattern_p (char *pattern)
760 register char *p = pattern;
764 while ((c = *p++) != '\0')
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 */
784 static int glob_error(const char *foo, int bar)
789 /*************************************************************/
793 /*************************************************************/
795 static int parseForAttr(char *buf, char **currPmode,
796 char **currUname, char **currGname)
798 char *p, *start, *end;
802 if (!(p = start = strstr(buf, "%attr"))) {
806 *currPmode = *currUname = *currGname = NULL;
809 while (*p && (*p == ' ' || *p == '\t')) {
814 rpmError(RPMERR_BADSPEC, "Bad %%attr() syntax: %s", buf);
815 return RPMERR_BADSPEC;
820 while (*end && *end != ')') {
825 rpmError(RPMERR_BADSPEC, "Bad %%attr() syntax: %s", buf);
826 return RPMERR_BADSPEC;
829 strncpy(ourbuf, p, end-p);
830 ourbuf[end-p] = '\0';
832 *currPmode = strtok(ourbuf, ", \n\t");
833 *currUname = strtok(NULL, ", \n\t");
834 *currGname = strtok(NULL, ", \n\t");
836 if (! (*currPmode && *currUname && *currGname)) {
837 rpmError(RPMERR_BADSPEC, "Bad %%attr() syntax: %s", buf);
838 *currPmode = *currUname = *currGname = NULL;
839 return RPMERR_BADSPEC;
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;
852 *currPmode = strdup(*currPmode);
853 *currUname = strdup(*currUname);
854 *currGname = strdup(*currGname);
856 /* Set everything we just parsed to blank spaces */
857 while (start <= end) {
864 /*************************************************************/
866 /* %verify parsing */
868 /*************************************************************/
870 static int parseForVerify(char *buf, int *verify_flags)
872 char *p, *start, *end;
876 if (!(p = start = strstr(buf, "%verify"))) {
881 while (*p && (*p == ' ' || *p == '\t')) {
886 rpmError(RPMERR_BADSPEC, "Bad %%verify() syntax: %s", buf);
887 return RPMERR_BADSPEC;
892 while (*end && *end != ')') {
897 rpmError(RPMERR_BADSPEC, "Bad %%verify() syntax: %s", buf);
898 return RPMERR_BADSPEC;
901 strncpy(ourbuf, p, end-p);
902 ourbuf[end-p] = '\0';
903 while (start <= end) {
907 p = strtok(ourbuf, ", \n\t");
909 *verify_flags = RPMVERIFY_NONE;
911 if (!strcmp(p, "not")) {
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;
930 rpmError(RPMERR_BADSPEC, "Invalid %%verify token: %s", p);
931 return RPMERR_BADSPEC;
933 p = strtok(NULL, ", \n\t");
937 *verify_flags = ~(*verify_flags);
943 static int parseForConfig(char *buf, int *conf)
945 char *p, *start, *end;
948 if (!(p = start = strstr(buf, "%config"))) {
951 *conf = RPMFILE_CONFIG;
954 while (*p && (*p == ' ' || *p == '\t')) {
967 while (*end && *end != ')') {
972 rpmError(RPMERR_BADSPEC, "Bad %%config() syntax: %s", buf);
973 return RPMERR_BADSPEC;
976 strncpy(ourbuf, p, end-p);
977 ourbuf[end-p] = '\0';
978 while (start <= end) {
982 p = strtok(ourbuf, ", \n\t");
984 if (!strcmp(p, "missingok")) {
985 *conf |= RPMFILE_MISSINGOK;
986 } else if (!strcmp(p, "noreplace")) {
987 *conf |= RPMFILE_NOREPLACE;
989 rpmError(RPMERR_BADSPEC, "Invalid %%config token: %s", p);
990 return RPMERR_BADSPEC;
992 p = strtok(NULL, ", \n\t");