#include #include #include #include #include #include #include #include #include #define MAX_ENTRIES 10000 #define GROW_ENTRIES 500 #define CMD_BUF_SIZE 2048 struct list_entry { char *path; char *name; char *version; char *release; }; struct list_entry **list = NULL; char *run_cmd(char *cmd, char *name) { int pd[2], pid; static char cmd_buf[CMD_BUF_SIZE]; /* Create a pipe */ pipe(pd); if (pid == -1) { perror("run_cmd - can't open a pipe"); return NULL; } /* Fork */ pid = fork(); if (pid == -1) { perror("run_cmd - can't fork"); return NULL; } if (pid) { /* Parent */ bzero(cmd_buf, CMD_BUF_SIZE); close(pd[1]); read(pd[0], cmd_buf, CMD_BUF_SIZE); close(pd[0]); wait(&pid); return pid ? NULL: cmd_buf; } else { /* Child */ close(pd[0]); dup2(pd[1], 1); execlp("rpm", "rpm", "-qvp", "--qf", cmd, name, NULL); perror("run_cmd"); return NULL; } } #if 0 /* Link with -lrpm */ /* * Borrowed as-is from rpm-3.0.3-0.20 */ int rpmvercmp(const char * a, const char * b) { char oldch1, oldch2; char * str1, * str2; char * one, * two; int rc; int isnum; /* easy comparison to see if versions are identical */ if (!strcmp(a, b)) return 0; str1 = alloca(strlen(a) + 1); str2 = alloca(strlen(b) + 1); strcpy(str1, a); strcpy(str2, b); one = str1; two = str2; /* loop through each version segment of str1 and str2 and compare them */ while (*one && *two) { while (*one && !isalnum(*one)) one++; while (*two && !isalnum(*two)) two++; str1 = one; str2 = two; /* grab first completely alpha or completely numeric segment */ /* leave one and two pointing to the start of the alpha or numeric */ /* segment and walk str1 and str2 to end of segment */ if (isdigit(*str1)) { while (*str1 && isdigit(*str1)) str1++; while (*str2 && isdigit(*str2)) str2++; isnum = 1; } else { while (*str1 && isalpha(*str1)) str1++; while (*str2 && isalpha(*str2)) str2++; isnum = 0; } /* save character at the end of the alpha or numeric segment */ /* so that they can be restored after the comparison */ oldch1 = *str1; *str1 = '\0'; oldch2 = *str2; *str2 = '\0'; /* take care of the case where the two version segments are */ /* different types: one numeric and one alpha */ if (one == str1) return -1; /* arbitrary */ if (two == str2) return -1; if (isnum) { /* this used to be done by converting the digit segments */ /* to ints using atoi() - it's changed because long */ /* digit segments can overflow an int - this should fix that. */ /* throw away any leading zeros - it's a number, right? */ while (*one == '0') one++; while (*two == '0') two++; /* whichever number has more digits wins */ if (strlen(one) > strlen(two)) return 1; if (strlen(two) > strlen(one)) return -1; } /* strcmp will return which one is greater - even if the two */ /* segments are alpha or if they are numeric. don't return */ /* if they are equal because there might be more segments to */ /* compare */ rc = strcmp(one, two); if (rc) return rc; /* restore character that was replaced by null above */ *str1 = oldch1; one = str1; *str2 = oldch2; two = str2; } /* this catches the case where all numeric and alpha segments have */ /* compared identically but the segment sepparating characters were */ /* different */ if ((!*one) && (!*two)) return 0; /* whichever version still has characters left over wins */ if (!*one) return -1; else return 1; } #endif /* * Comparison routine for qsort */ int mycmp(const void *a, const void *b) { int rc; struct list_entry *ap, *bp; ap = *(struct list_entry **)a; bp = *(struct list_entry **)b; if ((rc = strcmp(ap->name, bp->name))) return rc; if ((rc = rpmvercmp(bp->version, ap->version))) return rc; return rpmvercmp(bp->release, ap->release); } int main() { DIR *dir; char *cp1, *cp2, *cp3; struct dirent *entry; int i, entries = 0, list_size = 0, new = 0, old = 0, skipped = 0; /* Read the directory */ dir = opendir("."); while ((entry = readdir(dir))) { /* Increase list size up to NUM_ENTRIES + GROW_ENTRIES - 1 */ if (entries >= list_size && list_size < MAX_ENTRIES) { list_size += GROW_ENTRIES; list = realloc(list, list_size * sizeof(struct list_entry)); if (! list) { fprintf(stderr, "too many entries\n"); exit(1); } } /* Process ony RPM/SRPM files */ if (strstr(entry->d_name, ".rpm")) { list[++entries] = (struct list_entry *)malloc(sizeof(struct list_entry)); if (!list[entries]) { perror("allocating entry"); return 1; } /* Find name */ cp1 = run_cmd("%{NAME}\n%{VERSION}\n%{RELEASE}", entry->d_name); if (!cp1) { free(list[entries--]); continue; } cp2 = index(cp1, '\n'); *cp2 = '\0'; cp3 = index(cp2 + 1, '\n'); *cp3 = '\0'; list[entries]->path = (char *)malloc(strlen(entry->d_name) + 1); if (!list[entries]->path) { perror("allocating path"); return 1; } strcpy(list[entries]->path, entry->d_name); list[entries]->name = (char *)malloc(strlen(cp1) + 1); if (!list[entries]->name) { perror("allocating name"); return 1; } strcpy(list[entries]->name, cp1); list[entries]->version = (char *)malloc(strlen(cp2 + 1) + 1); if (!list[entries]->version) { perror("allocating version"); return 1; } strcpy(list[entries]->version, cp2 + 1); list[entries]->release = (char *)malloc(strlen(cp3 + 1) + 1); if (!list[entries]->release) { perror("allocating release"); return 1; } strcpy(list[entries]->release, cp3 + 1); } else { skipped++; } } closedir(dir); /* Sort the entries */ qsort(list + 1, entries, sizeof(struct list_entry *), mycmp); /* List the newest entries */ for (i = 1; i <= entries; i++) { if (i == 1 || strcmp(list[i]->name, list[i - 1]->name)) { printf("NEW %s\n", list[i]->path); new++; } else { printf("OLD %s\n", list[i]->path); old++; } } fprintf(stderr, "Found %d entries (%d new, %d old)\nSkipped %d entries\nProcessed %d entries\n", entries, new, old, skipped, entries + skipped); return 0; }