--- /dev/null
+/* RPM - Copyright (C) 1995 Red Hat Software
+ *
+ * prepack.c - routines for packaging
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <ftw.h>
+#include <glob.h>
+
+#include "spec.h"
+#include "specP.h"
+#include "stringbuf.h"
+#include "build.h"
+#include "messages.h"
+#include "md5.h"
+#include "myftw.h"
+#include "header.h"
+#include "files.h"
+#include "names.h"
+#include "rpmerr.h"
+#include "rpmlead.h"
+#include "rpmlib.h"
+#include "misc.h"
+
+#define BINARY_HEADER 0
+#define SOURCE_HEADER 1
+
+struct file_entry {
+ char file[1024];
+ int isdoc;
+ int isconf;
+ int verify_flags;
+ char *uname; /* reference -- do not free */
+ char *gname; /* reference -- do not free */
+ struct stat statbuf;
+ struct file_entry *next;
+};
+
+static int add_file(struct file_entry **festack, const char *name,
+ int isdoc, int isconf, int isdir, int verify_flags,
+ char *Pmode, char *Uname, char *Gname);
+static int compare_fe(const void *ap, const void *bp);
+static int add_file_aux(const char *file, struct stat *sb, int flag);
+static int glob_error(const char *foo, int bar);
+static int glob_pattern_p (char *pattern);
+static int parseForVerify(char *buf, int *verify_flags);
+static int parseForAttr(char *origbuf, char **currPmode,
+ char **currUname, char **currGname);
+
+static void resetDocdir(void);
+static void addDocdir(char *dirname);
+static int isDoc(char *filename);
+
+int process_filelist(Header header, struct PackageRec *pr,
+ StringBuf sb, int *size, char *name,
+ char *version, char *release, int type)
+{
+ char buf[1024];
+ char **files, **fp;
+ struct file_entry *fes, *fest;
+ struct file_entry **file_entry_array;
+ int isdoc, isconf, isdir, verify_flags;
+ char *currPmode=NULL; /* hold info from %attr() */
+ char *currUname=NULL; /* hold info from %attr() */
+ char *currGname=NULL; /* hold info from %attr() */
+ char *filename, *s;
+ char *str;
+ int count = 0;
+ int c, x;
+ glob_t glob_result;
+ int special_doc;
+ int passed_special_doc = 0;
+ FILE *file;
+
+ fes = NULL;
+ *size = 0;
+
+ resetDocdir();
+
+ if (type == RPMLEAD_BINARY && pr->fileFile) {
+ sprintf(buf, "%s/%s/%s", getVar(RPMVAR_BUILDDIR),
+ build_subdir, pr->fileFile);
+ message(MESS_DEBUG, "Reading file names from: %sXX\n", buf);
+ if ((file = fopen(buf, "r")) == NULL) {
+ perror("open fileFile");
+ exit(1);
+ }
+ while (fgets(buf, sizeof(buf), file)) {
+ appendStringBuf(sb, buf);
+ }
+ fclose(file);
+ }
+
+ str = getStringBuf(sb);
+ files = splitString(str, strlen(str), '\n');
+ fp = files;
+
+ while (*fp) {
+ strcpy(buf, *fp); /* temp copy */
+ isdoc = 0;
+ special_doc = 0;
+ isconf = 0;
+ isdir = 0;
+ if (currPmode) {
+ free (currPmode);
+ currPmode = NULL;
+ }
+ if (currUname) {
+ free (currUname);
+ currUname = NULL;
+ }
+ if (currGname) {
+ free (currGname);
+ currGname = NULL;
+ }
+ verify_flags = VERIFY_ALL;
+ filename = NULL;
+
+ /* First preparse buf for %verify() */
+ if (!parseForVerify(buf, &verify_flags)) {
+ return(RPMERR_BADSPEC);
+ }
+
+ /* Next parse for for %attr() */
+ if (!parseForAttr(buf, &currPmode, &currUname, &currGname)) {
+ return(RPMERR_BADSPEC);
+ }
+
+ s = strtok(buf, " \t\n");
+ while (s) {
+ if (!strcmp(s, "%docdir")) {
+ s = strtok(NULL, " \t\n");
+ addDocdir(s);
+ break;
+ } else if (!strcmp(s, "%doc")) {
+ isdoc = 1;
+ } else if (!strcmp(s, "%config")) {
+ isconf = 1;
+ } else if (!strcmp(s, "%dir")) {
+ isdir = 1;
+ } else {
+ if (isdoc && (*s != '/')) {
+ /* This is a special %doc macro */
+ special_doc = 1;
+ } else {
+ filename = s;
+ }
+ }
+ s = strtok(NULL, " \t\n");
+ }
+ if (special_doc) {
+ if (passed_special_doc) {
+ fp++;
+ continue;
+ } else {
+ if (filename || isconf || isdir) {
+ error(RPMERR_BADSPEC,
+ "Can't mix special %%doc with other forms: %s", fp);
+ return(RPMERR_BADSPEC);
+ }
+ sprintf(buf, "%s/%s-%s-%s", getVar(RPMVAR_DOCDIR),
+ name, version, release);
+ filename = buf;
+ passed_special_doc = 1;
+ }
+ }
+ if (! filename) {
+ fp++;
+ continue;
+ }
+
+ if (type == RPMLEAD_BINARY) {
+ /* check that file starts with leading "/" */
+ if (*filename != '/') {
+ error(RPMERR_BADSPEC,
+ "File needs leading \"/\": %s", filename);
+ return(RPMERR_BADSPEC);
+ }
+
+ if (glob_pattern_p(filename)) {
+ char fullname[1024];
+
+ if (getVar(RPMVAR_ROOT)) {
+ sprintf(fullname, "%s%s", getVar(RPMVAR_ROOT), filename);
+ } else {
+ strcpy(fullname, filename);
+ }
+
+ if (glob(fullname, 0, glob_error, &glob_result)) {
+ error(RPMERR_BADSPEC, "No matches: %s", fullname);
+ return(RPMERR_BADSPEC);
+ }
+ if (glob_result.gl_pathc < 1) {
+ error(RPMERR_BADSPEC, "No matches: %s", fullname);
+ return(RPMERR_BADSPEC);
+ }
+ x = 0;
+ c = 0;
+ while (x < glob_result.gl_pathc) {
+ int offset = strlen(getVar(RPMVAR_ROOT) ? : "");
+ c += add_file(&fes, &(glob_result.gl_pathv[x][offset]),
+ isdoc, isconf, isdir, verify_flags,
+ currPmode, currUname, currGname);
+ x++;
+ }
+ globfree(&glob_result);
+ } else {
+ c = add_file(&fes, filename, isdoc, isconf, isdir,
+ verify_flags, currPmode, currUname, currGname);
+ }
+ } else {
+ /* Source package are the simple case */
+ fest = malloc(sizeof(struct file_entry));
+ fest->isdoc = 0;
+ fest->isconf = 0;
+ fest->verify_flags = 0; /* XXX - something else? */
+ stat(filename, &fest->statbuf);
+ fest->uname = getUname(fest->statbuf.st_uid);
+ fest->gname = getGname(fest->statbuf.st_gid);
+ strcpy(fest->file, filename);
+ fest->next = fes;
+ fes = fest;
+ c = 1;
+ }
+
+ if (! c) {
+ error(RPMERR_BADSPEC, "File not found: %s", filename);
+ return(RPMERR_BADSPEC);
+ }
+ count += c;
+
+ fp++;
+ }
+
+ /* If there are no files, don't add anything to the header */
+ if (count) {
+ char ** fileList;
+ char ** fileMD5List;
+ char ** fileLinktoList;
+ int_32 * fileSizeList;
+ int_32 * fileUIDList;
+ int_32 * fileGIDList;
+ char ** fileUnameList;
+ char ** fileGnameList;
+ int_32 * fileMtimesList;
+ int_32 * fileFlagsList;
+ int_16 * fileModesList;
+ int_16 * fileRDevsList;
+ int_32 * fileVerifyFlagsList;
+
+ fileList = malloc(sizeof(char *) * count);
+ fileLinktoList = malloc(sizeof(char *) * count);
+ fileMD5List = malloc(sizeof(char *) * count);
+ fileSizeList = malloc(sizeof(int_32) * count);
+ fileUIDList = malloc(sizeof(int_32) * count);
+ fileGIDList = malloc(sizeof(int_32) * count);
+ fileUnameList = malloc(sizeof(char *) * count);
+ fileGnameList = malloc(sizeof(char *) * count);
+ fileMtimesList = malloc(sizeof(int_32) * count);
+ fileFlagsList = malloc(sizeof(int_32) * count);
+ fileModesList = malloc(sizeof(int_16) * count);
+ fileRDevsList = malloc(sizeof(int_16) * count);
+ fileVerifyFlagsList = malloc(sizeof(int_32) * count);
+
+ /* Build a reverse sorted file array. */
+ /* This makes uninstalls a lot easier. */
+ file_entry_array = malloc(sizeof(struct file_entry *) * count);
+ c = 0;
+ fest = fes;
+ while (fest) {
+ file_entry_array[c++] = fest;
+ fest = fest->next;
+ }
+ qsort(file_entry_array, count, sizeof(struct file_entry *),
+ compare_fe);
+
+ c = 0;
+ while (c < count) {
+ fest = file_entry_array[c];
+ if (type == RPMLEAD_BINARY) {
+ fileList[c] = fest->file;
+ } else {
+ fileList[c] = strrchr(fest->file, '/') + 1;
+ }
+ fileUnameList[c] = fest->uname;
+ fileGnameList[c] = fest->gname;
+ *size += fest->statbuf.st_size;
+ if (S_ISREG(fest->statbuf.st_mode)) {
+ if (getVar(RPMVAR_ROOT)) {
+ sprintf(buf, "%s%s", getVar(RPMVAR_ROOT), fest->file);
+ } else {
+ strcpy(buf, fest->file);
+ }
+ mdfile(buf, buf);
+ fileMD5List[c] = strdup(buf);
+ message(MESS_DEBUG, "md5(%s) = %s\n", fest->file, buf);
+ } else {
+ /* This is stupid */
+ fileMD5List[c] = strdup("");
+ }
+ fileSizeList[c] = fest->statbuf.st_size;
+ fileUIDList[c] = fest->statbuf.st_uid;
+ fileGIDList[c] = fest->statbuf.st_gid;
+ fileMtimesList[c] = fest->statbuf.st_mtime;
+ fileFlagsList[c] = 0;
+ if (isDoc(fest->file))
+ fileFlagsList[c] |= RPMFILE_DOC;
+ if (fest->isdoc)
+ fileFlagsList[c] |= RPMFILE_DOC;
+ if (fest->isconf)
+ fileFlagsList[c] |= RPMFILE_CONFIG;
+
+ fileModesList[c] = fest->statbuf.st_mode;
+ fileRDevsList[c] = fest->statbuf.st_rdev;
+ fileVerifyFlagsList[c] = fest->verify_flags;
+
+ if (S_ISLNK(fest->statbuf.st_mode)) {
+ if (getVar(RPMVAR_ROOT)) {
+ sprintf(buf, "%s%s", getVar(RPMVAR_ROOT), fest->file);
+ } else {
+ strcpy(buf, fest->file);
+ }
+ buf[readlink(buf, buf, 1024)] = '\0';
+ fileLinktoList[c] = strdup(buf);
+ } else {
+ /* This is stupid */
+ fileLinktoList[c] = strdup("");
+ }
+ c++;
+ }
+
+ /* Add the header entries */
+ c = count;
+ addEntry(header, RPMTAG_FILENAMES, STRING_ARRAY_TYPE, fileList, c);
+ addEntry(header, RPMTAG_FILELINKTOS, STRING_ARRAY_TYPE,
+ fileLinktoList, c);
+ addEntry(header, RPMTAG_FILEMD5S, STRING_ARRAY_TYPE, fileMD5List, c);
+ addEntry(header, RPMTAG_FILESIZES, INT32_TYPE, fileSizeList, c);
+ addEntry(header, RPMTAG_FILEUIDS, INT32_TYPE, fileUIDList, c);
+ addEntry(header, RPMTAG_FILEGIDS, INT32_TYPE, fileGIDList, c);
+ addEntry(header, RPMTAG_FILEUSERNAME, STRING_ARRAY_TYPE,
+ fileUnameList, c);
+ addEntry(header, RPMTAG_FILEGROUPNAME, STRING_ARRAY_TYPE,
+ fileGnameList, c);
+ addEntry(header, RPMTAG_FILEMTIMES, INT32_TYPE, fileMtimesList, c);
+ addEntry(header, RPMTAG_FILEFLAGS, INT32_TYPE, fileFlagsList, c);
+ addEntry(header, RPMTAG_FILEMODES, INT16_TYPE, fileModesList, c);
+ addEntry(header, RPMTAG_FILERDEVS, INT16_TYPE, fileRDevsList, c);
+ addEntry(header, RPMTAG_FILEVERIFYFLAGS, INT32_TYPE,
+ fileVerifyFlagsList, c);
+
+ /* Free the allocated strings */
+ c = count;
+ while (c--) {
+ free(fileMD5List[c]);
+ free(fileLinktoList[c]);
+ }
+
+ /* Free all those lists */
+ free(fileList);
+ free(fileLinktoList);
+ free(fileMD5List);
+ free(fileSizeList);
+ free(fileUIDList);
+ free(fileGIDList);
+ free(fileUnameList);
+ free(fileGnameList);
+ free(fileMtimesList);
+ free(fileFlagsList);
+ free(fileModesList);
+ free(fileRDevsList);
+ free(fileVerifyFlagsList);
+
+ /* Free the file entry array */
+ free(file_entry_array);
+
+ /* Free the file entry stack */
+ fest = fes;
+ while (fest) {
+ fes = fest->next;
+ free(fest);
+ fest = fes;
+ }
+ }
+
+ freeSplitString(files);
+ return 0;
+}
+
+/*************************************************************/
+/* */
+/* misc */
+/* */
+/*************************************************************/
+
+static int compare_fe(const void *ap, const void *bp)
+{
+ char *a, *b;
+
+ a = (*(struct file_entry **)ap)->file;
+ b = (*(struct file_entry **)bp)->file;
+
+ return strcmp(a, b);
+}
+
+/*************************************************************/
+/* */
+/* Doc dir stuff */
+/* */
+/*************************************************************/
+
+/* XXX hard coded limit -- only 1024 %docdir allowed */
+static char *docdirs[1024];
+static int docdir_count;
+
+static void resetDocdir(void)
+{
+ while (docdir_count--) {
+ free(docdirs[docdir_count]);
+ }
+ docdir_count = 0;
+ docdirs[docdir_count++] = strdup("/usr/doc");
+ docdirs[docdir_count++] = strdup("/usr/man");
+ docdirs[docdir_count++] = strdup("/usr/info");
+}
+
+static void addDocdir(char *dirname)
+{
+ if (docdir_count == 1024) {
+ fprintf(stderr, "RPMERR_INTERNAL: Hit limit in addDocdir()\n");
+ exit(RPMERR_INTERNAL);
+ }
+ docdirs[docdir_count++] = strdup(dirname);
+}
+
+static int isDoc(char *filename)
+{
+ int x = 0;
+
+ while (x < docdir_count) {
+ if (strstr(filename, docdirs[x]) == filename) {
+ return 1;
+ }
+ x++;
+ }
+ return 0;
+}
+
+/*************************************************************/
+/* */
+/* File stating / tree walk */
+/* */
+/*************************************************************/
+
+/* Need three globals to keep track of things in ftw() */
+static int Gisdoc;
+static int Gisconf;
+static int Gverify_flags;
+static int Gcount;
+static char *GPmode;
+static char *GUname;
+static char *GGname;
+static struct file_entry **Gfestack;
+
+static int add_file(struct file_entry **festack, const char *name,
+ int isdoc, int isconf, int isdir, int verify_flags,
+ char *Pmode, char *Uname, char *Gname)
+{
+ struct file_entry *p;
+ char fullname[1024];
+ int mode;
+
+ /* Set these up for ftw() */
+ Gfestack = festack;
+ Gisdoc = isdoc;
+ Gisconf = isconf;
+ Gverify_flags = verify_flags;
+ GPmode = Pmode;
+ GUname = Uname;
+ GGname = Gname;
+
+ p = malloc(sizeof(struct file_entry));
+ strcpy(p->file, name);
+ p->isdoc = isdoc;
+ p->isconf = isconf;
+ p->verify_flags = verify_flags;
+ if (getVar(RPMVAR_ROOT)) {
+ sprintf(fullname, "%s%s", getVar(RPMVAR_ROOT), name);
+ } else {
+ strcpy(fullname, name);
+ }
+ if (lstat(fullname, &p->statbuf)) {
+ return 0;
+ }
+
+ /*
+ * If %attr() was specified, then use those values instead of
+ * what lstat() returned.
+ */
+ if (Pmode && strcmp(Pmode, "-")) {
+ sscanf(Pmode, "%o", &mode);
+ mode |= p->statbuf.st_mode & S_IFMT;
+ p->statbuf.st_mode = (unsigned short)mode;
+ }
+
+ if (Uname && strcmp(Uname, "-")) {
+ p->uname = getUnameS(Uname);
+ } else {
+ p->uname = getUname(p->statbuf.st_uid);
+ }
+
+ if (Gname && strcmp(Gname, "-")) {
+ p->gname = getGnameS(Gname);
+ } else {
+ p->gname = getGname(p->statbuf.st_gid);
+ }
+
+ if ((! isdir) && S_ISDIR(p->statbuf.st_mode)) {
+ /* This means we need to descend with ftw() */
+ Gcount = 0;
+
+ /* We use our own ftw() call, because ftw() uses stat() */
+ /* instead of lstat(), which causes it to follow symlinks! */
+ myftw(fullname, add_file_aux, 16);
+
+ free(p);
+
+ return Gcount;
+ } else {
+ /* Link it in */
+ p->next = *festack;
+ *festack = p;
+
+ message(MESS_DEBUG, "ADDING: %s\n", name);
+
+ /* return number of entries added */
+ return 1;
+ }
+}
+
+static int add_file_aux(const char *file, struct stat *sb, int flag)
+{
+ const char *name = file;
+
+ if (getVar(RPMVAR_ROOT)) {
+ name += strlen(getVar(RPMVAR_ROOT));
+ }
+
+ /* The 1 will cause add_file() to *not* descend */
+ /* directories -- ftw() is already doing it! */
+ Gcount += add_file(Gfestack, name, Gisdoc, Gisconf, 1, Gverify_flags,
+ GPmode, GUname, GGname);
+
+ return 0; /* for ftw() */
+}
+
+/*************************************************************/
+/* */
+/* globbing */
+/* */
+/*************************************************************/
+
+/* glob_pattern_p() taken from bash
+ * Copyright (C) 1985, 1988, 1989 Free Software Foundation, Inc.
+ */
+
+/* Return nonzero if PATTERN has any special globbing chars in it. */
+static int glob_pattern_p (char *pattern)
+{
+ register char *p = pattern;
+ register char c;
+ int open = 0;
+
+ while ((c = *p++) != '\0')
+ switch (c) {
+ case '?':
+ case '*':
+ return (1);
+ case '[': /* Only accept an open brace if there is a close */
+ open++; /* brace to match it. Bracket expressions must be */
+ continue; /* complete, according to Posix.2 */
+ case ']':
+ if (open)
+ return (1);
+ continue;
+ case '\\':
+ if (*p++ == '\0')
+ return (0);
+ }
+
+ return (0);
+}
+
+static int glob_error(const char *foo, int bar)
+{
+ return 1;
+}
+
+/*************************************************************/
+/* */
+/* %attr parsing */
+/* */
+/*************************************************************/
+
+static int parseForAttr(char *buf, char **currPmode,
+ char **currUname, char **currGname)
+{
+ char *p, *start, *end;
+ char ourbuf[1024];
+ int mode, x;
+
+ if (!(p = start = strstr(buf, "%attr"))) {
+ return 1;
+ }
+
+ *currPmode = *currUname = *currGname = NULL;
+
+ p += 5;
+ while (*p && (*p == ' ' || *p == '\t')) {
+ p++;
+ }
+
+ if (*p != '(') {
+ error(RPMERR_BADSPEC, "Bad %%attr() syntax: %s", buf);
+ return 0;
+ }
+ p++;
+
+ end = p;
+ while (*end && *end != ')') {
+ end++;
+ }
+
+ if (! *end) {
+ error(RPMERR_BADSPEC, "Bad %%attr() syntax: %s", buf);
+ return 0;
+ }
+
+ strncpy(ourbuf, p, end-p);
+ ourbuf[end-p] = '\0';
+
+ *currPmode = strtok(ourbuf, ", \n\t");
+ *currUname = strtok(NULL, ", \n\t");
+ *currGname = strtok(NULL, ", \n\t");
+
+ if (! (*currPmode && *currUname && *currGname)) {
+ error(RPMERR_BADSPEC, "Bad %%attr() syntax: %s", buf);
+ *currPmode = *currUname = *currGname = NULL;
+ return 0;
+ }
+
+ /* Do a quick test on the mode argument */
+ if (strcmp(*currPmode, "-")) {
+ x = sscanf(*currPmode, "%o", &mode);
+ if ((x == 0) || (mode >> 12)) {
+ error(RPMERR_BADSPEC, "Bad %%attr() mode spec: %s", buf);
+ *currPmode = *currUname = *currGname = NULL;
+ return 0;
+ }
+ }
+
+ *currPmode = strdup(*currPmode);
+ *currUname = strdup(*currUname);
+ *currGname = strdup(*currGname);
+
+ /* Set everything we just parsed to blank spaces */
+ while (start <= end) {
+ *start++ = ' ';
+ }
+
+ return 1;
+}
+
+/*************************************************************/
+/* */
+/* %verify parsing */
+/* */
+/*************************************************************/
+
+static int parseForVerify(char *buf, int *verify_flags)
+{
+ char *p, *start, *end;
+ char ourbuf[1024];
+ int not;
+
+ if (!(p = start = strstr(buf, "%verify"))) {
+ return 1;
+ }
+
+ p += 7;
+ while (*p && (*p == ' ' || *p == '\t')) {
+ p++;
+ }
+
+ if (*p != '(') {
+ error(RPMERR_BADSPEC, "Bad %%verify() syntax: %s", buf);
+ return 0;
+ }
+ p++;
+
+ end = p;
+ while (*end && *end != ')') {
+ end++;
+ }
+
+ if (! *end) {
+ error(RPMERR_BADSPEC, "Bad %%verify() syntax: %s", buf);
+ return 0;
+ }
+
+ strncpy(ourbuf, p, end-p);
+ ourbuf[end-p] = '\0';
+ while (start <= end) {
+ *start++ = ' ';
+ }
+
+ p = strtok(ourbuf, ", \n\t");
+ not = 0;
+ *verify_flags = VERIFY_NONE;
+ while (p) {
+ if (!strcmp(p, "not")) {
+ not = 1;
+ } else if (!strcmp(p, "md5")) {
+ *verify_flags |= VERIFY_MD5;
+ } else if (!strcmp(p, "size")) {
+ *verify_flags |= VERIFY_FILESIZE;
+ } else if (!strcmp(p, "link")) {
+ *verify_flags |= VERIFY_LINKTO;
+ } else if (!strcmp(p, "user")) {
+ *verify_flags |= VERIFY_USER;
+ } else if (!strcmp(p, "group")) {
+ *verify_flags |= VERIFY_GROUP;
+ } else if (!strcmp(p, "mtime")) {
+ *verify_flags |= VERIFY_MTIME;
+ } else if (!strcmp(p, "mode")) {
+ *verify_flags |= VERIFY_MODE;
+ } else if (!strcmp(p, "rdev")) {
+ *verify_flags |= VERIFY_RDEV;
+ } else {
+ error(RPMERR_BADSPEC, "Invalid %%verify token: %s", p);
+ return 0;
+ }
+ p = strtok(NULL, ", \n\t");
+ }
+
+ if (not) {
+ *verify_flags = ~(*verify_flags);
+ }
+
+ return 1;
+}
--- /dev/null
+/* reqprov.c -- require/provide handling */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "specP.h"
+#include "reqprov.h"
+#include "messages.h"
+#include "rpmlib.h"
+#include "rpmerr.h"
+
+static void parseFileForProv(char *f, struct PackageRec *p);
+
+/*************************************************************/
+/* */
+/* Adding entries to the package reqprov list */
+/* Handle duplicate entries */
+/* */
+/*************************************************************/
+
+int addReqProv(struct PackageRec *p, int flags,
+ char *name, char *version)
+{
+ struct ReqProv *rd;
+ int same;
+
+ /* Frist see if the same entry is already there */
+ rd = p->reqprov;
+ while (rd) {
+ if (rd->flags == flags) {
+ if (rd->version == version) {
+ same = 1;
+ } else if (!rd->version || !version) {
+ same = 0;
+ } else if (!strcmp(rd->version, version)) {
+ same = 1;
+ } else {
+ same = 0;
+ }
+
+ if (same && !strcmp(rd->name, name)) {
+ /* They are exacty the same */
+ break;
+ }
+ }
+ rd = rd->next;
+ }
+ if (rd) {
+ /* already there */
+ message(MESS_DEBUG, "Already Got: %s\n", name);
+ return 0;
+ }
+
+ rd = (struct ReqProv *)malloc(sizeof(*rd));
+ rd->flags = flags;
+ rd->name = strdup(name);
+ rd->version = version ? strdup(version) : NULL;
+ rd->next = p->reqprov;
+ p->reqprov = rd;
+
+ if (flags & REQUIRE_PROVIDES) {
+ message(MESS_DEBUG, "Adding provide: %s\n", name);
+ p->numProv++;
+ } else {
+ message(MESS_DEBUG, "Adding require: %s\n", name);
+ p->numReq++;
+ }
+
+ return 0;
+}
+
+/*************************************************************/
+/* */
+/* Add require/provides for the files in the header */
+/* (adds to the package structure) */
+/* */
+/*************************************************************/
+
+static void parseFileForProv(char *f, struct PackageRec *p)
+{
+ char file[1024];
+ char *s, *tok;
+
+ strcpy(file, f);
+ s = file + strlen(f) - 1;
+ while (*s != '/') {
+ s--;
+ }
+ s++;
+ tok = s;
+
+ if ((s = strstr(s, ".so"))) {
+ s += 3;
+ *s = '\0';
+ addReqProv(p, REQUIRE_PROVIDES, tok, NULL);
+ }
+}
+
+int generateAutoReqProv(Header header, struct PackageRec *p)
+{
+ char **f, *s, *tok;
+ int count;
+ int lddPID;
+ int lddDead;
+ int toLdd[2];
+ int fromLdd[2];
+
+ StringBuf writeBuff;
+ StringBuf readBuff;
+ char *writePtr;
+ int writeBytesLeft, bytesWritten;
+
+ int bytes;
+ unsigned char buf[8193];
+
+ int status;
+ void *oldhandler;
+
+ message(MESS_VERBOSE, "Finding dependencies\n");
+
+ pipe(toLdd);
+ pipe(fromLdd);
+
+ oldhandler = signal(SIGPIPE, SIG_IGN);
+
+ if (!(lddPID = fork())) {
+ close(0);
+ close(1);
+ close(toLdd[1]);
+ close(fromLdd[0]);
+
+ dup2(toLdd[0], 0); /* Make stdin the in pipe */
+ dup2(fromLdd[1], 1); /* Make stdout the out pipe */
+ close(2); /* Toss stderr */
+
+ if (getVar(RPMVAR_ROOT)) {
+ if (chdir(getVar(RPMVAR_ROOT))) {
+ error(RPMERR_EXEC, "Couldn't chdir to %s",
+ getVar(RPMVAR_ROOT));
+ exit(RPMERR_EXEC);
+ }
+ } else {
+ chdir("/");
+ }
+
+ execlp("xargs", "xargs", "ldd", NULL);
+ error(RPMERR_EXEC, "Couldn't exec ldd");
+ exit(RPMERR_EXEC);
+ }
+ if (lddPID < 0) {
+ error(RPMERR_FORK, "Couldn't fork ldd (xargs)");
+ return RPMERR_FORK;
+ }
+
+ close(toLdd[0]);
+ close(fromLdd[1]);
+
+ /* Do not block reading or writing from/to ldd. */
+ fcntl(fromLdd[0], F_SETFL, O_NONBLOCK);
+ fcntl(toLdd[1], F_SETFL, O_NONBLOCK);
+
+ if (!getEntry(header, RPMTAG_FILENAMES, NULL, (void **) &f, &count)) {
+ /* count may already be 0, but this is safer */
+ count = 0;
+ }
+
+ readBuff = newStringBuf();
+
+ writeBuff = newStringBuf();
+ writeBytesLeft = 0;
+ while (count--) {
+ s = *f++;
+ writeBytesLeft += strlen(s) + 1;
+ appendLineStringBuf(writeBuff, s);
+ parseFileForProv(s, p);
+ }
+ writePtr = getStringBuf(writeBuff);
+
+ lddDead = 0;
+ do {
+ if (waitpid(lddPID, &status, WNOHANG)) {
+ lddDead = 1;
+ }
+
+ /* Write some stuff to the ldd process if possible */
+ if (writeBytesLeft) {
+ if ((bytesWritten =
+ write(toLdd[1], writePtr,
+ (1024<writeBytesLeft) ? 1024 : writeBytesLeft)) < 0) {
+ if (errno != EAGAIN) {
+ perror("Damn!");
+ exit(1);
+ }
+ bytesWritten = 0;
+ }
+ writeBytesLeft -= bytesWritten;
+ writePtr += bytesWritten;
+ } else {
+ close(toLdd[1]);
+ }
+
+ /* Read any data from ldd */
+ bytes = read(fromLdd[0], buf, sizeof(buf)-1);
+ while (bytes > 0) {
+ buf[bytes] = '\0';
+ appendStringBuf(readBuff, buf);
+ bytes = read(fromLdd[0], buf, sizeof(buf)-1);
+ }
+
+ /* terminate when ldd dies */
+ } while (!lddDead);
+
+ close(toLdd[1]);
+ close(fromLdd[0]);
+
+ signal(SIGPIPE, oldhandler);
+
+ if (writeBytesLeft) {
+ error(RPMERR_EXEC, "failed to write all data to ldd");
+ return 1;
+ }
+ waitpid(lddPID, &status, 0);
+ if (!WIFEXITED(status) || WEXITSTATUS(status)) {
+ error(RPMERR_EXEC, "ldd failed");
+ return 1;
+ }
+
+ freeStringBuf(writeBuff);
+ s = getStringBuf(readBuff);
+ while (*s) {
+ if (isspace(*s)) {
+ while (isspace(*s)) {
+ s++;
+ }
+ tok = s;
+ if ((s = strstr(s, ".so"))) {
+ s += 3;
+ *s = '\0';
+ addReqProv(p, REQUIRE_ANY, tok, NULL);
+ } else {
+ /* ACK! */
+ error(RPMERR_LDD, "ldd is babbling: %s", tok);
+ return 1;
+ }
+ s++;
+ }
+ while (*s && *s != '\n') {
+ s++;
+ }
+ if (*s) {
+ s++;
+ }
+ }
+ freeStringBuf(readBuff);
+
+ return 0;
+}
+
+/*************************************************************/
+/* */
+/* Generate and add header entry from package record */
+/* */
+/*************************************************************/
+
+int processReqProv(Header h, struct PackageRec *p)
+{
+ struct ReqProv *rd;
+ char **nameArray, **namePtr;
+ char **versionArray, **versionPtr;
+ int_32 *flagArray, *flagPtr;
+
+
+ if (p->numProv) {
+ rd = p->reqprov;
+ nameArray = namePtr = malloc(p->numProv * sizeof(*nameArray));
+ message(MESS_VERBOSE, "Provides (%d):", p->numProv);
+ while (rd) {
+ if (rd->flags & REQUIRE_PROVIDES) {
+ message(MESS_VERBOSE, " %s", rd->name);
+ *namePtr++ = rd->name;
+ }
+ rd = rd->next;
+ }
+ addEntry(h, RPMTAG_PROVIDES, STRING_ARRAY_TYPE, nameArray, p->numProv);
+ free(nameArray);
+ }
+
+ if (p->numReq) {
+ rd = p->reqprov;
+ nameArray = namePtr = malloc(p->numReq * sizeof(*nameArray));
+ versionArray = versionPtr = malloc(p->numReq * sizeof(*versionArray));
+ flagArray = flagPtr = malloc(p->numReq * sizeof(*flagArray));
+ message(MESS_VERBOSE, "\nRequires (%d):", p->numReq);
+ while (rd) {
+ if (! (rd->flags & REQUIRE_PROVIDES)) {
+ message(MESS_VERBOSE, " %s", rd->name);
+ *namePtr++ = rd->name;
+ *versionPtr++ = rd->version ? rd->version : "";
+ *flagPtr++ = rd->flags;
+ }
+ rd = rd->next;
+ }
+ message(MESS_VERBOSE, "\n");
+
+ addEntry(h, RPMTAG_REQUIRENAME, STRING_ARRAY_TYPE,
+ nameArray, p->numReq);
+ addEntry(h, RPMTAG_REQUIREVERSION, STRING_ARRAY_TYPE,
+ versionArray, p->numReq);
+ addEntry(h, RPMTAG_REQUIREFLAGS, INT32_TYPE,
+ flagArray, p->numReq);
+
+ free(nameArray);
+ free(versionArray);
+ free(flagArray);
+ }
+
+ return 0;
+}