2005-08-08 20:10:06 +00:00
|
|
|
/*-
|
|
|
|
* 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));
|
2005-08-09 09:49:01 +00:00
|
|
|
if (d == NULL)
|
|
|
|
err(1, "malloc(DEP)");
|
2005-08-08 20:10:06 +00:00
|
|
|
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;
|
|
|
|
}
|