diff options
Diffstat (limited to 'usr.sbin/portsnap/make_index/make_index.c')
-rw-r--r-- | usr.sbin/portsnap/make_index/make_index.c | 491 |
1 files changed, 491 insertions, 0 deletions
diff --git a/usr.sbin/portsnap/make_index/make_index.c b/usr.sbin/portsnap/make_index/make_index.c new file mode 100644 index 0000000..6139fad --- /dev/null +++ b/usr.sbin/portsnap/make_index/make_index.c @@ -0,0 +1,491 @@ +/*- + * Copyright 2005 Colin Percival + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +struct port; + +typedef union { + char * name; + struct port * p; +} DEP; + +typedef struct port { + char * pkgname; + char * portdir; + char * prefix; + char * comment; + char * pkgdescr; + char * maintainer; + char * categories; + size_t n_edep; + DEP * edep; + size_t n_pdep; + DEP * pdep; + size_t n_fdep; + DEP * fdep; + size_t n_bdep; + DEP * bdep; + size_t n_rdep; + DEP * rdep; + char * www; + int recursed; +} PORT; + +static void usage(void); +static char * strdup2(const char *str); +static DEP * makelist(char * str, size_t * n); +static PORT * portify(char * line); +static int portcompare(char * a, char * b); +static void heapifyports(PORT **pp, size_t size, size_t pos); +static PORT * findport(PORT ** pp, size_t st, size_t en, char * name); +static void translateport(PORT ** pp, size_t pplen, PORT * p); +static DEP * recurse_one(DEP * d, size_t * nd); +static void recurse(PORT * p); +static void heapifypkgs(DEP * d, size_t size, size_t pos); +static void sortpkgs(DEP * d, size_t nd); +static void printport(PORT * p); + +static void +usage(void) +{ + + fprintf(stderr, "usage: make_index file\n"); + exit(1); + /* NOTREACHED */ +} + +static char * +strdup2(const char *str) +{ + char * r; + + r = strdup(str); + if (r == NULL) + err(1, "strdup"); + return r; +} + +/* Take a space-separated list and return an array of (char *) */ +static DEP * +makelist(char * str, size_t * n) +{ + DEP * d; + size_t i; + + /* No depends at all? */ + if (str[0] == 0) { + *n = 0; + return NULL; + } + + /* Count the number of fields */ + *n = 1; + for (i = 0; str[i] != 0; i++) + if (str[i] == ' ') + (*n)++; + + /* Allocate and fill an array */ + d = malloc(*n * sizeof(DEP)); + for (i = 0; i < *n; i++) { + d[i].name = strdup2(strsep(&str, " ")); + + /* Strip trailing slashes */ + if (d[i].name[strlen(d[i].name) - 1] == '/') + d[i].name[strlen(d[i].name) - 1] = 0; + } + + return d; +} + +/* Take a port's describe line and split it into fields */ +static PORT * +portify(char * line) +{ + PORT * p; + size_t i, n; + + /* Verify that line has the right number of fields */ + for (n = i = 0; line[i] != 0; i++) + if (line[i] == '|') + n++; + if (n != 12) + errx(1, "Port describe line is corrupt:\n%s\n", line); + + p = malloc(sizeof(PORT)); + if (p == NULL) + err(1, "malloc(PORT)"); + + p->pkgname = strdup2(strsep(&line, "|")); + p->portdir = strdup2(strsep(&line, "|")); + p->prefix = strdup2(strsep(&line, "|")); + p->comment = strdup2(strsep(&line, "|")); + p->pkgdescr = strdup2(strsep(&line, "|")); + p->maintainer = strdup2(strsep(&line, "|")); + p->categories = strdup2(strsep(&line, "|")); + p->edep = makelist(strsep(&line, "|"), &p->n_edep); + p->pdep = makelist(strsep(&line, "|"), &p->n_pdep); + p->fdep = makelist(strsep(&line, "|"), &p->n_fdep); + p->bdep = makelist(strsep(&line, "|"), &p->n_bdep); + p->rdep = makelist(strsep(&line, "|"), &p->n_rdep); + p->www = strdup2(strsep(&line, "|")); + + p->recursed = 0; + + /* + * line will now be equal to NULL -- we counted the field + * separators at the top of the function. + */ + + return p; +} + +/* Returns -1, 0, or 1 based on a comparison of the portdir strings */ +static int +portcompare(char * a, char * b) +{ + size_t i; + + /* Find first non-matching position */ + for (i = 0; ; i++) { + if (a[i] != b[i]) + break; + if (a[i] == 0) /* End of strings */ + return 0; + } + + /* One string is a prefix of the other */ + if (a[i] == 0) + return -1; + if (b[i] == 0) + return 1; + + /* One string has a category which is a prefix of the other */ + if (a[i] == '/') + return -1; + if (b[i] == '/') + return 1; + + /* The two strings are simply different */ + if (a[i] < b[i]) + return -1; + else + return 1; +} + +/* Heapify (PORT *) number pos in a pseudo-heap pp[0]..pp[size - 1] */ +static void +heapifyports(PORT **pp, size_t size, size_t pos) +{ + size_t i = pos; + PORT * tmp; + +top: + /* Find the largest value out of {pos, 2*pos+1, 2*pos+2} */ + if ((2 * pos + 1 < size) && + (portcompare(pp[i]->portdir, pp[2 * pos + 1]->portdir) < 0)) + i = 2 * pos + 1; + if ((2 * pos + 2 < size) && + (portcompare(pp[i]->portdir, pp[2 * pos + 2]->portdir) < 0)) + i = 2 * pos + 2; + + /* If necessary, swap elements and iterate down the tree. */ + if (i != pos) { + tmp = pp[pos]; + pp[pos] = pp[i]; + pp[i] = tmp; + pos = i; + goto top; + } +} + +/* Translate a port directory name into a (PORT *), and free the name */ +static PORT * +findport(PORT ** pp, size_t st, size_t en, char * name) +{ + size_t mid; + int r; + + if (st == en) + errx(1, "Unresolved dependency: %s", name); + + mid = (st + en) / 2; + r = portcompare(pp[mid]->portdir, name); + + if (r == 0) { + free(name); + return pp[mid]; + } else if (r < 0) + return findport(pp, mid + 1, en, name); + else + return findport(pp, st, mid, name); +} + +/* Translate all depends from names into PORT *s */ +static void +translateport(PORT ** pp, size_t pplen, PORT * p) +{ + size_t i; + + for (i = 0; i < p->n_edep; i++) + p->edep[i].p = findport(pp, 0, pplen, p->edep[i].name); + for (i = 0; i < p->n_pdep; i++) + p->pdep[i].p = findport(pp, 0, pplen, p->pdep[i].name); + for (i = 0; i < p->n_fdep; i++) + p->fdep[i].p = findport(pp, 0, pplen, p->fdep[i].name); + for (i = 0; i < p->n_bdep; i++) + p->bdep[i].p = findport(pp, 0, pplen, p->bdep[i].name); + for (i = 0; i < p->n_rdep; i++) + p->rdep[i].p = findport(pp, 0, pplen, p->rdep[i].name); +} + +/* Recurse on one specific depends list */ +static DEP * +recurse_one(DEP * d, size_t * nd) +{ + size_t i, j, k, n, N; + + N = n = *nd; + for (i = 0; i < n; i++) { + recurse(d[i].p); + for (j = 0; j < d[i].p->n_rdep; j++) { + for (k = 0; k < N; k++) { + if (d[i].p->rdep[j].p == d[k].p) + break; + } + if (k == N) { + N++; + if (N >= *nd) { + *nd += *nd; + d = realloc(d, *nd * sizeof(DEP)); + if (d == NULL) + err(1, "realloc(d)"); + } + d[k].p = d[i].p->rdep[j].p; + } + } + } + *nd = N; + + return d; +} + +/* Recurse on the depends lists */ +static void +recurse(PORT * p) +{ + if (p->recursed != 0) + return; + p->recursed = 1; + + p->edep = recurse_one(p->edep, &p->n_edep); + p->pdep = recurse_one(p->pdep, &p->n_pdep); + p->fdep = recurse_one(p->fdep, &p->n_fdep); + p->bdep = recurse_one(p->bdep, &p->n_bdep); + p->rdep = recurse_one(p->rdep, &p->n_rdep); +} + +/* Heapify an element in a package list */ +static void +heapifypkgs(DEP * d, size_t size, size_t pos) +{ + size_t i = pos; + PORT * tmp; + +top: + /* Find the largest value out of {pos, 2*pos+1, 2*pos+2} */ + if ((2 * pos + 1 < size) && + (strcmp(d[i].p->pkgname, d[2 * pos + 1].p->pkgname) < 0)) + i = 2 * pos + 1; + if ((2 * pos + 2 < size) && + (strcmp(d[i].p->pkgname, d[2 * pos + 2].p->pkgname) < 0)) + i = 2 * pos + 2; + + /* If necessary, swap elements and iterate down the tree. */ + if (i != pos) { + tmp = d[pos].p; + d[pos].p = d[i].p; + d[i].p = tmp; + pos = i; + goto top; + } +} + +/* Sort a list of dependent packages in alphabetical order */ +static void +sortpkgs(DEP * d, size_t nd) +{ + size_t i; + PORT * tmp; + + if (nd == 0) + return; + + for (i = nd; i > 0; i--) + heapifypkgs(d, nd, i - 1); /* Build a heap */ + for (i = nd - 1; i > 0; i--) { + tmp = d[0].p; /* Extract elements */ + d[0].p = d[i].p; + d[i].p = tmp; + heapifypkgs(d, i, 0); /* And re-heapify */ + } +} + +/* Output an index line for the given port. */ +static void +printport(PORT * p) +{ + size_t i; + + sortpkgs(p->edep, p->n_edep); + sortpkgs(p->pdep, p->n_pdep); + sortpkgs(p->fdep, p->n_fdep); + sortpkgs(p->bdep, p->n_bdep); + sortpkgs(p->rdep, p->n_rdep); + + printf("%s|%s|%s|%s|%s|%s|%s|", + p->pkgname, p->portdir, p->prefix, p->comment, p->pkgdescr, + p->maintainer, p->categories); + for (i = 0; i < p->n_bdep; i++) + printf("%s%s", i ? " " : "", p->bdep[i].p->pkgname); + printf("|"); + for (i = 0; i < p->n_rdep; i++) + printf("%s%s", i ? " " : "", p->rdep[i].p->pkgname); + printf("|"); + printf("%s|", p->www); + for (i = 0; i < p->n_edep; i++) + printf("%s%s", i ? " " : "", p->edep[i].p->pkgname); + printf("|"); + for (i = 0; i < p->n_pdep; i++) + printf("%s%s", i ? " " : "", p->pdep[i].p->pkgname); + printf("|"); + for (i = 0; i < p->n_fdep; i++) + printf("%s%s", i ? " " : "", p->fdep[i].p->pkgname); + printf("\n"); +} + +/* + * Algorithm: + * 1. Suck in all the data, splitting into fields. + * 2. Sort the ports according to port directory. + * 3. Using a binary search, translate each dependency from a + * port directory name into a pointer to a port. + * 4. Recursively follow dependencies, expanding the lists of + * pointers as needed (using realloc). + * 5. Iterate through the ports, printing them out (remembering + * to list the dependent ports in alphabetical order). + */ + +int +main(int argc, char *argv[]) +{ + FILE * f; + char * line; + size_t linelen; + PORT ** pp; /* Array of pointers to PORTs */ + PORT * tmp; + size_t pplen; /* Allocated size of array */ + size_t i; + + if (argc != 2) + usage(); + if ((f = fopen(argv[1], "r")) == NULL) + err(1, "fopen(%s)", argv[1]); + + pplen = 1024; + if ((pp = malloc(pplen * sizeof(PORT *))) == NULL) + err(1, "malloc(pp)"); + + /* + * 1. Suck in all the data, splitting into fields. + */ + for(i = 0; (line = fgetln(f, &linelen)) != NULL; i++) { + if (line[linelen - 1] != '\n') + errx(1, "Unterminated line encountered"); + line[linelen - 1] = 0; + + /* Enlarge array if needed */ + if (i >= pplen) { + pplen *= 2; + if ((pp = realloc(pp, pplen * sizeof(PORT *))) == NULL) + err(1, "realloc(pp)"); + } + + pp[i] = portify(line); + } + /* Reallocate to the correct size */ + pplen = i; + if ((pp = realloc(pp, pplen * sizeof(PORT *))) == NULL) + err(1, "realloc(pp)"); + + /* Make sure we actually reached the EOF */ + if (!feof(f)) + err(1, "fgetln(%s)", argv[1]); + /* Close the describes file */ + if (fclose(f) != 0) + err(1, "fclose(%s)", argv[1]); + + /* + * 2. Sort the ports according to port directory. + */ + for (i = pplen; i > 0; i--) + heapifyports(pp, pplen, i - 1); /* Build a heap */ + for (i = pplen - 1; i > 0; i--) { + tmp = pp[0]; /* Extract elements */ + pp[0] = pp[i]; + pp[i] = tmp; + heapifyports(pp, i, 0); /* And re-heapify */ + } + + /* + * 3. Using a binary search, translate each dependency from a + * port directory name into a pointer to a port. + */ + for (i = 0; i < pplen; i++) + translateport(pp, pplen, pp[i]); + + /* + * 4. Recursively follow dependencies, expanding the lists of + * pointers as needed (using realloc). + */ + for (i = 0; i < pplen; i++) + recurse(pp[i]); + + /* + * 5. Iterate through the ports, printing them out (remembering + * to list the dependent ports in alphabetical order). + */ + for (i = 0; i < pplen; i++) + printport(pp[i]); + + return 0; +} |