[Midnightbsd-cvs] src [12167] trunk/usr.sbin: add portsnap

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Sat Feb 9 13:20:51 EST 2019


Revision: 12167
          http://svnweb.midnightbsd.org/src/?rev=12167
Author:   laffer1
Date:     2019-02-09 13:20:51 -0500 (Sat, 09 Feb 2019)
Log Message:
-----------
add portsnap

Modified Paths:
--------------
    trunk/usr.sbin/Makefile

Added Paths:
-----------
    trunk/usr.sbin/portsnap/
    trunk/usr.sbin/portsnap/Makefile
    trunk/usr.sbin/portsnap/Makefile.inc
    trunk/usr.sbin/portsnap/make_index/
    trunk/usr.sbin/portsnap/make_index/Makefile
    trunk/usr.sbin/portsnap/make_index/make_index.c
    trunk/usr.sbin/portsnap/phttpget/
    trunk/usr.sbin/portsnap/phttpget/Makefile
    trunk/usr.sbin/portsnap/phttpget/phttpget.8
    trunk/usr.sbin/portsnap/phttpget/phttpget.c
    trunk/usr.sbin/portsnap/portsnap/
    trunk/usr.sbin/portsnap/portsnap/Makefile
    trunk/usr.sbin/portsnap/portsnap/portsnap.8
    trunk/usr.sbin/portsnap/portsnap/portsnap.sh

Modified: trunk/usr.sbin/Makefile
===================================================================
--- trunk/usr.sbin/Makefile	2019-02-09 18:17:48 UTC (rev 12166)
+++ trunk/usr.sbin/Makefile	2019-02-09 18:20:51 UTC (rev 12167)
@@ -263,6 +263,10 @@
 SUBDIR+=	pmcstat
 .endif
 
+.if ${MK_PORTSNAP} != "no"
+SUBDIR+= portsnap
+.endif
+
 .if ${MK_PPP} != "no"
 SUBDIR+=	ppp
 .endif

Added: trunk/usr.sbin/portsnap/Makefile
===================================================================
--- trunk/usr.sbin/portsnap/Makefile	                        (rev 0)
+++ trunk/usr.sbin/portsnap/Makefile	2019-02-09 18:20:51 UTC (rev 12167)
@@ -0,0 +1,5 @@
+# $MidnightBSD$
+
+SUBDIR=	portsnap make_index phttpget
+
+.include <bsd.subdir.mk>


Property changes on: trunk/usr.sbin/portsnap/Makefile
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/portsnap/Makefile.inc
===================================================================
--- trunk/usr.sbin/portsnap/Makefile.inc	                        (rev 0)
+++ trunk/usr.sbin/portsnap/Makefile.inc	2019-02-09 18:20:51 UTC (rev 12167)
@@ -0,0 +1,5 @@
+# $MidnightBSD$
+
+LIBEXECDIR?=	/usr/libexec
+
+.include "../Makefile.inc"


Property changes on: trunk/usr.sbin/portsnap/Makefile.inc
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/portsnap/make_index/Makefile
===================================================================
--- trunk/usr.sbin/portsnap/make_index/Makefile	                        (rev 0)
+++ trunk/usr.sbin/portsnap/make_index/Makefile	2019-02-09 18:20:51 UTC (rev 12167)
@@ -0,0 +1,8 @@
+# $MidnightBSD$
+
+PROG=	make_index
+MAN=
+
+BINDIR=	${LIBEXECDIR}
+
+.include <bsd.prog.mk>


Property changes on: trunk/usr.sbin/portsnap/make_index/Makefile
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/portsnap/make_index/make_index.c
===================================================================
--- trunk/usr.sbin/portsnap/make_index/make_index.c	                        (rev 0)
+++ trunk/usr.sbin/portsnap/make_index/make_index.c	2019-02-09 18:20:51 UTC (rev 12167)
@@ -0,0 +1,513 @@
+/*-
+ * 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>
+__MBSDID("$MidnightBSD$");
+
+#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, char * from);
+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));
+	if (d == NULL)
+		err(1, "malloc(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, char * from)
+{
+	size_t mid;
+	int r;
+
+	if (st == en)
+		errx(1, "%s: no entry for %s", from, 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, from);
+	else
+		return findport(pp, st, mid, name, from);
+}
+
+/* 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, p->portdir);
+	for (i = 0; i < p->n_pdep; i++)
+		p->pdep[i].p = findport(pp, 0, pplen, p->pdep[i].name, p->portdir);
+	for (i = 0; i < p->n_fdep; i++)
+		p->fdep[i].p = findport(pp, 0, pplen, p->fdep[i].name, p->portdir);
+	for (i = 0; i < p->n_bdep; i++)
+		p->bdep[i].p = findport(pp, 0, pplen, p->bdep[i].name, p->portdir);
+	for (i = 0; i < p->n_rdep; i++)
+		p->rdep[i].p = findport(pp, 0, pplen, p->rdep[i].name, p->portdir);
+}
+
+/* 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)
+{
+	switch (p->recursed) {
+	case 0:
+		/* First time we've seen this port */
+		p->recursed = 1;
+		break;
+	case 1:
+		/* We're in the middle of recursing this port */
+		errx(1, "Circular dependency loop found: %s"
+		    " depends upon itself.\n", p->pkgname);
+	case 2:
+		/* This port has already been recursed */
+		return;
+	}
+
+	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);
+
+	/* Finished recursing on this port */
+	p->recursed = 2;
+}
+
+/* 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.
+ * 1a. If there are no ports, there is no INDEX.
+ * 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]);
+
+	/*
+	 * 1a. If there are no ports, there is no INDEX.
+	 */
+	if (pplen == 0)
+		return 0;
+
+	/*
+	 * 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;
+}


Property changes on: trunk/usr.sbin/portsnap/make_index/make_index.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/portsnap/phttpget/Makefile
===================================================================
--- trunk/usr.sbin/portsnap/phttpget/Makefile	                        (rev 0)
+++ trunk/usr.sbin/portsnap/phttpget/Makefile	2019-02-09 18:20:51 UTC (rev 12167)
@@ -0,0 +1,8 @@
+# $MidnightBSD$
+
+PROG=	phttpget
+MAN=	phttpget.8
+
+BINDIR=	${LIBEXECDIR}
+
+.include <bsd.prog.mk>


Property changes on: trunk/usr.sbin/portsnap/phttpget/Makefile
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/portsnap/phttpget/phttpget.8
===================================================================
--- trunk/usr.sbin/portsnap/phttpget/phttpget.8	                        (rev 0)
+++ trunk/usr.sbin/portsnap/phttpget/phttpget.8	2019-02-09 18:20:51 UTC (rev 12167)
@@ -0,0 +1,89 @@
+.\" $MidnightBSD$
+.\"-
+.\" Copyright (c) 2015 Xin Li <delphij at FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+.\"
+.\" $FreeBSD: stable/10/usr.sbin/portsnap/phttpget/phttpget.8 306427 2016-09-29 01:47:02Z emaste $
+.\"
+.Dd January 3, 2015
+.Dt PHTTPGET 8
+.Os
+.Sh NAME
+.Nm phttpget
+.Nd retrieve multiple files via pipelined HTTP
+.Sh SYNOPSIS
+.Nm
+.Ar server
+.Ar file ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is a minimalist pipelined HTTP client,
+which is used to retrieve multiple
+.Ar file Ns s
+from one
+.Ar server ,
+and saves the downloaded files in the current working directory,
+using the last portion of their download path as file names.
+.Pp
+By making several "in flight" HTTP requests,
+it can dramatically increase performance when a large number of
+small files need to be downloaded.
+.Pp
+The
+.Xr freebsd-update 8
+and
+.Xr portsnap 8
+tools use
+.Nm
+to download binary patch files.
+.Sh ENVIRONMENT
+.Bl -tag -width HTTP_PROXY_AUTH
+.It Ev HTTP_PROXY
+URL of the proxy to use for HTTP requests.
+.It Ev HTTP_PROXY_AUTH
+Authorization parameters for the HTTP proxy.
+.It Ev HTTP_USER_AGENT
+The User-Agent string to use for HTTP requests.
+The default is
+.Dq phttpget/0.1 .
+.It Ev HTTP_TIMEOUT
+Timeout for HTTP request in seconds.
+.El
+.Sh SEE ALSO
+.Xr fetch 1 ,
+.Xr freebsd-update 8 ,
+.Xr portsnap 8
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was written by
+.An Colin Percival Aq Mt cperciva at FreeBSD.org
+for use with
+.Xr portsnap 8
+and later with
+.Xr freebsd-update 8 .
+This manual page was written by
+.An Xin LI Aq Mt delphij at FreeBSD.org .


Property changes on: trunk/usr.sbin/portsnap/phttpget/phttpget.8
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/portsnap/phttpget/phttpget.c
===================================================================
--- trunk/usr.sbin/portsnap/phttpget/phttpget.c	                        (rev 0)
+++ trunk/usr.sbin/portsnap/phttpget/phttpget.c	2019-02-09 18:20:51 UTC (rev 12167)
@@ -0,0 +1,731 @@
+/* $MidnightBSD$ */
+/*-
+ * 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: stable/10/usr.sbin/portsnap/phttpget/phttpget.c 306428 2016-09-29 01:53:29Z emaste $");
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+static const char *	env_HTTP_PROXY;
+static char *		env_HTTP_PROXY_AUTH;
+static const char *	env_HTTP_USER_AGENT;
+static char *		env_HTTP_TIMEOUT;
+static const char *	proxyport;
+static char *		proxyauth;
+
+static struct timeval	timo = { 15, 0};
+
+static void
+usage(void)
+{
+
+	fprintf(stderr, "usage: phttpget server [file ...]\n");
+	exit(EX_USAGE);
+}
+
+/*
+ * Base64 encode a string; the string returned, if non-NULL, is
+ * allocated using malloc() and must be freed by the caller.
+ */
+static char *
+b64enc(const char *ptext)
+{
+	static const char base64[] =
+	    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+	    "abcdefghijklmnopqrstuvwxyz"
+	    "0123456789+/";
+	const char *pt;
+	char *ctext, *pc;
+	size_t ptlen, ctlen;
+	uint32_t t;
+	unsigned int j;
+
+	/*
+	 * Encoded length is 4 characters per 3-byte block or partial
+	 * block of plaintext, plus one byte for the terminating NUL
+	 */
+	ptlen = strlen(ptext);
+	if (ptlen > ((SIZE_MAX - 1) / 4) * 3 - 2)
+		return NULL;	/* Possible integer overflow */
+	ctlen = 4 * ((ptlen + 2) / 3) + 1;
+	if ((ctext = malloc(ctlen)) == NULL)
+		return NULL;
+	ctext[ctlen - 1] = 0;
+
+	/*
+	 * Scan through ptext, reading up to 3 bytes from ptext and
+	 * writing 4 bytes to ctext, until we run out of input.
+	 */
+	for (pt = ptext, pc = ctext; ptlen; ptlen -= 3, pc += 4) {
+		/* Read 3 bytes */
+		for (t = j = 0; j < 3; j++) {
+			t <<= 8;
+			if (j < ptlen)
+				t += *pt++;
+		}
+
+		/* Write 4 bytes */
+		for (j = 0; j < 4; j++) {
+			if (j <= ptlen + 1)
+				pc[j] = base64[(t >> 18) & 0x3f];
+			else
+				pc[j] = '=';
+			t <<= 6;
+		}
+
+		/* If we're done, exit the loop */
+		if (ptlen <= 3)
+			break;
+	}
+
+	return (ctext);
+}
+
+static void
+readenv(void)
+{
+	char *proxy_auth_userpass, *proxy_auth_userpass64, *p;
+	char *proxy_auth_user = NULL;
+	char *proxy_auth_pass = NULL;
+	long http_timeout;
+
+	env_HTTP_PROXY = getenv("HTTP_PROXY");
+	if (env_HTTP_PROXY == NULL)
+		env_HTTP_PROXY = getenv("http_proxy");
+	if (env_HTTP_PROXY != NULL) {
+		if (strncmp(env_HTTP_PROXY, "http://", 7) == 0)
+			env_HTTP_PROXY += 7;
+		p = strchr(env_HTTP_PROXY, '/');
+		if (p != NULL)
+			*p = 0;
+		p = strchr(env_HTTP_PROXY, ':');
+		if (p != NULL) {
+			*p = 0;
+			proxyport = p + 1;
+		} else
+			proxyport = "3128";
+	}
+
+	env_HTTP_PROXY_AUTH = getenv("HTTP_PROXY_AUTH");
+	if ((env_HTTP_PROXY != NULL) &&
+	    (env_HTTP_PROXY_AUTH != NULL) &&
+	    (strncasecmp(env_HTTP_PROXY_AUTH, "basic:" , 6) == 0)) {
+		/* Ignore authentication scheme */
+		(void) strsep(&env_HTTP_PROXY_AUTH, ":");
+
+		/* Ignore realm */
+		(void) strsep(&env_HTTP_PROXY_AUTH, ":");
+
+		/* Obtain username and password */
+		proxy_auth_user = strsep(&env_HTTP_PROXY_AUTH, ":");
+		proxy_auth_pass = env_HTTP_PROXY_AUTH;
+	}
+
+	if ((proxy_auth_user != NULL) && (proxy_auth_pass != NULL)) {
+		asprintf(&proxy_auth_userpass, "%s:%s",
+		    proxy_auth_user, proxy_auth_pass);
+		if (proxy_auth_userpass == NULL)
+			err(1, "asprintf");
+
+		proxy_auth_userpass64 = b64enc(proxy_auth_userpass);
+		if (proxy_auth_userpass64 == NULL)
+			err(1, "malloc");
+
+		asprintf(&proxyauth, "Proxy-Authorization: Basic %s\r\n",
+		    proxy_auth_userpass64);
+		if (proxyauth == NULL)
+			err(1, "asprintf");
+
+		free(proxy_auth_userpass);
+		free(proxy_auth_userpass64);
+	} else
+		proxyauth = NULL;
+
+	env_HTTP_USER_AGENT = getenv("HTTP_USER_AGENT");
+	if (env_HTTP_USER_AGENT == NULL)
+		env_HTTP_USER_AGENT = "phttpget/0.1";
+
+	env_HTTP_TIMEOUT = getenv("HTTP_TIMEOUT");
+	if (env_HTTP_TIMEOUT != NULL) {
+		http_timeout = strtol(env_HTTP_TIMEOUT, &p, 10);
+		if ((*env_HTTP_TIMEOUT == '\0') || (*p != '\0') ||
+		    (http_timeout < 0))
+			warnx("HTTP_TIMEOUT (%s) is not a positive integer",
+			    env_HTTP_TIMEOUT);
+		else
+			timo.tv_sec = http_timeout;
+	}
+}
+
+static int
+makerequest(char ** buf, char * path, char * server, int connclose)
+{
+	int buflen;
+
+	buflen = asprintf(buf,
+	    "GET %s%s/%s HTTP/1.1\r\n"
+	    "Host: %s\r\n"
+	    "User-Agent: %s\r\n"
+	    "%s"
+	    "%s"
+	    "\r\n",
+	    env_HTTP_PROXY ? "http://" : "",
+	    env_HTTP_PROXY ? server : "",
+	    path, server, env_HTTP_USER_AGENT,
+	    proxyauth ? proxyauth : "",
+	    connclose ? "Connection: Close\r\n" : "Connection: Keep-Alive\r\n");
+	if (buflen == -1)
+		err(1, "asprintf");
+	return(buflen);
+}
+
+static int
+readln(int sd, char * resbuf, int * resbuflen, int * resbufpos)
+{
+	ssize_t len;
+
+	while (strnstr(resbuf + *resbufpos, "\r\n",
+	    *resbuflen - *resbufpos) == NULL) {
+		/* Move buffered data to the start of the buffer */
+		if (*resbufpos != 0) {
+			memmove(resbuf, resbuf + *resbufpos,
+			    *resbuflen - *resbufpos);
+			*resbuflen -= *resbufpos;
+			*resbufpos = 0;
+		}
+
+		/* If the buffer is full, complain */
+		if (*resbuflen == BUFSIZ)
+			return -1;
+
+		/* Read more data into the buffer */
+		len = recv(sd, resbuf + *resbuflen, BUFSIZ - *resbuflen, 0);
+		if ((len == 0) ||
+		    ((len == -1) && (errno != EINTR)))
+			return -1;
+
+		if (len != -1)
+			*resbuflen += len;
+	}
+
+	return 0;
+}
+
+static int
+copybytes(int sd, int fd, off_t copylen, char * resbuf, int * resbuflen,
+    int * resbufpos)
+{
+	ssize_t len;
+
+	while (copylen) {
+		/* Write data from resbuf to fd */
+		len = *resbuflen - *resbufpos;
+		if (copylen < len)
+			len = copylen;
+		if (len > 0) {
+			if (fd != -1)
+				len = write(fd, resbuf + *resbufpos, len);
+			if (len == -1)
+				err(1, "write");
+			*resbufpos += len;
+			copylen -= len;
+			continue;
+		}
+
+		/* Read more data into buffer */
+		len = recv(sd, resbuf, BUFSIZ, 0);
+		if (len == -1) {
+			if (errno == EINTR)
+				continue;
+			return -1;
+		} else if (len == 0) {
+			return -2;
+		} else {
+			*resbuflen = len;
+			*resbufpos = 0;
+		}
+	}
+
+	return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+	struct addrinfo hints;	/* Hints to getaddrinfo */
+	struct addrinfo *res;	/* Pointer to server address being used */
+	struct addrinfo *res0;	/* Pointer to server addresses */
+	char * resbuf = NULL;	/* Response buffer */
+	int resbufpos = 0;	/* Response buffer position */
+	int resbuflen = 0;	/* Response buffer length */
+	char * eolp;		/* Pointer to "\r\n" within resbuf */
+	char * hln;		/* Pointer within header line */
+	char * servername;	/* Name of server */
+	char * fname = NULL;	/* Name of downloaded file */
+	char * reqbuf = NULL;	/* Request buffer */
+	int reqbufpos = 0;	/* Request buffer position */
+	int reqbuflen = 0;	/* Request buffer length */
+	ssize_t len;		/* Length sent or received */
+	int nreq = 0;		/* Number of next request to send */
+	int nres = 0;		/* Number of next reply to receive */
+	int pipelined = 0;	/* != 0 if connection in pipelined mode. */
+	int keepalive;		/* != 0 if HTTP/1.0 keep-alive rcvd. */
+	int sd = -1;		/* Socket descriptor */
+	int sdflags = 0;	/* Flags on the socket sd */
+	int fd = -1;		/* Descriptor for download target file */
+	int error;		/* Error code */
+	int statuscode;		/* HTTP Status code */
+	off_t contentlength;	/* Value from Content-Length header */
+	int chunked;		/* != if transfer-encoding is chunked */
+	off_t clen;		/* Chunk length */
+	int firstreq = 0;	/* # of first request for this connection */
+	int val;		/* Value used for setsockopt call */
+
+	/* Check that the arguments are sensible */
+	if (argc < 2)
+		usage();
+
+	/* Read important environment variables */
+	readenv();
+
+	/* Get server name and adjust arg[cv] to point at file names */
+	servername = argv[1];
+	argv += 2;
+	argc -= 2;
+
+	/* Allocate response buffer */
+	resbuf = malloc(BUFSIZ);
+	if (resbuf == NULL)
+		err(1, "malloc");
+
+	/* Look up server */
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = PF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+	error = getaddrinfo(env_HTTP_PROXY ? env_HTTP_PROXY : servername,
+	    env_HTTP_PROXY ? proxyport : "http", &hints, &res0);
+	if (error)
+		errx(1, "host = %s, port = %s: %s",
+		    env_HTTP_PROXY ? env_HTTP_PROXY : servername,
+		    env_HTTP_PROXY ? proxyport : "http",
+		    gai_strerror(error));
+	if (res0 == NULL)
+		errx(1, "could not look up %s", servername);
+	res = res0;
+
+	/* Do the fetching */
+	while (nres < argc) {
+		/* Make sure we have a connected socket */
+		for (; sd == -1; res = res->ai_next) {
+			/* No addresses left to try :-( */
+			if (res == NULL)
+				errx(1, "Could not connect to %s", servername);
+
+			/* Create a socket... */
+			sd = socket(res->ai_family, res->ai_socktype,
+			    res->ai_protocol);
+			if (sd == -1)
+				continue;
+
+			/* ... set 15-second timeouts ... */
+			setsockopt(sd, SOL_SOCKET, SO_SNDTIMEO,
+			    (void *)&timo, (socklen_t)sizeof(timo));
+			setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO,
+			    (void *)&timo, (socklen_t)sizeof(timo));
+
+			/* ... disable SIGPIPE generation ... */
+			val = 1;
+			setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE,
+			    (void *)&val, sizeof(int));
+
+			/* ... and connect to the server. */
+			if(connect(sd, res->ai_addr, res->ai_addrlen)) {
+				close(sd);
+				sd = -1;
+				continue;
+			}
+
+			firstreq = nres;
+		}
+
+		/*
+		 * If in pipelined HTTP mode, put socket into non-blocking
+		 * mode, since we're probably going to want to try to send
+		 * several HTTP requests.
+		 */
+		if (pipelined) {
+			sdflags = fcntl(sd, F_GETFL);
+			if (fcntl(sd, F_SETFL, sdflags | O_NONBLOCK) == -1)
+				err(1, "fcntl");
+		}
+
+		/* Construct requests and/or send them without blocking */
+		while ((nreq < argc) && ((reqbuf == NULL) || pipelined)) {
+			/* If not in the middle of a request, make one */
+			if (reqbuf == NULL) {
+				reqbuflen = makerequest(&reqbuf, argv[nreq],
+				    servername, (nreq == argc - 1));
+				reqbufpos = 0;
+			}
+
+			/* If in pipelined mode, try to send the request */
+			if (pipelined) {
+				while (reqbufpos < reqbuflen) {
+					len = send(sd, reqbuf + reqbufpos,
+					    reqbuflen - reqbufpos, 0);
+					if (len == -1)
+						break;
+					reqbufpos += len;
+				}
+				if (reqbufpos < reqbuflen) {
+					if (errno != EAGAIN)
+						goto conndied;
+					break;
+				} else {
+					free(reqbuf);
+					reqbuf = NULL;
+					nreq++;
+				}
+			}
+		}
+
+		/* Put connection back into blocking mode */
+		if (pipelined) {
+			if (fcntl(sd, F_SETFL, sdflags) == -1)
+				err(1, "fcntl");
+		}
+
+		/* Do we need to blocking-send a request? */
+		if (nres == nreq) {
+			while (reqbufpos < reqbuflen) {
+				len = send(sd, reqbuf + reqbufpos,
+				    reqbuflen - reqbufpos, 0);
+				if (len == -1)
+					goto conndied;
+				reqbufpos += len;
+			}
+			free(reqbuf);
+			reqbuf = NULL;
+			nreq++;
+		}
+
+		/* Scan through the response processing headers. */
+		statuscode = 0;
+		contentlength = -1;
+		chunked = 0;
+		keepalive = 0;
+		do {
+			/* Get a header line */
+			error = readln(sd, resbuf, &resbuflen, &resbufpos);
+			if (error)
+				goto conndied;
+			hln = resbuf + resbufpos;
+			eolp = strnstr(hln, "\r\n", resbuflen - resbufpos);
+			resbufpos = (eolp - resbuf) + 2;
+			*eolp = '\0';
+
+			/* Make sure it doesn't contain a NUL character */
+			if (strchr(hln, '\0') != eolp)
+				goto conndied;
+
+			if (statuscode == 0) {
+				/* The first line MUST be HTTP/1.x xxx ... */
+				if ((strncmp(hln, "HTTP/1.", 7) != 0) ||
+				    ! isdigit(hln[7]))
+					goto conndied;
+
+				/*
+				 * If the minor version number isn't zero,
+				 * then we can assume that pipelining our
+				 * requests is OK -- as long as we don't
+				 * see a "Connection: close" line later
+				 * and we either have a Content-Length or
+				 * Transfer-Encoding: chunked header to
+				 * tell us the length.
+				 */
+				if (hln[7] != '0')
+					pipelined = 1;
+
+				/* Skip over the minor version number */
+				hln = strchr(hln + 7, ' ');
+				if (hln == NULL)
+					goto conndied;
+				else
+					hln++;
+
+				/* Read the status code */
+				while (isdigit(*hln)) {
+					statuscode = statuscode * 10 +
+					    *hln - '0';
+					hln++;
+				}
+
+				if (statuscode < 100 || statuscode > 599)
+					goto conndied;
+
+				/* Ignore the rest of the line */
+				continue;
+			}
+
+			/*
+			 * Check for "Connection: close" or
+			 * "Connection: Keep-Alive" header
+			 */
+			if (strncasecmp(hln, "Connection:", 11) == 0) {
+				hln += 11;
+				if (strcasestr(hln, "close") != NULL)
+					pipelined = 0;
+				if (strcasestr(hln, "Keep-Alive") != NULL)
+					keepalive = 1;
+
+				/* Next header... */
+				continue;
+			}
+
+			/* Check for "Content-Length:" header */
+			if (strncasecmp(hln, "Content-Length:", 15) == 0) {
+				hln += 15;
+				contentlength = 0;
+
+				/* Find the start of the length */
+				while (!isdigit(*hln) && (*hln != '\0'))
+					hln++;
+
+				/* Compute the length */
+				while (isdigit(*hln)) {
+					if (contentlength >= OFF_MAX / 10) {
+						/* Nasty people... */
+						goto conndied;
+					}
+					contentlength = contentlength * 10 +
+					    *hln - '0';
+					hln++;
+				}
+
+				/* Next header... */
+				continue;
+			}
+
+			/* Check for "Transfer-Encoding: chunked" header */
+			if (strncasecmp(hln, "Transfer-Encoding:", 18) == 0) {
+				hln += 18;
+				if (strcasestr(hln, "chunked") != NULL)
+					chunked = 1;
+
+				/* Next header... */
+				continue;
+			}
+
+			/* We blithely ignore any other header lines */
+
+			/* No more header lines */
+			if (strlen(hln) == 0) {
+				/*
+				 * If the status code was 1xx, then there will
+				 * be a real header later.  Servers may emit
+				 * 1xx header blocks at will, but since we
+				 * don't expect one, we should just ignore it.
+				 */
+				if (100 <= statuscode && statuscode <= 199) {
+					statuscode = 0;
+					continue;
+				}
+
+				/* End of header; message body follows */
+				break;
+			}
+		} while (1);
+
+		/* No message body for 204 or 304 */
+		if (statuscode == 204 || statuscode == 304) {
+			nres++;
+			continue;
+		}
+
+		/*
+		 * There should be a message body coming, but we only want
+		 * to send it to a file if the status code is 200
+		 */
+		if (statuscode == 200) {
+			/* Generate a file name for the download */
+			fname = strrchr(argv[nres], '/');
+			if (fname == NULL)
+				fname = argv[nres];
+			else
+				fname++;
+			if (strlen(fname) == 0)
+				errx(1, "Cannot obtain file name from %s\n",
+				    argv[nres]);
+
+			fd = open(fname, O_CREAT | O_TRUNC | O_WRONLY, 0644);
+			if (fd == -1)
+				errx(1, "open(%s)", fname);
+		}
+
+		/* Read the message and send data to fd if appropriate */
+		if (chunked) {
+			/* Handle a chunked-encoded entity */
+
+			/* Read chunks */
+			do {
+				error = readln(sd, resbuf, &resbuflen,
+				    &resbufpos);
+				if (error)
+					goto conndied;
+				hln = resbuf + resbufpos;
+				eolp = strstr(hln, "\r\n");
+				resbufpos = (eolp - resbuf) + 2;
+
+				clen = 0;
+				while (isxdigit(*hln)) {
+					if (clen >= OFF_MAX / 16) {
+						/* Nasty people... */
+						goto conndied;
+					}
+					if (isdigit(*hln))
+						clen = clen * 16 + *hln - '0';
+					else
+						clen = clen * 16 + 10 +
+						    tolower(*hln) - 'a';
+					hln++;
+				}
+
+				error = copybytes(sd, fd, clen, resbuf,
+				    &resbuflen, &resbufpos);
+				if (error) {
+					goto conndied;
+				}
+			} while (clen != 0);
+
+			/* Read trailer and final CRLF */
+			do {
+				error = readln(sd, resbuf, &resbuflen,
+				    &resbufpos);
+				if (error)
+					goto conndied;
+				hln = resbuf + resbufpos;
+				eolp = strstr(hln, "\r\n");
+				resbufpos = (eolp - resbuf) + 2;
+			} while (hln != eolp);
+		} else if (contentlength != -1) {
+			error = copybytes(sd, fd, contentlength, resbuf,
+			    &resbuflen, &resbufpos);
+			if (error)
+				goto conndied;
+		} else {
+			/*
+			 * Not chunked, and no content length header.
+			 * Read everything until the server closes the
+			 * socket.
+			 */
+			error = copybytes(sd, fd, OFF_MAX, resbuf,
+			    &resbuflen, &resbufpos);
+			if (error == -1)
+				goto conndied;
+			pipelined = 0;
+		}
+
+		if (fd != -1) {
+			close(fd);
+			fd = -1;
+		}
+
+		fprintf(stderr, "http://%s/%s: %d ", servername, argv[nres],
+		    statuscode);
+		if (statuscode == 200)
+			fprintf(stderr, "OK\n");
+		else if (statuscode < 300)
+			fprintf(stderr, "Successful (ignored)\n");
+		else if (statuscode < 400)
+			fprintf(stderr, "Redirection (ignored)\n");
+		else
+			fprintf(stderr, "Error (ignored)\n");
+
+		/* We've finished this file! */
+		nres++;
+
+		/*
+		 * If necessary, clean up this connection so that we
+		 * can start a new one.
+		 */
+		if (pipelined == 0 && keepalive == 0)
+			goto cleanupconn;
+		continue;
+
+conndied:
+		/*
+		 * Something went wrong -- our connection died, the server
+		 * sent us garbage, etc.  If this happened on the first
+		 * request we sent over this connection, give up.  Otherwise,
+		 * close this connection, open a new one, and reissue the
+		 * request.
+		 */
+		if (nres == firstreq)
+			errx(1, "Connection failure");
+
+cleanupconn:
+		/*
+		 * Clean up our connection and keep on going
+		 */
+		shutdown(sd, SHUT_RDWR);
+		close(sd);
+		sd = -1;
+		if (fd != -1) {
+			close(fd);
+			fd = -1;
+		}
+		if (reqbuf != NULL) {
+			free(reqbuf);
+			reqbuf = NULL;
+		}
+		nreq = nres;
+		res = res0;
+		pipelined = 0;
+		resbufpos = resbuflen = 0;
+		continue;
+	}
+
+	free(resbuf);
+	freeaddrinfo(res0);
+
+	return 0;
+}


Property changes on: trunk/usr.sbin/portsnap/phttpget/phttpget.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/portsnap/portsnap/Makefile
===================================================================
--- trunk/usr.sbin/portsnap/portsnap/Makefile	                        (rev 0)
+++ trunk/usr.sbin/portsnap/portsnap/Makefile	2019-02-09 18:20:51 UTC (rev 12167)
@@ -0,0 +1,6 @@
+# $MidnightBSD$
+
+SCRIPTS=portsnap.sh
+MAN=	portsnap.8
+
+.include <bsd.prog.mk>


Property changes on: trunk/usr.sbin/portsnap/portsnap/Makefile
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/portsnap/portsnap/portsnap.8
===================================================================
--- trunk/usr.sbin/portsnap/portsnap/portsnap.8	                        (rev 0)
+++ trunk/usr.sbin/portsnap/portsnap/portsnap.8	2019-02-09 18:20:51 UTC (rev 12167)
@@ -0,0 +1,268 @@
+.\" $MidnightBSD$
+.\"-
+.\" Copyright 2004-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.
+.\"
+.\" $FreeBSD: stable/10/usr.sbin/portsnap/portsnap/portsnap.8 296638 2016-03-11 01:39:28Z jgh $
+.\"
+.Dd March 1, 2016
+.Dt PORTSNAP 8
+.Os FreeBSD
+.Sh NAME
+.Nm portsnap
+.Nd fetch and extract compressed snapshots of the ports tree
+.Sh SYNOPSIS
+.Nm
+.Op Fl I
+.Op Fl d Ar workdir
+.Op Fl f Ar conffile
+.Op Fl k Ar KEY
+.Op Fl l Ar descfile
+.Op Fl p Ar portsdir
+.Op Fl s Ar server
+.Cm command ...
+.Op Ar path
+.Sh DESCRIPTION
+The
+.Nm
+tool is used to fetch and update compressed snapshots
+of the
+.Fx
+ports tree, and extract and update an
+uncompressed ports tree.
+.Pp
+In a normal update operation,
+.Nm
+will routinely restore modified files to their unmodified state and
+delete unrecognized local files.
+.Sh OPTIONS
+The following options are supported:
+.Bl -tag -width "-f conffile"
+.It Fl d Ar workdir
+Store working files (e.g.\& downloaded updates) in
+.Ar workdir .
+(default:
+.Pa /var/db/portsnap ,
+or as given in the configuration file.)
+.It Fl f Ar conffile
+Read the configuration from
+.Ar conffile .
+(default:
+.Pa /etc/portsnap.conf )
+.It Fl I
+For the
+.Cm update
+command, update INDEX files, but not the rest of the ports tree.
+.It Fl k Ar KEY
+Expect a public key with given SHA256 hash.
+(default: read value from configuration file.)
+.It Fl l Ar descfile
+Merge the specified local describes file into the INDEX files being
+built.
+The
+.Ar descfile
+should be generated by running
+.Cm make describe
+in each of the local port directories.
+.It Fl p Ar portsdir
+When extracting or updating an uncompressed snapshot,
+operate on the directory
+.Ar portsdir .
+(default:
+.Pa /usr/ports/ ,
+or as given in the configuration file.)
+.It Fl s Ar server
+Fetch files from the specified server or server pool.
+(default: portsnap.FreeBSD.org, or as given in the
+configuration file.)
+.It path
+For
+.Cm extract
+command only, operate only on parts of the ports tree starting with
+.Ar path .
+(e.g.\&
+.Nm
+.Cm extract
+.Ar sysutils/port
+would extract sysutils/portsman, sysutils/portsnap,
+sysutils/portupgrade, etc.)
+.It Fl Fl interactive
+override auto-detection of calling process.
+Only use this when calling portsnap from an
+.Sy interactive, non-terminal application.
+(Cron jobs are particularly bad since they cause
+load spikes on the Portsnap mirrors.)
+.El
+.Sh COMMANDS
+The
+.Cm command
+can be any one of the following:
+.Bl -tag -width "-f conffile"
+.It fetch
+Fetch a compressed snapshot of the ports tree, or update
+the existing snapshot.
+This command should only be used interactively; for
+non-interactive use, you should use the
+.Cm cron
+command.
+.It cron
+Sleep a random amount of time between 1 and 3600 seconds,
+then operate as if the
+.Cm fetch
+command was specified.
+As the name suggests, this command is designed for running
+from
+.Xr cron 8 ;
+the random delay serves to minimize the probability that
+a large number of machines will simultaneously attempt to
+fetch updates.
+.It extract
+Extract a ports tree, replacing existing files and directories.
+NOTE: This will remove anything occupying the location where
+files or directories are being extracted; in particular, any
+changes made locally to the ports tree (for example, adding new
+patches) will be silently obliterated.
+.Pp
+Only run this command to initialize your portsnap-maintained
+ports tree for the first time, if you wish to start over with
+a clean, completely unmodified tree, or if you wish to extract
+a specific part of the tree (using the
+.Ar path
+option).
+.It update
+Update a ports tree extracted using the
+.Cm extract
+command.
+You must run this command to apply changes to your ports tree
+after downloading updates via the
+.Cm fetch
+or
+.Cm cron
+commands.
+Again, note that in the parts of the ports tree which are being
+updated, any local changes or additions will be removed.
+.El
+.Sh TIPS
+.Bl -bullet
+.It
+If your clock is set to local time, adding the line
+.Pp
+.Dl 0 3 * * * root /usr/sbin/portsnap cron
+.Pp
+to
+.Pa /etc/crontab
+is a good way to make sure you always have
+an up-to-date snapshot of the ports tree available which
+can quickly be extracted into
+.Pa /usr/ports .
+If your clock is set to UTC, please pick a random time other
+than 3AM, to avoid overly imposing an uneven load on the
+server(s) hosting the snapshots.
+.Pp
+Note that running
+.Nm
+.Cm cron
+or
+.Nm
+.Cm fetch
+does not apply the changes that were received: they only download
+them.
+To apply the changes, you must follow these commands with
+.Nm
+.Cm update .
+The
+.Nm
+.Cm update
+command is normally run by hand at a time when you are sure that
+no one is manually working in the ports tree.
+.It
+Running
+.Nm
+.Cm update
+from
+.Xr cron 8
+is a bad idea -- if you are ever installing or updating a
+port at the time the cron job runs, you will probably end up
+in a mess when
+.Nm
+updates or removes files which are being used by the port
+build.
+However, running
+.Nm
+.Fl I
+.Cm update
+is probably safe, and can be used together with
+.Xr pkg-version 8
+to identify installed software which is out of date.
+.It
+If you wish to use
+.Nm
+to keep a large number of machines up to date, you may wish
+to set up a caching HTTP proxy.
+Since
+.Nm
+uses
+.Xr fetch 1
+to download updates, setting the
+.Ev HTTP_PROXY
+environment variable will direct it to fetch updates from
+the given proxy.
+This is much more efficient than
+.Em mirroring
+the files on the portsnap server, since the vast majority
+of files are not needed by any particular client.
+.El
+.Sh PRIVACY NOTICE
+As an unavoidable part of its operation, a machine running
+.Nm
+will make its public IP address and the list of files it fetches
+available to the server from which it fetches updates.
+Using these it may be possible to recognize a machine over an extended
+period of time, determine when it is updated, and identify which
+portions of the FreeBSD ports tree, if any, are being ignored using
+"REFUSE" directives in
+.Pa portsnap.conf .
+In addition, the FreeBSD release level is transmitted to the server.
+.Pp
+Statistical data generated from information collected in this manner
+may be published, but only in aggregate and after anonymizing the
+individual systems.
+.Sh FILES
+.Bl -tag -width "/etc/portsnap.conf"
+.It Pa /etc/portsnap.conf
+Default location of the portsnap configuration file.
+.It Pa /var/db/portsnap
+Default location where compressed snapshots are stored.
+.It Pa /usr/ports
+Default location where the ports tree is extracted.
+.El
+.Sh SEE ALSO
+.Xr fetch 1 ,
+.Xr sha256 1 ,
+.Xr fetch 3 ,
+.Xr portsnap.conf 5 ,
+.Xr pkg 7 ,
+.Xr pkg-version 8
+.Sh AUTHORS
+.An Colin Percival Aq cperciva at FreeBSD.org


Property changes on: trunk/usr.sbin/portsnap/portsnap/portsnap.8
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/portsnap/portsnap/portsnap.sh
===================================================================
--- trunk/usr.sbin/portsnap/portsnap/portsnap.sh	                        (rev 0)
+++ trunk/usr.sbin/portsnap/portsnap/portsnap.sh	2019-02-09 18:20:51 UTC (rev 12167)
@@ -0,0 +1,1142 @@
+#!/bin/sh
+
+#-
+# Copyright 2004-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.
+
+# $FreeBSD: stable/10/usr.sbin/portsnap/portsnap/portsnap.sh 306697 2016-10-05 00:33:06Z emaste $
+# $MidnightBSD$
+
+#### Usage function -- called from command-line handling code.
+
+# Usage instructions.  Options not listed:
+# --debug	-- don't filter output from utilities
+# --no-stats	-- don't show progress statistics while fetching files
+usage() {
+	cat <<EOF
+usage: `basename $0` [options] command ... [path]
+
+Options:
+  -d workdir   -- Store working files in workdir
+                  (default: /var/db/portsnap/)
+  -f conffile  -- Read configuration options from conffile
+                  (default: /etc/portsnap.conf)
+  -I           -- Update INDEX only. (update command only)
+  -k KEY       -- Trust an RSA key with SHA256 hash of KEY
+  -l descfile  -- Merge the specified local describes file into the INDEX.
+  -p portsdir  -- Location of uncompressed ports tree
+                  (default: /usr/ports/)
+  -s server    -- Server from which to fetch updates.
+                  (default: portsnap.FreeBSD.org)
+  --interactive -- interactive: override auto-detection of calling process
+                  (use this when calling portsnap from an interactive, non-
+                  terminal application AND NEVER ELSE).
+  path         -- Extract only parts of the tree starting with the given
+                  string.  (extract command only)
+Commands:
+  fetch        -- Fetch a compressed snapshot of the ports tree,
+                  or update an existing snapshot.
+  cron         -- Sleep rand(3600) seconds, and then fetch updates.
+  extract      -- Extract snapshot of ports tree, replacing existing
+                  files and directories.
+  update       -- Update ports tree to match current snapshot, replacing
+                  files and directories which have changed.
+EOF
+	exit 0
+}
+
+#### Parameter handling functions.
+
+# Initialize parameters to null, just in case they're
+# set in the environment.
+init_params() {
+	KEYPRINT=""
+	EXTRACTPATH=""
+	WORKDIR=""
+	PORTSDIR=""
+	CONFFILE=""
+	COMMAND=""
+	COMMANDS=""
+	QUIETREDIR=""
+	QUIETFLAG=""
+	STATSREDIR=""
+	XARGST=""
+	NDEBUG=""
+	DDSTATS=""
+	INDEXONLY=""
+	SERVERNAME=""
+	REFUSE=""
+	LOCALDESC=""
+	INTERACTIVE=""
+}
+
+# Parse the command line
+parse_cmdline() {
+	while [ $# -gt 0 ]; do
+		case "$1" in
+		-d)
+			if [ $# -eq 1 ]; then usage; fi
+			if [ ! -z "${WORKDIR}" ]; then usage; fi
+			shift; WORKDIR="$1"
+			;;
+		--debug)
+			QUIETREDIR="/dev/stderr"
+			STATSREDIR="/dev/stderr"
+			QUIETFLAG=" "
+			NDEBUG=" "
+			XARGST="-t"
+			DDSTATS=".."
+			;;
+		--interactive)
+			INTERACTIVE="YES"
+			;;
+		-f)
+			if [ $# -eq 1 ]; then usage; fi
+			if [ ! -z "${CONFFILE}" ]; then usage; fi
+			shift; CONFFILE="$1"
+			;;
+		-h | --help | help)
+			usage
+			;;
+		-I)
+			INDEXONLY="YES"
+			;;
+		-k)
+			if [ $# -eq 1 ]; then usage; fi
+			if [ ! -z "${KEYPRINT}" ]; then usage; fi
+			shift; KEYPRINT="$1"
+			;;
+		-l)
+			if [ $# -eq 1 ]; then usage; fi
+			if [ ! -z "${LOCALDESC}" ]; then usage; fi
+			shift; LOCALDESC="$1"
+			;;
+		--no-stats)
+			if [ -z "${STATSREDIR}" ]; then
+				STATSREDIR="/dev/null"
+				DDSTATS=".. "
+			fi
+			;;
+		-p)
+			if [ $# -eq 1 ]; then usage; fi
+			if [ ! -z "${PORTSDIR}" ]; then usage; fi
+			shift; PORTSDIR="$1"
+			;;
+		-s)
+			if [ $# -eq 1 ]; then usage; fi
+			if [ ! -z "${SERVERNAME}" ]; then usage; fi
+			shift; SERVERNAME="$1"
+			;;
+		cron | extract | fetch | update | alfred)
+			COMMANDS="${COMMANDS} $1"
+			;;
+		up)
+			COMMANDS="${COMMANDS} update"
+			;;
+		*)
+			if [ $# -gt 1 ]; then usage; fi
+			if echo ${COMMANDS} | grep -vq extract; then
+				usage
+			fi
+			EXTRACTPATH="$1"
+			;;
+		esac
+		shift
+	done
+
+	if [ -z "${COMMANDS}" ]; then
+		usage
+	fi
+}
+
+# If CONFFILE was specified at the command-line, make
+# sure that it exists and is readable.
+sanity_conffile() {
+	if [ ! -z "${CONFFILE}" ] && [ ! -r "${CONFFILE}" ]; then
+		echo -n "File does not exist "
+		echo -n "or is not readable: "
+		echo ${CONFFILE}
+		exit 1
+	fi
+}
+
+# If a configuration file hasn't been specified, use
+# the default value (/etc/portsnap.conf)
+default_conffile() {
+	if [ -z "${CONFFILE}" ]; then
+		CONFFILE="/etc/portsnap.conf"
+	fi
+}
+
+# Read {KEYPRINT, SERVERNAME, WORKDIR, PORTSDIR} from the configuration
+# file if they haven't already been set.  If the configuration
+# file doesn't exist, do nothing.
+# Also read REFUSE (which cannot be set via the command line) if it is
+# present in the configuration file.
+parse_conffile() {
+	if [ -r "${CONFFILE}" ]; then
+		for X in KEYPRINT WORKDIR PORTSDIR SERVERNAME; do
+			eval _=\$${X}
+			if [ -z "${_}" ]; then
+				eval ${X}=`grep "^${X}=" "${CONFFILE}" |
+				    cut -f 2- -d '=' | tail -1`
+			fi
+		done
+
+		if grep -qE "^REFUSE[[:space:]]" ${CONFFILE}; then
+			REFUSE="^(`
+				grep -E "^REFUSE[[:space:]]" "${CONFFILE}" |
+				    cut -c 7- | xargs echo | tr ' ' '|'
+				`)"
+		fi
+
+		if grep -qE "^INDEX[[:space:]]" ${CONFFILE}; then
+			INDEXPAIRS="`
+				grep -E "^INDEX[[:space:]]" "${CONFFILE}" |
+				    cut -c 7- | tr ' ' '|' | xargs echo`"
+		fi
+	fi
+}
+
+# If parameters have not been set, use default values
+default_params() {
+	_QUIETREDIR="/dev/null"
+	_QUIETFLAG="-q"
+	_STATSREDIR="/dev/stdout"
+	_WORKDIR="/var/db/portsnap"
+	_PORTSDIR="/usr/ports"
+	_NDEBUG="-n"
+	_LOCALDESC="/dev/null"
+	for X in QUIETREDIR QUIETFLAG STATSREDIR WORKDIR PORTSDIR	\
+	    NDEBUG LOCALDESC; do
+		eval _=\$${X}
+		eval __=\$_${X}
+		if [ -z "${_}" ]; then
+			eval ${X}=${__}
+		fi
+	done
+	if [ -z "${INTERACTIVE}" ]; then
+		if [ -t 0 ]; then
+			INTERACTIVE="YES"
+		else
+			INTERACTIVE="NO"
+		fi
+	fi
+}
+
+# Perform sanity checks and set some final parameters
+# in preparation for fetching files.  Also chdir into
+# the working directory.
+fetch_check_params() {
+	export HTTP_USER_AGENT="portsnap (${COMMAND}, `uname -r`)"
+
+	_SERVERNAME_z=\
+"SERVERNAME must be given via command line or configuration file."
+	_KEYPRINT_z="Key must be given via -k option or configuration file."
+	_KEYPRINT_bad="Invalid key fingerprint: "
+	_WORKDIR_bad="Directory does not exist or is not writable: "
+
+	if [ -z "${SERVERNAME}" ]; then
+		echo -n "`basename $0`: "
+		echo "${_SERVERNAME_z}"
+		exit 1
+	fi
+	if [ -z "${KEYPRINT}" ]; then
+		echo -n "`basename $0`: "
+		echo "${_KEYPRINT_z}"
+		exit 1
+	fi
+	if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then
+		echo -n "`basename $0`: "
+		echo -n "${_KEYPRINT_bad}"
+		echo ${KEYPRINT}
+		exit 1
+	fi
+	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
+		echo -n "`basename $0`: "
+		echo -n "${_WORKDIR_bad}"
+		echo ${WORKDIR}
+		exit 1
+	fi
+	cd ${WORKDIR} || exit 1
+
+	BSPATCH=/usr/bin/bspatch
+	SHA256=/sbin/sha256
+	PHTTPGET=/usr/libexec/phttpget
+}
+
+# Perform sanity checks and set some final parameters
+# in preparation for extracting or updating ${PORTSDIR}
+# Complain if ${PORTSDIR} exists but is not writable,
+# but don't complain if ${PORTSDIR} doesn't exist.
+extract_check_params() {
+	_WORKDIR_bad="Directory does not exist: "
+	_PORTSDIR_bad="Directory is not writable: "
+
+	if ! [ -d "${WORKDIR}" ]; then
+		echo -n "`basename $0`: "
+		echo -n "${_WORKDIR_bad}"
+		echo ${WORKDIR}
+		exit 1
+	fi
+	if [ -d "${PORTSDIR}" ] && ! [ -w "${PORTSDIR}" ]; then
+		echo -n "`basename $0`: "
+		echo -n "${_PORTSDIR_bad}"
+		echo ${PORTSDIR}
+		exit 1
+	fi
+
+	if ! [ -d "${WORKDIR}/files" -a -r "${WORKDIR}/tag"	\
+	    -a -r "${WORKDIR}/INDEX" -a -r "${WORKDIR}/tINDEX" ]; then
+		echo "No snapshot available.  Try running"
+		echo "# `basename $0` fetch"
+		exit 1
+	fi
+
+	MKINDEX=/usr/libexec/make_index
+}
+
+# Perform sanity checks and set some final parameters
+# in preparation for updating ${PORTSDIR}
+update_check_params() {
+	extract_check_params
+
+	if ! [ -r ${PORTSDIR}/.portsnap.INDEX ]; then
+		echo "${PORTSDIR} was not created by portsnap."
+		echo -n "You must run '`basename $0` extract' before "
+		echo "running '`basename $0` update'."
+		exit 1
+	fi
+
+}
+
+#### Core functionality -- the actual work gets done here
+
+# Use an SRV query to pick a server.  If the SRV query doesn't provide
+# a useful answer, use the server name specified by the user.
+# Put another way... look up _http._tcp.${SERVERNAME} and pick a server
+# from that; or if no servers are returned, use ${SERVERNAME}.
+# This allows a user to specify "portsnap.freebsd.org" (in which case
+# portsnap will select one of the mirrors) or "portsnap5.tld.freebsd.org"
+# (in which case portsnap will use that particular server, since there
+# won't be an SRV entry for that name).
+#
+# We ignore the Port field, since we are always going to use port 80.
+
+# Fetch the mirror list, but do not pick a mirror yet.  Returns 1 if
+# no mirrors are available for any reason.
+fetch_pick_server_init() {
+	: > serverlist_tried
+
+# Check that host(1) exists (i.e., that the system wasn't built with the
+# WITHOUT_BIND set) and don't try to find a mirror if it doesn't exist.
+	if ! which -s host; then
+		: > serverlist_full
+		return 1
+	fi
+
+	echo -n "Looking up ${SERVERNAME} mirrors... "
+
+# Issue the SRV query and pull out the Priority, Weight, and Target fields.
+# BIND 9 prints "$name has SRV record ..." while BIND 8 prints
+# "$name server selection ..."; we allow either format.
+	MLIST="_http._tcp.${SERVERNAME}"
+	host -t srv "${MLIST}" |
+	    sed -nE "s/${MLIST} (has SRV record|server selection) //Ip" |
+	    cut -f 1,2,4 -d ' ' |
+	    sed -e 's/\.$//' |
+	    sort > serverlist_full
+
+# If no records, give up -- we'll just use the server name we were given.
+	if [ `wc -l < serverlist_full` -eq 0 ]; then
+		echo "none found."
+		return 1
+	fi
+
+# Report how many mirrors we found.
+	echo `wc -l < serverlist_full` "mirrors found."
+
+# Generate a random seed for use in picking mirrors.  If HTTP_PROXY
+# is set, this will be used to generate the seed; otherwise, the seed
+# will be random.
+	if [ -n "${HTTP_PROXY}${http_proxy}" ]; then
+		RANDVALUE=`sha256 -qs "${HTTP_PROXY}${http_proxy}" |
+		    tr -d 'a-f' |
+		    cut -c 1-9`
+	else
+		RANDVALUE=`jot -r 1 0 999999999`
+	fi
+}
+
+# Pick a mirror.  Returns 1 if we have run out of mirrors to try.
+fetch_pick_server() {
+# Generate a list of not-yet-tried mirrors
+	sort serverlist_tried |
+	    comm -23 serverlist_full - > serverlist
+
+# Have we run out of mirrors?
+	if [ `wc -l < serverlist` -eq 0 ]; then
+		echo "No mirrors remaining, giving up."
+		return 1
+	fi
+
+# Find the highest priority level (lowest numeric value).
+	SRV_PRIORITY=`cut -f 1 -d ' ' serverlist | sort -n | head -1`
+
+# Add up the weights of the response lines at that priority level.
+	SRV_WSUM=0;
+	while read X; do
+		case "$X" in
+		${SRV_PRIORITY}\ *)
+			SRV_W=`echo $X | cut -f 2 -d ' '`
+			SRV_WSUM=$(($SRV_WSUM + $SRV_W))
+			;;
+		esac
+	done < serverlist
+
+# If all the weights are 0, pretend that they are all 1 instead.
+	if [ ${SRV_WSUM} -eq 0 ]; then
+		SRV_WSUM=`grep -E "^${SRV_PRIORITY} " serverlist | wc -l`
+		SRV_W_ADD=1
+	else
+		SRV_W_ADD=0
+	fi
+
+# Pick a value between 0 and the sum of the weights - 1
+	SRV_RND=`expr ${RANDVALUE} % ${SRV_WSUM}`
+
+# Read through the list of mirrors and set SERVERNAME.  Write the line
+# corresponding to the mirror we selected into serverlist_tried so that
+# we won't try it again.
+	while read X; do
+		case "$X" in
+		${SRV_PRIORITY}\ *)
+			SRV_W=`echo $X | cut -f 2 -d ' '`
+			SRV_W=$(($SRV_W + $SRV_W_ADD))
+			if [ $SRV_RND -lt $SRV_W ]; then
+				SERVERNAME=`echo $X | cut -f 3 -d ' '`
+				echo "$X" >> serverlist_tried
+				break
+			else
+				SRV_RND=$(($SRV_RND - $SRV_W))
+			fi
+			;;
+		esac
+	done < serverlist
+}
+
+# Check that we have a public key with an appropriate hash, or
+# fetch the key if it doesn't exist.  Returns 1 if the key has
+# not yet been fetched.
+fetch_key() {
+	if [ -r pub.ssl ] && [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
+		return 0
+	fi
+
+	echo -n "Fetching public key from ${SERVERNAME}... "
+	rm -f pub.ssl
+	fetch ${QUIETFLAG} http://${SERVERNAME}/pub.ssl \
+	    2>${QUIETREDIR} || true
+	if ! [ -r pub.ssl ]; then
+		echo "failed."
+		return 1
+	fi
+	if ! [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
+		echo "key has incorrect hash."
+		rm -f pub.ssl
+		return 1
+	fi
+	echo "done."
+}
+
+# Fetch a snapshot tag
+fetch_tag() {
+	rm -f snapshot.ssl tag.new
+
+	echo ${NDEBUG} "Fetching snapshot tag from ${SERVERNAME}... "
+	fetch ${QUIETFLAG} http://${SERVERNAME}/$1.ssl		\
+	    2>${QUIETREDIR} || true
+	if ! [ -r $1.ssl ]; then
+		echo "failed."
+		return 1
+	fi
+
+	openssl rsautl -pubin -inkey pub.ssl -verify		\
+	    < $1.ssl > tag.new 2>${QUIETREDIR} || true
+	rm $1.ssl
+
+	if ! [ `wc -l < tag.new` = 1 ] ||
+	    ! grep -qE "^portsnap\|[0-9]{10}\|[0-9a-f]{64}" tag.new; then
+		echo "invalid snapshot tag."
+		return 1
+	fi
+
+	echo "done."
+
+	SNAPSHOTDATE=`cut -f 2 -d '|' < tag.new`
+	SNAPSHOTHASH=`cut -f 3 -d '|' < tag.new`
+}
+
+# Sanity-check the date on a snapshot tag
+fetch_snapshot_tagsanity() {
+	if [ `date "+%s"` -gt `expr ${SNAPSHOTDATE} + 31536000` ]; then
+		echo "Snapshot appears to be more than a year old!"
+		echo "(Is the system clock correct?)"
+		echo "Cowardly refusing to proceed any further."
+		return 1
+	fi
+	if [ `date "+%s"` -lt `expr ${SNAPSHOTDATE} - 86400` ]; then
+		echo -n "Snapshot appears to have been created more than "
+		echo "one day into the future!"
+		echo "(Is the system clock correct?)"
+		echo "Cowardly refusing to proceed any further."
+		return 1
+	fi
+}
+
+# Sanity-check the date on a snapshot update tag
+fetch_update_tagsanity() {
+	fetch_snapshot_tagsanity || return 1
+
+	if [ ${OLDSNAPSHOTDATE} -gt ${SNAPSHOTDATE} ]; then
+		echo -n "Latest snapshot on server is "
+		echo "older than what we already have!"
+		echo -n "Cowardly refusing to downgrade from "
+		date -r ${OLDSNAPSHOTDATE}
+		echo "to `date -r ${SNAPSHOTDATE}`."
+		return 1
+	fi
+}
+
+# Compare old and new tags; return 1 if update is unnecessary
+fetch_update_neededp() {
+	if [ ${OLDSNAPSHOTDATE} -eq ${SNAPSHOTDATE} ]; then
+		echo -n "Latest snapshot on server matches "
+		echo "what we already have."
+		echo "No updates needed."
+		rm tag.new
+		return 1
+	fi
+	if [ ${OLDSNAPSHOTHASH} = ${SNAPSHOTHASH} ]; then
+		echo -n "Ports tree hasn't changed since "
+		echo "last snapshot."
+		echo "No updates needed."
+		rm tag.new
+		return 1
+	fi
+
+	return 0
+}
+
+# Fetch snapshot metadata file
+fetch_metadata() {
+	rm -f ${SNAPSHOTHASH} tINDEX.new
+
+	echo ${NDEBUG} "Fetching snapshot metadata... "
+	fetch ${QUIETFLAG} http://${SERVERNAME}/t/${SNAPSHOTHASH} \
+	    2>${QUIETREDIR} || return
+	if [ "`${SHA256} -q ${SNAPSHOTHASH}`" != ${SNAPSHOTHASH} ]; then
+		echo "snapshot metadata corrupt."
+		return 1
+	fi
+	mv ${SNAPSHOTHASH} tINDEX.new
+	echo "done."
+}
+
+# Warn user about bogus metadata
+fetch_metadata_freakout() {
+	echo
+	echo "Portsnap metadata is correctly signed, but contains"
+	echo "at least one line which appears bogus."
+	echo "Cowardly refusing to proceed any further."
+}
+
+# Sanity-check a snapshot metadata file
+fetch_metadata_sanity() {
+	if grep -qvE "^[0-9A-Z.]+\|[0-9a-f]{64}$" tINDEX.new; then
+		fetch_metadata_freakout
+		return 1
+	fi
+	if [ `look INDEX tINDEX.new | wc -l` != 1 ]; then
+		echo
+		echo "Portsnap metadata appears bogus."
+		echo "Cowardly refusing to proceed any further."
+		return 1
+	fi
+}
+
+# Take a list of ${oldhash}|${newhash} and output a list of needed patches
+fetch_make_patchlist() {
+	local IFS='|'
+	echo "" 1>${QUIETREDIR}
+	grep -vE "^([0-9a-f]{64})\|\1$" |
+		while read X Y; do
+			printf "Processing: $X $Y ...\r" 1>${QUIETREDIR}
+			if [ -f "files/${Y}.gz" -o ! -f "files/${X}.gz" ]; then continue; fi
+			echo "${X}|${Y}"
+		done
+	echo "" 1>${QUIETREDIR}
+}
+
+# Print user-friendly progress statistics
+fetch_progress() {
+	LNC=0
+	while read x; do
+		LNC=$(($LNC + 1))
+		if [ $(($LNC % 10)) = 0 ]; then
+			echo -n $LNC
+		elif [ $(($LNC % 2)) = 0 ]; then
+			echo -n .
+		fi
+	done
+	echo -n " "
+}
+
+pct_fmt()
+{
+	printf "                                     \r"
+	printf "($1/$2) %02.2f%% " `echo "scale=4;$LNC / $TOTAL * 100"|bc`
+}
+
+fetch_progress_percent() {
+	TOTAL=$1
+	LNC=0
+	pct_fmt $LNC $TOTAL
+	while read x; do
+		LNC=$(($LNC + 1))
+		if [ $(($LNC % 100)) = 0 ]; then
+                     pct_fmt $LNC $TOTAL
+		elif [ $(($LNC % 10)) = 0 ]; then
+			echo -n .
+		fi
+	done
+	pct_fmt $LNC $TOTAL
+	echo " done. "
+}
+
+# Sanity-check an index file
+fetch_index_sanity() {
+	if grep -qvE "^[-_+./@0-9A-Za-z]+\|[0-9a-f]{64}$" INDEX.new ||
+	    fgrep -q "./" INDEX.new; then
+		fetch_metadata_freakout
+		return 1
+	fi
+}
+
+# Verify a list of files
+fetch_snapshot_verify() {
+	while read F; do
+		if [ "`gunzip -c < snap/${F}.gz | ${SHA256} -q`" != ${F} ]; then
+			echo "snapshot corrupt."
+			return 1
+		fi
+	done
+	return 0
+}
+
+# Fetch a snapshot tarball, extract, and verify.
+fetch_snapshot() {
+	while ! fetch_tag snapshot; do
+		fetch_pick_server || return 1
+	done
+	fetch_snapshot_tagsanity || return 1
+	fetch_metadata || return 1
+	fetch_metadata_sanity || return 1
+
+	rm -rf snap/
+
+# Don't ask fetch(1) to be quiet -- downloading a snapshot of ~ 35MB will
+# probably take a while, so the progrees reports that fetch(1) generates
+# will be useful for keeping the users' attention from drifting.
+	echo "Fetching snapshot generated at `date -r ${SNAPSHOTDATE}`:"
+	fetch -r http://${SERVERNAME}/s/${SNAPSHOTHASH}.tgz || return 1
+
+	echo -n "Extracting snapshot... "
+	tar -xz --numeric-owner -f ${SNAPSHOTHASH}.tgz snap/ || return 1
+	rm ${SNAPSHOTHASH}.tgz
+	echo "done."
+
+	echo -n "Verifying snapshot integrity... "
+# Verify the metadata files
+	cut -f 2 -d '|' tINDEX.new | fetch_snapshot_verify || return 1
+# Extract the index
+	rm -f INDEX.new
+	gunzip -c < snap/`look INDEX tINDEX.new |
+	    cut -f 2 -d '|'`.gz > INDEX.new
+	fetch_index_sanity || return 1
+# Verify the snapshot contents
+	cut -f 2 -d '|' INDEX.new | fetch_snapshot_verify || return 1
+	cut -f 2 -d '|' tINDEX.new INDEX.new | sort -u > files.expected
+	find snap -mindepth 1 | sed -E 's^snap/(.*)\.gz^\1^' | sort > files.snap
+	if ! cmp -s files.expected files.snap; then
+		echo "unexpected files in snapshot."
+		return 1
+	fi
+	rm files.expected files.snap
+	echo "done."
+
+# Move files into their proper locations
+	rm -f tag INDEX tINDEX
+	rm -rf files
+	mv tag.new tag
+	mv tINDEX.new tINDEX
+	mv INDEX.new INDEX
+	mv snap/ files/
+
+	return 0
+}
+
+# Update a compressed snapshot
+fetch_update() {
+	rm -f patchlist diff OLD NEW filelist INDEX.new
+
+	OLDSNAPSHOTDATE=`cut -f 2 -d '|' < tag`
+	OLDSNAPSHOTHASH=`cut -f 3 -d '|' < tag`
+
+	while ! fetch_tag latest; do
+		fetch_pick_server || return 1
+	done
+	fetch_update_tagsanity || return 1
+	fetch_update_neededp || return 0
+	fetch_metadata || return 1
+	fetch_metadata_sanity || return 1
+
+	echo -n "Updating from `date -r ${OLDSNAPSHOTDATE}` "
+	echo "to `date -r ${SNAPSHOTDATE}`."
+
+# Generate a list of wanted metadata patches
+	join -t '|' -o 1.2,2.2 tINDEX tINDEX.new |
+	    fetch_make_patchlist > patchlist
+
+# Attempt to fetch metadata patches
+	echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
+	echo ${NDEBUG} "metadata patches.${DDSTATS}"
+	tr '|' '-' < patchlist |
+	    lam -s "tp/" - -s ".gz" |
+	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
+	    2>${STATSREDIR} | fetch_progress
+	echo "done."
+
+# Attempt to apply metadata patches
+	echo -n "Applying metadata patches... "
+	local oldifs="$IFS" IFS='|'
+	while read X Y; do
+		if [ ! -f "${X}-${Y}.gz" ]; then continue; fi
+		gunzip -c < ${X}-${Y}.gz > diff
+		gunzip -c < files/${X}.gz > OLD
+		cut -c 2- diff | join -t '|' -v 2 - OLD > ptmp
+		grep '^\+' diff | cut -c 2- |
+		    sort -k 1,1 -t '|' -m - ptmp > NEW
+		if [ `${SHA256} -q NEW` = ${Y} ]; then
+			mv NEW files/${Y}
+			gzip -n files/${Y}
+		fi
+		rm -f diff OLD NEW ${X}-${Y}.gz ptmp
+	done < patchlist 2>${QUIETREDIR}
+	IFS="$oldifs"
+	echo "done."
+
+# Update metadata without patches
+	join -t '|' -v 2 tINDEX tINDEX.new |
+	    cut -f 2 -d '|' /dev/stdin patchlist |
+		while read Y; do
+			if [ ! -f "files/${Y}.gz" ]; then
+				echo ${Y};
+			fi
+		done > filelist
+	echo -n "Fetching `wc -l < filelist | tr -d ' '` "
+	echo ${NDEBUG} "metadata files... "
+	lam -s "f/" - -s ".gz" < filelist |
+	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
+	    2>${QUIETREDIR}
+
+	while read Y; do
+		echo -n "Verifying ${Y}... " 1>${QUIETREDIR}
+		if [ `gunzip -c < ${Y}.gz | ${SHA256} -q` = ${Y} ]; then
+			mv ${Y}.gz files/${Y}.gz
+		else
+			echo "metadata is corrupt."
+			return 1
+		fi
+		echo "ok." 1>${QUIETREDIR}
+	done < filelist
+	echo "done."
+
+# Extract the index
+	echo -n "Extracting index... " 1>${QUIETREDIR}
+	gunzip -c < files/`look INDEX tINDEX.new |
+	    cut -f 2 -d '|'`.gz > INDEX.new
+	fetch_index_sanity || return 1
+
+# If we have decided to refuse certain updates, construct a hybrid index which
+# is equal to the old index for parts of the tree which we don't want to
+# update, and equal to the new index for parts of the tree which gets updates.
+# This means that we should always have a "complete snapshot" of the ports
+# tree -- with the caveat that it isn't actually a snapshot.
+	if [ ! -z "${REFUSE}" ]; then
+		echo "Refusing to download updates for ${REFUSE}"	\
+		    >${QUIETREDIR}
+
+		grep -Ev "${REFUSE}" INDEX.new > INDEX.tmp
+		grep -E "${REFUSE}" INDEX |
+		    sort -m -k 1,1 -t '|' - INDEX.tmp > INDEX.new
+		rm -f INDEX.tmp
+	fi
+
+# Generate a list of wanted ports patches
+	echo -n "Generating list of wanted patches..." 1>${QUIETREDIR}
+	join -t '|' -o 1.2,2.2 INDEX INDEX.new |
+	    fetch_make_patchlist > patchlist
+	echo " done." 1>${QUIETREDIR}
+
+# Attempt to fetch ports patches
+	patchcnt=`wc -l < patchlist | tr -d ' '`      
+	echo -n "Fetching $patchcnt "
+	echo ${NDEBUG} "patches.${DDSTATS}"
+	echo " "
+	tr '|' '-' < patchlist | lam -s "bp/" - |
+	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
+	    2>${STATSREDIR} | fetch_progress_percent $patchcnt
+	echo "done."
+
+# Attempt to apply ports patches
+	PATCHCNT=`wc -l patchlist`
+	echo "Applying patches... "
+	local oldifs="$IFS" IFS='|'
+	I=0
+	while read X Y; do
+		I=$(($I + 1))
+		F="${X}-${Y}"
+		if [ ! -f "${F}" ]; then
+			printf "  Skipping ${F} (${I} of ${PATCHCNT}).\r"
+			continue;
+		fi
+		echo "  Processing ${F}..." 1>${QUIETREDIR}
+		gunzip -c < files/${X}.gz > OLD
+		${BSPATCH} OLD NEW ${X}-${Y}
+		if [ `${SHA256} -q NEW` = ${Y} ]; then
+			mv NEW files/${Y}
+			gzip -n files/${Y}
+		fi
+		rm -f diff OLD NEW ${X}-${Y}
+	done < patchlist 2>${QUIETREDIR}
+	IFS="$oldifs"
+	echo "done."
+
+# Update ports without patches
+	join -t '|' -v 2 INDEX INDEX.new |
+	    cut -f 2 -d '|' /dev/stdin patchlist |
+		while read Y; do
+			if [ ! -f "files/${Y}.gz" ]; then
+				echo ${Y};
+			fi
+		done > filelist
+	echo -n "Fetching `wc -l < filelist | tr -d ' '` "
+	echo ${NDEBUG} "new ports or files... "
+	lam -s "f/" - -s ".gz" < filelist |
+	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
+	    2>${QUIETREDIR}
+
+	I=0
+	while read Y; do
+		I=$(($I + 1))
+		printf "   Processing ${Y} (${I} of ${PATCHCNT}).\r" 1>${QUIETREDIR}
+		if [ `gunzip -c < ${Y}.gz | ${SHA256} -q` = ${Y} ]; then
+			mv ${Y}.gz files/${Y}.gz
+		else
+			echo "snapshot is corrupt."
+			return 1
+		fi
+	done < filelist
+	echo "done."
+
+# Remove files which are no longer needed
+	cut -f 2 -d '|' tINDEX INDEX | sort -u > oldfiles
+	cut -f 2 -d '|' tINDEX.new INDEX.new | sort -u | comm -13 - oldfiles |
+	    lam -s "files/" - -s ".gz" | xargs rm -f
+	rm patchlist filelist oldfiles
+
+# We're done!
+	mv INDEX.new INDEX
+	mv tINDEX.new tINDEX
+	mv tag.new tag
+
+	return 0
+}
+
+# Do the actual work involved in "fetch" / "cron".
+fetch_run() {
+	fetch_pick_server_init && fetch_pick_server
+
+	while ! fetch_key; do
+		fetch_pick_server || return 1
+	done
+
+	if ! [ -d files -a -r tag -a -r INDEX -a -r tINDEX ]; then
+		fetch_snapshot || return 1
+	fi
+	fetch_update || return 1
+}
+
+# Build a ports INDEX file
+extract_make_index() {
+	if ! look $1 ${WORKDIR}/tINDEX > /dev/null; then
+		echo -n "$1 not provided by portsnap server; "
+		echo "$2 not being generated."
+	else
+	gunzip -c < "${WORKDIR}/files/`look $1 ${WORKDIR}/tINDEX |
+	    cut -f 2 -d '|'`.gz" |
+	    cat - ${LOCALDESC} |
+	    ${MKINDEX} /dev/stdin > ${PORTSDIR}/$2
+	fi
+}
+
+# Create INDEX, INDEX-5, INDEX-6
+extract_indices() {
+	echo -n "Building new INDEX files... "
+	for PAIR in ${INDEXPAIRS}; do
+		INDEXFILE=`echo ${PAIR} | cut -f 1 -d '|'`
+		DESCRIBEFILE=`echo ${PAIR} | cut -f 2 -d '|'`
+		extract_make_index ${DESCRIBEFILE} ${INDEXFILE} || return 1
+	done
+	echo "done."
+}
+
+# Create .portsnap.INDEX; if we are REFUSEing to touch certain directories,
+# merge the values from any exiting .portsnap.INDEX file.
+extract_metadata() {
+	if [ -z "${REFUSE}" ]; then
+		sort ${WORKDIR}/INDEX > ${PORTSDIR}/.portsnap.INDEX
+	elif [ -f ${PORTSDIR}/.portsnap.INDEX ]; then
+		grep -E "${REFUSE}" ${PORTSDIR}/.portsnap.INDEX	\
+		    > ${PORTSDIR}/.portsnap.INDEX.tmp
+		grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort |
+		    sort -m - ${PORTSDIR}/.portsnap.INDEX.tmp	\
+		    > ${PORTSDIR}/.portsnap.INDEX
+		rm -f ${PORTSDIR}/.portsnap.INDEX.tmp
+	else
+		grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort \
+		    > ${PORTSDIR}/.portsnap.INDEX
+	fi
+}
+
+# Do the actual work involved in "extract"
+extract_run() {
+	local oldifs="$IFS" IFS='|'
+	mkdir -p ${PORTSDIR} || return 1
+
+	if !
+		if ! [ -z "${EXTRACTPATH}" ]; then
+			grep "^${EXTRACTPATH}" ${WORKDIR}/INDEX
+		elif ! [ -z "${REFUSE}" ]; then
+			grep -vE "${REFUSE}" ${WORKDIR}/INDEX
+		else
+			cat ${WORKDIR}/INDEX
+		fi | while read FILE HASH; do
+		echo ${PORTSDIR}/${FILE}
+		if ! [ -r "${WORKDIR}/files/${HASH}.gz" ]; then
+			echo "files/${HASH}.gz not found -- snapshot corrupt."
+			return 1
+		fi
+		case ${FILE} in
+		*/)
+			rm -rf ${PORTSDIR}/${FILE%/}
+			mkdir -p ${PORTSDIR}/${FILE}
+			tar -xz --numeric-owner -f ${WORKDIR}/files/${HASH}.gz \
+			    -C ${PORTSDIR}/${FILE}
+			;;
+		*)
+			rm -f ${PORTSDIR}/${FILE}
+			tar -xz --numeric-owner -f ${WORKDIR}/files/${HASH}.gz \
+			    -C ${PORTSDIR} ${FILE}
+			;;
+		esac
+	done; then
+		return 1
+	fi
+	if [ ! -z "${EXTRACTPATH}" ]; then
+		return 0;
+	fi
+
+	IFS="$oldifs"
+
+	extract_metadata
+	extract_indices
+}
+
+update_run_extract() {
+	local IFS='|'
+
+# Install new files
+	echo "Extracting new files:"
+	if !
+		if ! [ -z "${REFUSE}" ]; then
+			grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort
+		else
+			sort ${WORKDIR}/INDEX
+		fi |
+	    comm -13 ${PORTSDIR}/.portsnap.INDEX - |
+	    while read FILE HASH; do
+		echo ${PORTSDIR}/${FILE}
+		if ! [ -r "${WORKDIR}/files/${HASH}.gz" ]; then
+			echo "files/${HASH}.gz not found -- snapshot corrupt."
+			return 1
+		fi
+		case ${FILE} in
+		*/)
+			mkdir -p ${PORTSDIR}/${FILE}
+			tar -xz --numeric-owner -f ${WORKDIR}/files/${HASH}.gz \
+			    -C ${PORTSDIR}/${FILE}
+			;;
+		*)
+			tar -xz --numeric-owner -f ${WORKDIR}/files/${HASH}.gz \
+			    -C ${PORTSDIR} ${FILE}
+			;;
+		esac
+	done; then
+		return 1
+	fi
+}
+
+# Do the actual work involved in "update"
+update_run() {
+	if ! [ -z "${INDEXONLY}" ]; then
+		extract_indices >/dev/null || return 1
+		return 0
+	fi
+
+	if sort ${WORKDIR}/INDEX |
+	    cmp -s ${PORTSDIR}/.portsnap.INDEX -; then
+		echo "Ports tree is already up to date."
+		return 0
+	fi
+
+# If we are REFUSEing to touch certain directories, don't remove files
+# from those directories (even if they are out of date)
+	echo -n "Removing old files and directories... "
+	if ! [ -z "${REFUSE}" ]; then 
+		sort ${WORKDIR}/INDEX |
+		    comm -23 ${PORTSDIR}/.portsnap.INDEX - | cut -f 1 -d '|' |
+		    grep -vE "${REFUSE}" |
+		    lam -s "${PORTSDIR}/" - |
+		    sed -e 's|/$||' | xargs rm -rf
+	else
+		sort ${WORKDIR}/INDEX |
+		    comm -23 ${PORTSDIR}/.portsnap.INDEX - | cut -f 1 -d '|' |
+		    lam -s "${PORTSDIR}/" - |
+		    sed -e 's|/$||' | xargs rm -rf
+	fi
+	echo "done."
+
+	update_run_extract || return 1
+	extract_metadata
+	extract_indices
+}
+
+#### Main functions -- call parameter-handling and core functions
+
+# Using the command line, configuration file, and defaults,
+# set all the parameters which are needed later.
+get_params() {
+	init_params
+	parse_cmdline $@
+	sanity_conffile
+	default_conffile
+	parse_conffile
+	default_params
+}
+
+# Fetch command.  Make sure that we're being called
+# interactively, then run fetch_check_params and fetch_run
+cmd_fetch() {
+	if [ "${INTERACTIVE}" != "YES" ]; then
+		echo -n "`basename $0` fetch should not "
+		echo "be run non-interactively."
+		echo "Run `basename $0` cron instead"
+		exit 1
+	fi
+	fetch_check_params
+	fetch_run || exit 1
+}
+
+# Cron command.  Make sure the parameters are sensible; wait
+# rand(3600) seconds; then fetch updates.  While fetching updates,
+# send output to a temporary file; only print that file if the
+# fetching failed.
+cmd_cron() {
+	fetch_check_params
+	sleep `jot -r 1 0 3600`
+
+	TMPFILE=`mktemp /tmp/portsnap.XXXXXX` || exit 1
+	if ! fetch_run >> ${TMPFILE}; then
+		cat ${TMPFILE}
+		rm ${TMPFILE}
+		exit 1
+	fi
+
+	rm ${TMPFILE}
+}
+
+# Extract command.  Make sure the parameters are sensible,
+# then extract the ports tree (or part thereof).
+cmd_extract() {
+	extract_check_params
+	extract_run || exit 1
+}
+
+# Update command.  Make sure the parameters are sensible,
+# then update the ports tree.
+cmd_update() {
+	update_check_params
+	update_run || exit 1
+}
+
+# Alfred command.  Run 'fetch' or 'cron' depending on
+# whether stdin is a terminal; then run 'update' or
+# 'extract' depending on whether ${PORTSDIR} exists.
+cmd_alfred() {
+	if [ "${INTERACTIVE}" = "YES" ]; then
+		cmd_fetch
+	else
+		cmd_cron
+	fi
+	if [ -r ${PORTSDIR}/.portsnap.INDEX ]; then
+		cmd_update
+	else
+		cmd_extract
+	fi
+}
+
+#### Entry point
+
+# Make sure we find utilities from the base system
+export PATH=/sbin:/bin:/usr/sbin:/usr/bin:${PATH}
+
+# Set LC_ALL in order to avoid problems with character ranges like [A-Z].
+export LC_ALL=C
+
+get_params $@
+for COMMAND in ${COMMANDS}; do
+	cmd_${COMMAND}
+done


Property changes on: trunk/usr.sbin/portsnap/portsnap/portsnap.sh
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property


More information about the Midnightbsd-cvs mailing list