diff --git a/lib/libfetch/Makefile b/lib/libfetch/Makefile
index 6358e62..ebb99ea 100644
--- a/lib/libfetch/Makefile
+++ b/lib/libfetch/Makefile
@@ -1,4 +1,4 @@
-# $FreeBSD: src/lib/libfetch/Makefile,v 1.14.2.5 2003/01/09 11:50:32 des Exp $
+# $FreeBSD: src/lib/libfetch/Makefile,v 1.51 2007/12/19 05:10:07 ru Exp $
 # $DragonFly: src/lib/libfetch/Makefile,v 1.9 2007/08/05 21:48:12 swildner Exp $
 
 LIB=		fetch
@@ -19,9 +19,9 @@ LDADD=		-lssl -lcrypto
 
 SHLIB_MAJOR=    4
 
-ftperr.h: ftp.errors
-	@echo "static struct fetcherr _ftp_errlist[] = {" > ${.TARGET}
-	@cat ${.ALLSRC} \
+ftperr.h: ftp.errors ${.CURDIR}/Makefile
+	@echo "static struct fetcherr ftp_errlist[] = {" > ${.TARGET}
+	@cat ${.CURDIR}/ftp.errors \
 	  | grep -v ^# \
 	  | sort \
 	  | while read NUM CAT STRING; do \
@@ -30,9 +30,9 @@ ftperr.h: ftp.errors
 	@echo "    { -1, FETCH_UNKNOWN, \"Unknown FTP error\" }" >> ${.TARGET}
 	@echo "};" >> ${.TARGET}
 
-httperr.h: http.errors
-	@echo "static struct fetcherr _http_errlist[] = {" > ${.TARGET}
-	@cat ${.ALLSRC} \
+httperr.h: http.errors ${.CURDIR}/Makefile
+	@echo "static struct fetcherr http_errlist[] = {" > ${.TARGET}
+	@cat ${.CURDIR}/http.errors \
 	  | grep -v ^# \
 	  | sort \
 	  | while read NUM CAT STRING; do \
diff --git a/lib/libfetch/common.c b/lib/libfetch/common.c
index bdd393a..9c8c990 100644
--- a/lib/libfetch/common.c
+++ b/lib/libfetch/common.c
@@ -25,7 +25,7 @@
  * (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: src/lib/libfetch/common.c,v 1.50 2005/02/16 12:46:46 des Exp $
+ * $FreeBSD: src/lib/libfetch/common.c,v 1.56 2008/04/15 23:29:51 cperciva Exp $
  * $DragonFly: src/lib/libfetch/common.c,v 1.5 2008/04/02 14:46:37 joerg Exp $
  */
 
@@ -33,8 +33,10 @@
 #include <sys/socket.h>
 #include <sys/time.h>
 #include <sys/uio.h>
+
 #include <netinet/in.h>
 
+#include <ctype.h>
 #include <errno.h>
 #include <netdb.h>
 #include <pwd.h>
@@ -53,7 +55,7 @@
 /*
  * Error messages for resolver errors
  */
-static struct fetcherr _netdb_errlist[] = {
+static struct fetcherr netdb_errlist[] = {
 #ifdef EAI_NODATA
 	{ EAI_NODATA,	FETCH_RESOLV,	"Host not found" },
 #endif
@@ -73,7 +75,7 @@ static const char ENDL[2] = "\r\n";
  * Map error code to string
  */
 static struct fetcherr *
-_fetch_finderr(struct fetcherr *p, int e)
+fetch_finderr(struct fetcherr *p, int e)
 {
 	while (p->num != -1 && p->num != e)
 		p++;
@@ -84,9 +86,9 @@ _fetch_finderr(struct fetcherr *p, int e)
  * Set error code
  */
 void
-_fetch_seterr(struct fetcherr *p, int e)
+fetch_seterr(struct fetcherr *p, int e)
 {
-	p = _fetch_finderr(p, e);
+	p = fetch_finderr(p, e);
 	fetchLastErrCode = p->cat;
 	snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
 }
@@ -95,7 +97,7 @@ _fetch_seterr(struct fetcherr *p, int e)
  * Set error code according to errno
  */
 void
-_fetch_syserr(void)
+fetch_syserr(void)
 {
 	switch (errno) {
 	case 0:
@@ -155,7 +157,7 @@ default:
  * Emit status message
  */
 void
-_fetch_info(const char *fmt, ...)
+fetch_info(const char *fmt, ...)
 {
 	va_list ap;
 
@@ -172,7 +174,7 @@ _fetch_info(const char *fmt, ...)
  * Return the default port for a scheme
  */
 int
-_fetch_default_port(const char *scheme)
+fetch_default_port(const char *scheme)
 {
 	struct servent *se;
 
@@ -189,7 +191,7 @@ _fetch_default_port(const char *scheme)
  * Return the default proxy port for a scheme
  */
 int
-_fetch_default_proxy_port(const char *scheme)
+fetch_default_proxy_port(const char *scheme)
 {
 	if (strcasecmp(scheme, SCHEME_FTP) == 0)
 		return (FTP_DEFAULT_PROXY_PORT);
@@ -203,7 +205,7 @@ _fetch_default_proxy_port(const char *scheme)
  * Create a connection for an existing descriptor.
  */
 conn_t *
-_fetch_reopen(int sd)
+fetch_reopen(int sd)
 {
 	conn_t *conn;
 
@@ -220,7 +222,7 @@ _fetch_reopen(int sd)
  * Bump a connection's reference count.
  */
 conn_t *
-_fetch_ref(conn_t *conn)
+fetch_ref(conn_t *conn)
 {
 
 	++conn->ref;
@@ -232,7 +234,7 @@ _fetch_ref(conn_t *conn)
  * Bind a socket to a specific local address
  */
 int
-_fetch_bind(int sd, int af, const char *addr)
+fetch_bind(int sd, int af, const char *addr)
 {
 	struct addrinfo hints, *res, *res0;
 	int err;
@@ -254,7 +256,7 @@ _fetch_bind(int sd, int af, const char *addr)
  * Establish a TCP connection to the specified port on the specified host.
  */
 conn_t *
-_fetch_connect(const char *host, int port, int af, int verbose)
+fetch_connect(const char *host, int port, int af, int verbose)
 {
 	conn_t *conn;
 	char pbuf[10];
@@ -265,7 +267,7 @@ _fetch_connect(const char *host, int port, int af, int verbose)
 	DEBUG(fprintf(stderr, "---> %s:%d\n", host, port));
 
 	if (verbose)
-		_fetch_info("looking up %s", host);
+		fetch_info("looking up %s", host);
 
 	/* look up host name and set up socket address structure */
 	snprintf(pbuf, sizeof(pbuf), "%d", port);
@@ -274,13 +276,13 @@ _fetch_connect(const char *host, int port, int af, int verbose)
 	hints.ai_socktype = SOCK_STREAM;
 	hints.ai_protocol = 0;
 	if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
-		_netdb_seterr(err);
+		netdb_seterr(err);
 		return (NULL);
 	}
 	bindaddr = getenv("FETCH_BIND_ADDRESS");
 
 	if (verbose)
-		_fetch_info("connecting to %s:%d", host, port);
+		fetch_info("connecting to %s:%d", host, port);
 
 	/* try to connect */
 	for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) {
@@ -288,8 +290,8 @@ _fetch_connect(const char *host, int port, int af, int verbose)
 			 res->ai_protocol)) == -1)
 			continue;
 		if (bindaddr != NULL && *bindaddr != '\0' &&
-		    _fetch_bind(sd, res->ai_family, bindaddr) != 0) {
-			_fetch_info("failed to bind to '%s'", bindaddr);
+		    fetch_bind(sd, res->ai_family, bindaddr) != 0) {
+			fetch_info("failed to bind to '%s'", bindaddr);
 			close(sd);
 			continue;
 		}
@@ -299,12 +301,12 @@ _fetch_connect(const char *host, int port, int af, int verbose)
 	}
 	freeaddrinfo(res0);
 	if (sd == -1) {
-		_fetch_syserr();
+		fetch_syserr();
 		return (NULL);
 	}
 
-	if ((conn = _fetch_reopen(sd)) == NULL) {
-		_fetch_syserr();
+	if ((conn = fetch_reopen(sd)) == NULL) {
+		fetch_syserr();
 		close(sd);
 	}
 	return (conn);
@@ -315,7 +317,7 @@ _fetch_connect(const char *host, int port, int af, int verbose)
  * Enable SSL on a connection.
  */
 int
-_fetch_ssl(conn_t *conn, int verbose)
+fetch_ssl(conn_t *conn, int verbose)
 {
 
 #ifdef WITH_SSL
@@ -373,9 +375,9 @@ _fetch_ssl(conn_t *conn, int verbose)
  * Read a character from a connection w/ timeout
  */
 ssize_t
-_fetch_read(conn_t *conn, char *buf, size_t len)
+fetch_read(conn_t *conn, char *buf, size_t len)
 {
-	struct timeval now, timeout, wait;
+	struct timeval now, timeout, delta;
 	fd_set readfds;
 	ssize_t rlen, total;
 	int r;
@@ -391,23 +393,23 @@ _fetch_read(conn_t *conn, char *buf, size_t len)
 		while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) {
 			FD_SET(conn->sd, &readfds);
 			gettimeofday(&now, NULL);
-			wait.tv_sec = timeout.tv_sec - now.tv_sec;
-			wait.tv_usec = timeout.tv_usec - now.tv_usec;
-			if (wait.tv_usec < 0) {
-				wait.tv_usec += 1000000;
-				wait.tv_sec--;
+			delta.tv_sec = timeout.tv_sec - now.tv_sec;
+			delta.tv_usec = timeout.tv_usec - now.tv_usec;
+			if (delta.tv_usec < 0) {
+				delta.tv_usec += 1000000;
+				delta.tv_sec--;
 			}
-			if (wait.tv_sec < 0) {
+			if (delta.tv_sec < 0) {
 				errno = ETIMEDOUT;
-				_fetch_syserr();
+				fetch_syserr();
 				return (-1);
 			}
 			errno = 0;
-			r = select(conn->sd + 1, &readfds, NULL, NULL, &wait);
+			r = select(conn->sd + 1, &readfds, NULL, NULL, &delta);
 			if (r == -1) {
 				if (errno == EINTR && fetchRestartCalls)
 					continue;
-				_fetch_syserr();
+				fetch_syserr();
 				return (-1);
 			}
 		}
@@ -438,7 +440,7 @@ _fetch_read(conn_t *conn, char *buf, size_t len)
 #define MIN_BUF_SIZE 1024
 
 int
-_fetch_getln(conn_t *conn)
+fetch_getln(conn_t *conn)
 {
 	char *tmp;
 	size_t tmpsize;
@@ -457,7 +459,7 @@ _fetch_getln(conn_t *conn)
 	conn->buflen = 0;
 
 	do {
-		len = _fetch_read(conn, &c, 1);
+		len = fetch_read(conn, &c, 1);
 		if (len == -1)
 			return (-1);
 		if (len == 0)
@@ -485,13 +487,13 @@ _fetch_getln(conn_t *conn)
  * Write to a connection w/ timeout
  */
 ssize_t
-_fetch_write(conn_t *conn, const char *buf, size_t len)
+fetch_write(conn_t *conn, const char *buf, size_t len)
 {
 	struct iovec iov;
 
 	iov.iov_base = __DECONST(char *, buf);
 	iov.iov_len = len;
-	return _fetch_writev(conn, &iov, 1);
+	return fetch_writev(conn, &iov, 1);
 }
 
 /*
@@ -499,9 +501,9 @@ _fetch_write(conn_t *conn, const char *buf, size_t len)
  * Note: can modify the iovec.
  */
 ssize_t
-_fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt)
+fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt)
 {
-	struct timeval now, timeout, wait;
+	struct timeval now, timeout, delta;
 	fd_set writefds;
 	ssize_t wlen, total;
 	int r;
@@ -517,19 +519,19 @@ _fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt)
 		while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) {
 			FD_SET(conn->sd, &writefds);
 			gettimeofday(&now, NULL);
-			wait.tv_sec = timeout.tv_sec - now.tv_sec;
-			wait.tv_usec = timeout.tv_usec - now.tv_usec;
-			if (wait.tv_usec < 0) {
-				wait.tv_usec += 1000000;
-				wait.tv_sec--;
+			delta.tv_sec = timeout.tv_sec - now.tv_sec;
+			delta.tv_usec = timeout.tv_usec - now.tv_usec;
+			if (delta.tv_usec < 0) {
+				delta.tv_usec += 1000000;
+				delta.tv_sec--;
 			}
-			if (wait.tv_sec < 0) {
+			if (delta.tv_sec < 0) {
 				errno = ETIMEDOUT;
-				_fetch_syserr();
+				fetch_syserr();
 				return (-1);
 			}
 			errno = 0;
-			r = select(conn->sd + 1, NULL, &writefds, NULL, &wait);
+			r = select(conn->sd + 1, NULL, &writefds, NULL, &delta);
 			if (r == -1) {
 				if (errno == EINTR && fetchRestartCalls)
 					continue;
@@ -547,7 +549,7 @@ _fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt)
 		if (wlen == 0) {
 			/* we consider a short write a failure */
 			errno = EPIPE;
-			_fetch_syserr();
+			fetch_syserr();
 			return (-1);
 		}
 		if (wlen < 0) {
@@ -574,7 +576,7 @@ _fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt)
  * Write a line of text to a connection w/ timeout
  */
 int
-_fetch_putln(conn_t *conn, const char *str, size_t len)
+fetch_putln(conn_t *conn, const char *str, size_t len)
 {
 	struct iovec iov[2];
 	int ret;
@@ -585,9 +587,9 @@ _fetch_putln(conn_t *conn, const char *str, size_t len)
 	iov[1].iov_base = __DECONST(char *, ENDL);
 	iov[1].iov_len = sizeof(ENDL);
 	if (len == 0)
-		ret = _fetch_writev(conn, &iov[1], 1);
+		ret = fetch_writev(conn, &iov[1], 1);
 	else
-		ret = _fetch_writev(conn, iov, 2);
+		ret = fetch_writev(conn, iov, 2);
 	if (ret == -1)
 		return (-1);
 	return (0);
@@ -598,7 +600,7 @@ _fetch_putln(conn_t *conn, const char *str, size_t len)
  * Close connection
  */
 int
-_fetch_close(conn_t *conn)
+fetch_close(conn_t *conn)
 {
 	int ret;
 
@@ -614,7 +616,7 @@ _fetch_close(conn_t *conn)
 /*** Directory-related utility functions *************************************/
 
 int
-_fetch_add_entry(struct url_ent **p, int *size, int *len,
+fetch_add_entry(struct url_ent **p, int *size, int *len,
     const char *name, struct url_stat *us)
 {
 	struct url_ent *tmp;
@@ -628,7 +630,7 @@ _fetch_add_entry(struct url_ent **p, int *size, int *len,
 		tmp = realloc(*p, (*size * 2 + 1) * sizeof(**p));
 		if (tmp == NULL) {
 			errno = ENOMEM;
-			_fetch_syserr();
+			fetch_syserr();
 			return (-1);
 		}
 		*size = (*size * 2 + 1);
@@ -637,7 +639,7 @@ _fetch_add_entry(struct url_ent **p, int *size, int *len,
 
 	tmp = *p + *len;
 	snprintf(tmp->name, PATH_MAX, "%s", name);
-	bcopy(us, &tmp->stat, sizeof(*us));
+	memcpy(&tmp->stat, us, sizeof(*us));
 
 	(*len)++;
 	(++tmp)->name[0] = 0;
@@ -649,7 +651,7 @@ _fetch_add_entry(struct url_ent **p, int *size, int *len,
 /*** Authentication-related utility functions ********************************/
 
 static const char *
-_fetch_read_word(FILE *f)
+fetch_read_word(FILE *f)
 {
 	static char word[1024];
 
@@ -662,7 +664,7 @@ _fetch_read_word(FILE *f)
  * Get authentication data for a URL from .netrc
  */
 int
-_fetch_netrc_auth(struct url *url)
+fetch_netrc_auth(struct url *url)
 {
 	char fn[PATH_MAX];
 	const char *word;
@@ -671,7 +673,7 @@ _fetch_netrc_auth(struct url *url)
 
 	if ((p = getenv("NETRC")) != NULL) {
 		if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) {
-			_fetch_info("$NETRC specifies a file name "
+			fetch_info("$NETRC specifies a file name "
 			    "longer than PATH_MAX");
 			return (-1);
 		}
@@ -689,39 +691,39 @@ _fetch_netrc_auth(struct url *url)
 
 	if ((f = fopen(fn, "r")) == NULL)
 		return (-1);
-	while ((word = _fetch_read_word(f)) != NULL) {
+	while ((word = fetch_read_word(f)) != NULL) {
 		if (strcmp(word, "default") == 0) {
-			DEBUG(_fetch_info("Using default .netrc settings"));
+			DEBUG(fetch_info("Using default .netrc settings"));
 			break;
 		}
 		if (strcmp(word, "machine") == 0 &&
-		    (word = _fetch_read_word(f)) != NULL &&
+		    (word = fetch_read_word(f)) != NULL &&
 		    strcasecmp(word, url->host) == 0) {
-			DEBUG(_fetch_info("Using .netrc settings for %s", word));
+			DEBUG(fetch_info("Using .netrc settings for %s", word));
 			break;
 		}
 	}
 	if (word == NULL)
 		goto ferr;
-	while ((word = _fetch_read_word(f)) != NULL) {
+	while ((word = fetch_read_word(f)) != NULL) {
 		if (strcmp(word, "login") == 0) {
-			if ((word = _fetch_read_word(f)) == NULL)
+			if ((word = fetch_read_word(f)) == NULL)
 				goto ferr;
 			if (snprintf(url->user, sizeof(url->user),
 				"%s", word) > (int)sizeof(url->user)) {
-				_fetch_info("login name in .netrc is too long");
+				fetch_info("login name in .netrc is too long");
 				url->user[0] = '\0';
 			}
 		} else if (strcmp(word, "password") == 0) {
-			if ((word = _fetch_read_word(f)) == NULL)
+			if ((word = fetch_read_word(f)) == NULL)
 				goto ferr;
 			if (snprintf(url->pwd, sizeof(url->pwd),
 				"%s", word) > (int)sizeof(url->pwd)) {
-				_fetch_info("password in .netrc is too long");
+				fetch_info("password in .netrc is too long");
 				url->pwd[0] = '\0';
 			}
 		} else if (strcmp(word, "account") == 0) {
-			if ((word = _fetch_read_word(f)) == NULL)
+			if ((word = fetch_read_word(f)) == NULL)
 				goto ferr;
 			/* XXX not supported! */
 		} else {
@@ -734,3 +736,51 @@ _fetch_netrc_auth(struct url *url)
 	fclose(f);
 	return (-1);
 }
+
+/*
+ * The no_proxy environment variable specifies a set of domains for
+ * which the proxy should not be consulted; the contents is a comma-,
+ * or space-separated list of domain names.  A single asterisk will
+ * override all proxy variables and no transactions will be proxied
+ * (for compatability with lynx and curl, see the discussion at
+ * <http://curl.haxx.se/mail/archive_pre_oct_99/0009.html>).
+ */
+int
+fetch_no_proxy_match(const char *host)
+{
+	const char *no_proxy, *p, *q;
+	size_t h_len, d_len;
+
+	if ((no_proxy = getenv("NO_PROXY")) == NULL &&
+	    (no_proxy = getenv("no_proxy")) == NULL)
+		return (0);
+
+	/* asterisk matches any hostname */
+	if (strcmp(no_proxy, "*") == 0)
+		return (1);
+
+	h_len = strlen(host);
+	p = no_proxy;
+	do {
+		/* position p at the beginning of a domain suffix */
+		while (*p == ',' || isspace((unsigned char)*p))
+			p++;
+
+		/* position q at the first separator character */
+		for (q = p; *q; ++q)
+			if (*q == ',' || isspace((unsigned char)*q))
+				break;
+
+		d_len = q - p;
+		if (d_len > 0 && h_len > d_len &&
+		    strncasecmp(host + h_len - d_len,
+			p, d_len) == 0) {
+			/* domain name matches */
+			return (1);
+		}
+
+		p = q + 1;
+	} while (*q);
+
+	return (0);
+}
diff --git a/lib/libfetch/common.h b/lib/libfetch/common.h
index 4561a86..21032a7 100644
--- a/lib/libfetch/common.h
+++ b/lib/libfetch/common.h
@@ -25,7 +25,7 @@
  * (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: src/lib/libfetch/common.h,v 1.28 2004/09/21 18:35:20 des Exp $
+ * $FreeBSD: src/lib/libfetch/common.h,v 1.30 2007/12/18 11:03:07 des Exp $
  * $DragonFly: src/lib/libfetch/common.h,v 1.3 2007/08/05 21:48:12 swildner Exp $
  */
 
@@ -69,33 +69,34 @@ struct fetcherr {
 	const char	*string;
 };
 
-/* for _fetch_writev */
+/* for fetch_writev */
 struct iovec;
 
-void		 _fetch_seterr(struct fetcherr *, int);
-void		 _fetch_syserr(void);
-void		 _fetch_info(const char *, ...);
-int		 _fetch_default_port(const char *);
-int		 _fetch_default_proxy_port(const char *);
-int		 _fetch_bind(int, int, const char *);
-conn_t		*_fetch_connect(const char *, int, int, int);
-conn_t		*_fetch_reopen(int);
-conn_t		*_fetch_ref(conn_t *);
-int		 _fetch_ssl(conn_t *, int);
-ssize_t		 _fetch_read(conn_t *, char *, size_t);
-int		 _fetch_getln(conn_t *);
-ssize_t		 _fetch_write(conn_t *, const char *, size_t);
-ssize_t		 _fetch_writev(conn_t *, struct iovec *, int);
-int		 _fetch_putln(conn_t *, const char *, size_t);
-int		 _fetch_close(conn_t *);
-int		 _fetch_add_entry(struct url_ent **, int *, int *,
+void		 fetch_seterr(struct fetcherr *, int);
+void		 fetch_syserr(void);
+void		 fetch_info(const char *, ...);
+int		 fetch_default_port(const char *);
+int		 fetch_default_proxy_port(const char *);
+int		 fetch_bind(int, int, const char *);
+conn_t		*fetch_connect(const char *, int, int, int);
+conn_t		*fetch_reopen(int);
+conn_t		*fetch_ref(conn_t *);
+int		 fetch_ssl(conn_t *, int);
+ssize_t		 fetch_read(conn_t *, char *, size_t);
+int		 fetch_getln(conn_t *);
+ssize_t		 fetch_write(conn_t *, const char *, size_t);
+ssize_t		 fetch_writev(conn_t *, struct iovec *, int);
+int		 fetch_putln(conn_t *, const char *, size_t);
+int		 fetch_close(conn_t *);
+int		 fetch_add_entry(struct url_ent **, int *, int *,
 		     const char *, struct url_stat *);
-int		 _fetch_netrc_auth(struct url *url);
+int		 fetch_netrc_auth(struct url *url);
+int		 fetch_no_proxy_match(const char *);
 
-#define _ftp_seterr(n)	 _fetch_seterr(_ftp_errlist, n)
-#define _http_seterr(n)	 _fetch_seterr(_http_errlist, n)
-#define _netdb_seterr(n) _fetch_seterr(_netdb_errlist, n)
-#define _url_seterr(n)	 _fetch_seterr(_url_errlist, n)
+#define ftp_seterr(n)	 fetch_seterr(ftp_errlist, n)
+#define http_seterr(n)	 fetch_seterr(http_errlist, n)
+#define netdb_seterr(n)	 fetch_seterr(netdb_errlist, n)
+#define url_seterr(n)	 fetch_seterr(url_errlist, n)
 
 #ifndef NDEBUG
 #define DEBUG(x) do { if (fetchDebug) { x; } } while (0)
@@ -104,7 +105,7 @@ int		 _fetch_netrc_auth(struct url *url);
 #endif
 
 /*
- * I don't really like exporting _http_request() and _ftp_request(),
+ * I don't really like exporting http_request() and ftp_request(),
  * but the HTTP and FTP code occasionally needs to cross-call
  * eachother, and this saves me from adding a lot of special-case code
  * to handle those cases.
@@ -112,9 +113,9 @@ int		 _fetch_netrc_auth(struct url *url);
  * Note that _*_request() free purl, which is way ugly but saves us a
  * whole lot of trouble.
  */
-FILE		*_http_request(struct url *, const char *,
+FILE		*http_request(struct url *, const char *,
 		     struct url_stat *, struct url *, const char *);
-FILE		*_ftp_request(struct url *, const char *,
+FILE		*ftp_request(struct url *, const char *,
 		     struct url_stat *, struct url *, const char *);
 
 /*
diff --git a/lib/libfetch/fetch.3 b/lib/libfetch/fetch.3
index 35813d7..115cbaf 100644
--- a/lib/libfetch/fetch.3
+++ b/lib/libfetch/fetch.3
@@ -1,4 +1,5 @@
-.\" Copyright (c) 1998 Dag-Erling Co�dan Sm�rgrav
+.\"-
+.\" Copyright (c) 1998-2004 Dag-Erling Co�dan Sm�rgrav
 .\" All rights reserved.
 .\"
 .\" Redistribution and use in source and binary forms, with or without
@@ -22,10 +23,10 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/lib/libfetch/fetch.3,v 1.63 2007/05/24 20:28:14 des Exp $
+.\" $FreeBSD: src/lib/libfetch/fetch.3,v 1.66 2008/12/15 08:27:44 murray Exp $
 .\" $DragonFly: src/lib/libfetch/fetch.3,v 1.5 2007/11/23 23:16:36 swildner Exp $
 .\"
-.Dd July 1, 1998
+.Dd May 10, 2009
 .Dt FETCH 3
 .Os
 .Sh NAME
@@ -165,9 +166,16 @@ struct url {
     char	*doc;
     off_t	 offset;
     size_t	 length;
+    time_t	 ims_time;
 };
 .Ed
 .Pp
+The
+.Va ims_time
+field stores the time value for
+.Li If-Modified-Since
+HTTP requests.
+.Pp
 The pointer returned by
 .Fn fetchMakeURL
 or
@@ -236,7 +244,7 @@ structure is defined as follows in
 .In fetch.h :
 .Bd -literal
 struct url_ent {
-    char         name[MAXPATHLEN];
+    char         name[PATH_MAX];
     struct url_stat stat;
 };
 .Ed
@@ -353,6 +361,22 @@ and
 .Fn fetchPutHTTP
 will use a direct connection even if a proxy server is defined.
 .Pp
+If the
+.Ql i
+(if-modified-since) flag is specified, and
+the
+.Va ims_time
+field is set in 
+.Vt "struct url" ,
+then
+.Fn fetchXGetHTTP
+and
+.Fn fetchGetHTTP
+will send a conditional
+.Li If-Modified-Since
+HTTP header to only fetch the content if it is newer than
+.Va ims_time .
+.Pp
 Since there seems to be no good way of implementing the HTTP PUT
 method in a manner consistent with the rest of the
 .Nm fetch
@@ -532,6 +556,14 @@ See
 .Xr ftp 1
 for a description of the file format.
 This feature is experimental.
+.It Ev NO_PROXY
+Either a single asterisk, which disables the use of proxies
+altogether, or a comma- or whitespace-separated list of hosts for
+which proxies should not be used.
+.It Ev no_proxy
+Same as
+.Ev NO_PROXY ,
+for compatibility.
 .El
 .Sh EXAMPLES
 To access a proxy server on
@@ -555,6 +587,14 @@ environment variable:
 HTTP_PROXY=http://proxy.example.com:8080
 HTTP_PROXY_AUTH=basic:*:<user>:<pwd>
 .Ed
+.Pp
+To disable the use of a proxy for an HTTP server running on the local
+host, define
+.Ev NO_PROXY
+as follows:
+.Bd -literal -offset indent
+NO_PROXY=localhost,127.0.0.1
+.Ed
 .Sh SEE ALSO
 .Xr fetch 1 ,
 .Xr ftpio 3 ,
@@ -631,7 +671,7 @@ and
 .An Jordan K. Hubbard Aq jkh@FreeBSD.org .
 .Pp
 This manual page was written by
-.An Dag-Erling Co\(:idan Sm\(/orgrav Aq des@FreeBSD.org .
+.An Dag-Erling Sm\(/orgrav Aq des@FreeBSD.org .
 .Sh BUGS
 Some parts of the library are not yet implemented.
 The most notable
diff --git a/lib/libfetch/fetch.c b/lib/libfetch/fetch.c
index 1b5754d..3f06c7e 100644
--- a/lib/libfetch/fetch.c
+++ b/lib/libfetch/fetch.c
@@ -25,7 +25,7 @@
  * (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: src/lib/libfetch/fetch.c,v 1.38 2004/09/21 18:35:20 des Exp $
+ * $FreeBSD: src/lib/libfetch/fetch.c,v 1.42 2008/12/17 18:00:18 murray Exp $
  * $DragonFly: src/lib/libfetch/fetch.c,v 1.3 2007/08/05 21:48:12 swildner Exp $
  */
 
@@ -56,7 +56,7 @@ int	 fetchDebug;
 #define URL_MALFORMED		1
 #define URL_BAD_SCHEME		2
 #define URL_BAD_PORT		3
-static struct fetcherr _url_errlist[] = {
+static struct fetcherr url_errlist[] = {
 	{ URL_MALFORMED,	FETCH_URL,	"Malformed URL" },
 	{ URL_BAD_SCHEME,	FETCH_URL,	"Invalid URL scheme" },
 	{ URL_BAD_PORT,		FETCH_URL,	"Invalid server port" },
@@ -74,9 +74,7 @@ static struct fetcherr _url_errlist[] = {
 FILE *
 fetchXGet(struct url *URL, struct url_stat *us, const char *flags)
 {
-	int direct;
 
-	direct = CHECK_FLAG('d');
 	if (us != NULL) {
 		us->size = -1;
 		us->atime = us->mtime = 0;
@@ -89,7 +87,7 @@ fetchXGet(struct url *URL, struct url_stat *us, const char *flags)
 		return (fetchXGetHTTP(URL, us, flags));
 	else if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0)
 		return (fetchXGetHTTP(URL, us, flags));
-	_url_seterr(URL_BAD_SCHEME);
+	url_seterr(URL_BAD_SCHEME);
 	return (NULL);
 }
 
@@ -110,9 +108,7 @@ fetchGet(struct url *URL, const char *flags)
 FILE *
 fetchPut(struct url *URL, const char *flags)
 {
-	int direct;
 
-	direct = CHECK_FLAG('d');
 	if (strcasecmp(URL->scheme, SCHEME_FILE) == 0)
 		return (fetchPutFile(URL, flags));
 	else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0)
@@ -121,7 +117,7 @@ fetchPut(struct url *URL, const char *flags)
 		return (fetchPutHTTP(URL, flags));
 	else if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0)
 		return (fetchPutHTTP(URL, flags));
-	_url_seterr(URL_BAD_SCHEME);
+	url_seterr(URL_BAD_SCHEME);
 	return (NULL);
 }
 
@@ -132,9 +128,7 @@ fetchPut(struct url *URL, const char *flags)
 int
 fetchStat(struct url *URL, struct url_stat *us, const char *flags)
 {
-	int direct;
 
-	direct = CHECK_FLAG('d');
 	if (us != NULL) {
 		us->size = -1;
 		us->atime = us->mtime = 0;
@@ -147,7 +141,7 @@ fetchStat(struct url *URL, struct url_stat *us, const char *flags)
 		return (fetchStatHTTP(URL, us, flags));
 	else if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0)
 		return (fetchStatHTTP(URL, us, flags));
-	_url_seterr(URL_BAD_SCHEME);
+	url_seterr(URL_BAD_SCHEME);
 	return (-1);
 }
 
@@ -158,9 +152,7 @@ fetchStat(struct url *URL, struct url_stat *us, const char *flags)
 struct url_ent *
 fetchList(struct url *URL, const char *flags)
 {
-	int direct;
 
-	direct = CHECK_FLAG('d');
 	if (strcasecmp(URL->scheme, SCHEME_FILE) == 0)
 		return (fetchListFile(URL, flags));
 	else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0)
@@ -169,7 +161,7 @@ fetchList(struct url *URL, const char *flags)
 		return (fetchListHTTP(URL, flags));
 	else if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0)
 		return (fetchListHTTP(URL, flags));
-	_url_seterr(URL_BAD_SCHEME);
+	url_seterr(URL_BAD_SCHEME);
 	return (NULL);
 }
 
@@ -264,23 +256,23 @@ fetchMakeURL(const char *scheme, const char *host, int port, const char *doc,
 	struct url *u;
 
 	if (!scheme || (!host && !doc)) {
-		_url_seterr(URL_MALFORMED);
+		url_seterr(URL_MALFORMED);
 		return (NULL);
 	}
 
 	if (port < 0 || port > 65535) {
-		_url_seterr(URL_BAD_PORT);
+		url_seterr(URL_BAD_PORT);
 		return (NULL);
 	}
 
 	/* allocate struct url */
 	if ((u = calloc(1, sizeof(*u))) == NULL) {
-		_fetch_syserr();
+		fetch_syserr();
 		return (NULL);
 	}
 
 	if ((u->doc = strdup(doc ? doc : "/")) == NULL) {
-		_fetch_syserr();
+		fetch_syserr();
 		free(u);
 		return (NULL);
 	}
@@ -311,7 +303,7 @@ fetchParseURL(const char *URL)
 
 	/* allocate struct url */
 	if ((u = calloc(1, sizeof(*u))) == NULL) {
-		_fetch_syserr();
+		fetch_syserr();
 		return (NULL);
 	}
 
@@ -369,11 +361,11 @@ fetchParseURL(const char *URL)
 	/* port */
 	if (*p == ':') {
 		for (q = ++p; *q && (*q != '/'); q++)
-			if (isdigit(*q))
+			if (isdigit((unsigned char)*q))
 				u->port = u->port * 10 + (*q - '0');
 			else {
 				/* invalid port */
-				_url_seterr(URL_BAD_PORT);
+				url_seterr(URL_BAD_PORT);
 				goto ouch;
 			}
 		p = q;
@@ -390,12 +382,12 @@ nohost:
 
 		/* percent-escape whitespace. */
 		if ((doc = malloc(strlen(p) * 3 + 1)) == NULL) {
-			_fetch_syserr();
+			fetch_syserr();
 			goto ouch;
 		}
 		u->doc = doc;
 		while (*p != '\0') {
-			if (!isspace(*p)) {
+			if (!isspace((unsigned char)*p)) {
 				*doc++ = *p++;
 			} else {
 				*doc++ = '%';
@@ -406,7 +398,7 @@ nohost:
 		}
 		*doc = '\0';
 	} else if ((u->doc = strdup(p)) == NULL) {
-		_fetch_syserr();
+		fetch_syserr();
 		goto ouch;
 	}
 
diff --git a/lib/libfetch/fetch.h b/lib/libfetch/fetch.h
index 6fc5af2..193922c 100644
--- a/lib/libfetch/fetch.h
+++ b/lib/libfetch/fetch.h
@@ -25,7 +25,7 @@
  * (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: src/lib/libfetch/fetch.h,v 1.26 2004/09/21 18:35:20 des Exp $
+ * $FreeBSD: src/lib/libfetch/fetch.h,v 1.27 2008/12/15 08:27:44 murray Exp $
  * $DragonFly: src/lib/libfetch/fetch.h,v 1.3 2007/08/05 21:48:12 swildner Exp $
  */
 
@@ -47,6 +47,7 @@ struct url {
 	char		*doc;
 	off_t		 offset;
 	size_t		 length;
+	time_t		 ims_time;
 };
 
 struct url_stat {
diff --git a/lib/libfetch/file.c b/lib/libfetch/file.c
index ed955ad..35b111e 100644
--- a/lib/libfetch/file.c
+++ b/lib/libfetch/file.c
@@ -25,7 +25,7 @@
  * (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: src/lib/libfetch/file.c,v 1.17 2004/09/21 18:35:20 des Exp $
+ * $FreeBSD: src/lib/libfetch/file.c,v 1.18 2007/12/14 10:26:58 des Exp $
  * $DragonFly: src/lib/libfetch/file.c,v 1.3 2007/08/05 21:48:12 swildner Exp $
  */
 
@@ -50,11 +50,11 @@ fetchXGetFile(struct url *u, struct url_stat *us, const char *flags)
 	f = fopen(u->doc, "r");
 
 	if (f == NULL)
-		_fetch_syserr();
+		fetch_syserr();
 
 	if (u->offset && fseeko(f, u->offset, SEEK_SET) == -1) {
 		fclose(f);
-		_fetch_syserr();
+		fetch_syserr();
 	}
 
 	return (f);
@@ -77,25 +77,25 @@ fetchPutFile(struct url *u, const char *flags)
 		f = fopen(u->doc, "w+");
 
 	if (f == NULL)
-		_fetch_syserr();
+		fetch_syserr();
 
 	if (u->offset && fseeko(f, u->offset, SEEK_SET) == -1) {
 		fclose(f);
-		_fetch_syserr();
+		fetch_syserr();
 	}
 
 	return (f);
 }
 
 static int
-_fetch_stat_file(const char *fn, struct url_stat *us)
+fetch_stat_file(const char *fn, struct url_stat *us)
 {
 	struct stat sb;
 
 	us->size = -1;
 	us->atime = us->mtime = 0;
 	if (stat(fn, &sb) == -1) {
-		_fetch_syserr();
+		fetch_syserr();
 		return (-1);
 	}
 	us->size = sb.st_size;
@@ -107,7 +107,7 @@ _fetch_stat_file(const char *fn, struct url_stat *us)
 int
 fetchStatFile(struct url *u, struct url_stat *us, const char *flags __unused)
 {
-	return (_fetch_stat_file(u->doc, us));
+	return (fetch_stat_file(u->doc, us));
 }
 
 struct url_ent *
@@ -122,7 +122,7 @@ fetchListFile(struct url *u, const char *flags __unused)
 	int l;
 
 	if ((dir = opendir(u->doc)) == NULL) {
-		_fetch_syserr();
+		fetch_syserr();
 		return (NULL);
 	}
 
@@ -136,10 +136,10 @@ fetchListFile(struct url *u, const char *flags __unused)
 	while ((de = readdir(dir)) != NULL) {
 		strncpy(p, de->d_name, l - 1);
 		p[l - 1] = 0;
-		if (_fetch_stat_file(fn, &us) == -1)
+		if (fetch_stat_file(fn, &us) == -1)
 			/* should I return a partial result, or abort? */
 			break;
-		_fetch_add_entry(&ue, &size, &len, de->d_name, &us);
+		fetch_add_entry(&ue, &size, &len, de->d_name, &us);
 	}
 
 	return (ue);
diff --git a/lib/libfetch/ftp.c b/lib/libfetch/ftp.c
index 0fa3b2d..bf439d0 100644
--- a/lib/libfetch/ftp.c
+++ b/lib/libfetch/ftp.c
@@ -25,7 +25,7 @@
  * (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: src/lib/libfetch/ftp.c,v 1.96 2007/04/22 22:33:29 njl Exp $
+ * $FreeBSD: src/lib/libfetch/ftp.c,v 1.102 2008/02/08 09:48:48 des Exp $
  * $DragonFly: src/lib/libfetch/ftp.c,v 1.4 2007/08/05 21:48:12 swildner Exp $
  */
 
@@ -102,11 +102,16 @@
 static struct url cached_host;
 static conn_t	*cached_connection;
 
-#define isftpreply(foo) (isdigit(foo[0]) && isdigit(foo[1]) \
-			 && isdigit(foo[2]) \
-			 && (foo[3] == ' ' || foo[3] == '\0'))
-#define isftpinfo(foo) (isdigit(foo[0]) && isdigit(foo[1]) \
-			&& isdigit(foo[2]) && foo[3] == '-')
+#define isftpreply(foo)				\
+	(isdigit((unsigned char)foo[0]) &&	\
+	    isdigit((unsigned char)foo[1]) &&	\
+	    isdigit((unsigned char)foo[2]) &&	\
+	    (foo[3] == ' ' || foo[3] == '\0'))
+#define isftpinfo(foo) \
+	(isdigit((unsigned char)foo[0]) &&	\
+	    isdigit((unsigned char)foo[1]) &&	\
+	    isdigit((unsigned char)foo[2]) &&	\
+	    foo[3] == '-')
 
 /*
  * Translate IPv4 mapped IPv6 address to IPv4 address
@@ -135,27 +140,28 @@ unmappedaddr(struct sockaddr_in6 *sin6)
  * Get server response
  */
 static int
-_ftp_chkerr(conn_t *conn)
+ftp_chkerr(conn_t *conn)
 {
-	if (_fetch_getln(conn) == -1) {
-		_fetch_syserr();
+	if (fetch_getln(conn) == -1) {
+		fetch_syserr();
 		return (-1);
 	}
 	if (isftpinfo(conn->buf)) {
 		while (conn->buflen && !isftpreply(conn->buf)) {
-			if (_fetch_getln(conn) == -1) {
-				_fetch_syserr();
+			if (fetch_getln(conn) == -1) {
+				fetch_syserr();
 				return (-1);
 			}
 		}
 	}
 
-	while (conn->buflen && isspace(conn->buf[conn->buflen - 1]))
+	while (conn->buflen &&
+	    isspace((unsigned char)conn->buf[conn->buflen - 1]))
 		conn->buflen--;
 	conn->buf[conn->buflen] = '\0';
 
 	if (!isftpreply(conn->buf)) {
-		_ftp_seterr(FTP_PROTOCOL_ERROR);
+		ftp_seterr(FTP_PROTOCOL_ERROR);
 		return (-1);
 	}
 
@@ -170,7 +176,7 @@ _ftp_chkerr(conn_t *conn)
  * Send a command and check reply
  */
 static int
-_ftp_cmd(conn_t *conn, const char *fmt, ...)
+ftp_cmd(conn_t *conn, const char *fmt, ...)
 {
 	va_list ap;
 	size_t len;
@@ -183,26 +189,26 @@ _ftp_cmd(conn_t *conn, const char *fmt, ...)
 
 	if (msg == NULL) {
 		errno = ENOMEM;
-		_fetch_syserr();
+		fetch_syserr();
 		return (-1);
 	}
 
-	r = _fetch_putln(conn, msg, len);
+	r = fetch_putln(conn, msg, len);
 	free(msg);
 
 	if (r == -1) {
-		_fetch_syserr();
+		fetch_syserr();
 		return (-1);
 	}
 
-	return (_ftp_chkerr(conn));
+	return (ftp_chkerr(conn));
 }
 
 /*
  * Return a pointer to the filename part of a path
  */
 static const char *
-_ftp_filename(const char *file, int *len, int *type)
+ftp_filename(const char *file, int *len, int *type)
 {
 	const char *s;
 
@@ -225,7 +231,7 @@ _ftp_filename(const char *file, int *len, int *type)
  * command.
  */
 static int
-_ftp_pwd(conn_t *conn, char *pwd, size_t pwdlen)
+ftp_pwd(conn_t *conn, char *pwd, size_t pwdlen)
 {
 	char *src, *dst, *end;
 	int q;
@@ -261,7 +267,7 @@ _ftp_pwd(conn_t *conn, char *pwd, size_t pwdlen)
  * file.
  */
 static int
-_ftp_cwd(conn_t *conn, const char *file)
+ftp_cwd(conn_t *conn, const char *file)
 {
 	const char *beg, *end;
 	char pwd[PATH_MAX];
@@ -270,9 +276,9 @@ _ftp_cwd(conn_t *conn, const char *file)
 	/* If no slashes in name, no need to change dirs. */
 	if ((end = strrchr(file, '/')) == NULL)
 		return (0);
-	if ((e = _ftp_cmd(conn, "PWD")) != FTP_WORKING_DIRECTORY ||
-	    (e = _ftp_pwd(conn, pwd, sizeof(pwd))) != FTP_OK) {
-		_ftp_seterr(e);
+	if ((e = ftp_cmd(conn, "PWD")) != FTP_WORKING_DIRECTORY ||
+	    (e = ftp_pwd(conn, pwd, sizeof(pwd))) != FTP_OK) {
+		ftp_seterr(e);
 		return (-1);
 	}
 	for (;;) {
@@ -289,10 +295,10 @@ _ftp_cwd(conn_t *conn, const char *file)
 		/* Keep going up a dir until we have a matching prefix. */
 		if (pwd[i] == '\0' && (file[i - 1] == '/' || file[i] == '/'))
 			break;
-		if ((e = _ftp_cmd(conn, "CDUP")) != FTP_FILE_ACTION_OK ||
-		    (e = _ftp_cmd(conn, "PWD")) != FTP_WORKING_DIRECTORY ||
-		    (e = _ftp_pwd(conn, pwd, sizeof(pwd))) != FTP_OK) {
-			_ftp_seterr(e);
+		if ((e = ftp_cmd(conn, "CDUP")) != FTP_FILE_ACTION_OK ||
+		    (e = ftp_cmd(conn, "PWD")) != FTP_WORKING_DIRECTORY ||
+		    (e = ftp_pwd(conn, pwd, sizeof(pwd))) != FTP_OK) {
+			ftp_seterr(e);
 			return (-1);
 		}
 	}
@@ -307,7 +313,7 @@ _ftp_cwd(conn_t *conn, const char *file)
 		return (0);
 
 	/* Change to the directory all in one chunk (e.g., foo/bar/baz). */
-	e = _ftp_cmd(conn, "CWD %.*s", (int)(end - beg), beg);
+	e = ftp_cmd(conn, "CWD %.*s", (int)(end - beg), beg);
 	if (e == FTP_FILE_ACTION_OK)
 		return (0);
 #endif /* FTP_COMBINE_CWDS */
@@ -318,9 +324,9 @@ _ftp_cwd(conn_t *conn, const char *file)
 			++beg, ++i;
 		for (++i; file + i < end && file[i] != '/'; ++i)
 			/* nothing */ ;
-		e = _ftp_cmd(conn, "CWD %.*s", file + i - beg, beg);
+		e = ftp_cmd(conn, "CWD %.*s", file + i - beg, beg);
 		if (e != FTP_FILE_ACTION_OK) {
-			_ftp_seterr(e);
+			ftp_seterr(e);
 			return (-1);
 		}
 	}
@@ -331,7 +337,7 @@ _ftp_cwd(conn_t *conn, const char *file)
  * Set transfer mode and data type
  */
 static int
-_ftp_mode_type(conn_t *conn, int mode, int type)
+ftp_mode_type(conn_t *conn, int mode, int type)
 {
 	int e;
 
@@ -344,7 +350,7 @@ _ftp_mode_type(conn_t *conn, int mode, int type)
 	default:
 		return (FTP_PROTOCOL_ERROR);
 	}
-	if ((e = _ftp_cmd(conn, "MODE %c", mode)) != FTP_OK) {
+	if ((e = ftp_cmd(conn, "MODE %c", mode)) != FTP_OK) {
 		if (mode == 'S') {
 			/*
 			 * Stream mode is supposed to be the default - so
@@ -380,7 +386,7 @@ _ftp_mode_type(conn_t *conn, int mode, int type)
 	default:
 		return (FTP_PROTOCOL_ERROR);
 	}
-	if ((e = _ftp_cmd(conn, "TYPE %c", type)) != FTP_OK)
+	if ((e = ftp_cmd(conn, "TYPE %c", type)) != FTP_OK)
 		return (e);
 
 	return (FTP_OK);
@@ -390,7 +396,7 @@ _ftp_mode_type(conn_t *conn, int mode, int type)
  * Request and parse file stats
  */
 static int
-_ftp_stat(conn_t *conn, const char *file, struct url_stat *us)
+ftp_stat(conn_t *conn, const char *file, struct url_stat *us)
 {
 	char *ln;
 	const char *filename;
@@ -402,24 +408,24 @@ _ftp_stat(conn_t *conn, const char *file, struct url_stat *us)
 	us->size = -1;
 	us->atime = us->mtime = 0;
 
-	filename = _ftp_filename(file, &filenamelen, &type);
+	filename = ftp_filename(file, &filenamelen, &type);
 
-	if ((e = _ftp_mode_type(conn, 0, type)) != FTP_OK) {
-		_ftp_seterr(e);
+	if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK) {
+		ftp_seterr(e);
 		return (-1);
 	}
 
-	e = _ftp_cmd(conn, "SIZE %.*s", filenamelen, filename);
+	e = ftp_cmd(conn, "SIZE %.*s", filenamelen, filename);
 	if (e != FTP_FILE_STATUS) {
-		_ftp_seterr(e);
+		ftp_seterr(e);
 		return (-1);
 	}
-	for (ln = conn->buf + 4; *ln && isspace(*ln); ln++)
+	for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++)
 		/* nothing */ ;
-	for (us->size = 0; *ln && isdigit(*ln); ln++)
+	for (us->size = 0; *ln && isdigit((unsigned char)*ln); ln++)
 		us->size = us->size * 10 + *ln - '0';
-	if (*ln && !isspace(*ln)) {
-		_ftp_seterr(FTP_PROTOCOL_ERROR);
+	if (*ln && !isspace((unsigned char)*ln)) {
+		ftp_seterr(FTP_PROTOCOL_ERROR);
 		us->size = -1;
 		return (-1);
 	}
@@ -427,12 +433,12 @@ _ftp_stat(conn_t *conn, const char *file, struct url_stat *us)
 		us->size = -1;
 	DEBUG(fprintf(stderr, "size: [%lld]\n", (long long)us->size));
 
-	e = _ftp_cmd(conn, "MDTM %.*s", filenamelen, filename);
+	e = ftp_cmd(conn, "MDTM %.*s", filenamelen, filename);
 	if (e != FTP_FILE_STATUS) {
-		_ftp_seterr(e);
+		ftp_seterr(e);
 		return (-1);
 	}
-	for (ln = conn->buf + 4; *ln && isspace(*ln); ln++)
+	for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++)
 		/* nothing */ ;
 	switch (strspn(ln, "0123456789")) {
 	case 14:
@@ -443,13 +449,13 @@ _ftp_stat(conn_t *conn, const char *file, struct url_stat *us)
 		ln[1] = '0';
 		break;
 	default:
-		_ftp_seterr(FTP_PROTOCOL_ERROR);
+		ftp_seterr(FTP_PROTOCOL_ERROR);
 		return (-1);
 	}
 	if (sscanf(ln, "%04d%02d%02d%02d%02d%02d",
 	    &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
 	    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
-		_ftp_seterr(FTP_PROTOCOL_ERROR);
+		ftp_seterr(FTP_PROTOCOL_ERROR);
 		return (-1);
 	}
 	tm.tm_mon--;
@@ -478,13 +484,13 @@ struct ftpio {
 	int	 err;		/* Error code */
 };
 
-static int	 _ftp_readfn(void *, char *, int);
-static int	 _ftp_writefn(void *, const char *, int);
-static fpos_t	 _ftp_seekfn(void *, fpos_t, int);
-static int	 _ftp_closefn(void *);
+static int	 ftp_readfn(void *, char *, int);
+static int	 ftp_writefn(void *, const char *, int);
+static fpos_t	 ftp_seekfn(void *, fpos_t, int);
+static int	 ftp_closefn(void *);
 
 static int
-_ftp_readfn(void *v, char *buf, int len)
+ftp_readfn(void *v, char *buf, int len)
 {
 	struct ftpio *io;
 	int r;
@@ -504,7 +510,7 @@ _ftp_readfn(void *v, char *buf, int len)
 	}
 	if (io->eof)
 		return (0);
-	r = _fetch_read(io->dconn, buf, len);
+	r = fetch_read(io->dconn, buf, len);
 	if (r > 0)
 		return (r);
 	if (r == 0) {
@@ -517,7 +523,7 @@ _ftp_readfn(void *v, char *buf, int len)
 }
 
 static int
-_ftp_writefn(void *v, const char *buf, int len)
+ftp_writefn(void *v, const char *buf, int len)
 {
 	struct ftpio *io;
 	int w;
@@ -535,7 +541,7 @@ _ftp_writefn(void *v, const char *buf, int len)
 		errno = io->err;
 		return (-1);
 	}
-	w = _fetch_write(io->dconn, buf, len);
+	w = fetch_write(io->dconn, buf, len);
 	if (w >= 0)
 		return (w);
 	if (errno != EINTR)
@@ -544,7 +550,7 @@ _ftp_writefn(void *v, const char *buf, int len)
 }
 
 static fpos_t
-_ftp_seekfn(void *v, fpos_t pos __unused, int whence __unused)
+ftp_seekfn(void *v, fpos_t pos __unused, int whence __unused)
 {
 	struct ftpio *io;
 
@@ -558,7 +564,7 @@ _ftp_seekfn(void *v, fpos_t pos __unused, int whence __unused)
 }
 
 static int
-_ftp_closefn(void *v)
+ftp_closefn(void *v)
 {
 	struct ftpio *io;
 	int r;
@@ -574,20 +580,20 @@ _ftp_closefn(void *v)
 		errno = EBADF;
 		return (-1);
 	}
-	_fetch_close(io->dconn);
+	fetch_close(io->dconn);
 	io->dir = -1;
 	io->dconn = NULL;
 	DEBUG(fprintf(stderr, "Waiting for final status\n"));
-	r = _ftp_chkerr(io->cconn);
+	r = ftp_chkerr(io->cconn);
 	if (io->cconn == cached_connection && io->cconn->ref == 1)
 		cached_connection = NULL;
-	_fetch_close(io->cconn);
+	fetch_close(io->cconn);
 	free(io);
 	return (r == FTP_TRANSFER_COMPLETE) ? 0 : -1;
 }
 
 static FILE *
-_ftp_setup(conn_t *cconn, conn_t *dconn, int mode)
+ftp_setup(conn_t *cconn, conn_t *dconn, int mode)
 {
 	struct ftpio *io;
 	FILE *f;
@@ -600,7 +606,7 @@ _ftp_setup(conn_t *cconn, conn_t *dconn, int mode)
 	io->dconn = dconn;
 	io->dir = mode;
 	io->eof = io->err = 0;
-	f = funopen(io, _ftp_readfn, _ftp_writefn, _ftp_seekfn, _ftp_closefn);
+	f = funopen(io, ftp_readfn, ftp_writefn, ftp_seekfn, ftp_closefn);
 	if (f == NULL)
 		free(io);
 	return (f);
@@ -610,7 +616,7 @@ _ftp_setup(conn_t *cconn, conn_t *dconn, int mode)
  * Transfer file
  */
 static FILE *
-_ftp_transfer(conn_t *conn, const char *oper, const char *file,
+ftp_transfer(conn_t *conn, const char *oper, const char *file,
     int mode, off_t offset, const char *flags)
 {
 	struct sockaddr_storage sa;
@@ -636,10 +642,10 @@ _ftp_transfer(conn_t *conn, const char *oper, const char *file,
 		    strncasecmp(s, "no", 2) != 0);
 
 	/* isolate filename */
-	filename = _ftp_filename(file, &filenamelen, &type);
+	filename = ftp_filename(file, &filenamelen, &type);
 
 	/* set transfer mode and data type */
-	if ((e = _ftp_mode_type(conn, 0, type)) != FTP_OK)
+	if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK)
 		goto ouch;
 
 	/* find our own address, bind, and listen */
@@ -651,7 +657,7 @@ _ftp_transfer(conn_t *conn, const char *oper, const char *file,
 
 	/* open data socket */
 	if ((sd = socket(sa.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
-		_fetch_syserr();
+		fetch_syserr();
 		return (NULL);
 	}
 
@@ -663,17 +669,17 @@ _ftp_transfer(conn_t *conn, const char *oper, const char *file,
 
 		/* send PASV command */
 		if (verbose)
-			_fetch_info("setting passive mode");
+			fetch_info("setting passive mode");
 		switch (sa.ss_family) {
 		case AF_INET:
-			if ((e = _ftp_cmd(conn, "PASV")) != FTP_PASSIVE_MODE)
+			if ((e = ftp_cmd(conn, "PASV")) != FTP_PASSIVE_MODE)
 				goto ouch;
 			break;
 		case AF_INET6:
-			if ((e = _ftp_cmd(conn, "EPSV")) != FTP_EPASSIVE_MODE) {
+			if ((e = ftp_cmd(conn, "EPSV")) != FTP_EPASSIVE_MODE) {
 				if (e == -1)
 					goto ouch;
-				if ((e = _ftp_cmd(conn, "LPSV")) !=
+				if ((e = ftp_cmd(conn, "LPSV")) !=
 				    FTP_LPASSIVE_MODE)
 					goto ouch;
 			}
@@ -691,7 +697,7 @@ _ftp_transfer(conn_t *conn, const char *oper, const char *file,
 		switch (e) {
 		case FTP_PASSIVE_MODE:
 		case FTP_LPASSIVE_MODE:
-			for (p = ln + 3; *p && !isdigit(*p); p++)
+			for (p = ln + 3; *p && !isdigit((unsigned char)*p); p++)
 				/* nothing */ ;
 			if (!*p) {
 				e = FTP_PROTOCOL_ERROR;
@@ -725,7 +731,7 @@ _ftp_transfer(conn_t *conn, const char *oper, const char *file,
 
 		/* seek to required offset */
 		if (offset)
-			if (_ftp_cmd(conn, "REST %lu", (u_long)offset) != FTP_FILE_OK)
+			if (ftp_cmd(conn, "REST %lu", (u_long)offset) != FTP_FILE_OK)
 				goto sysouch;
 
 		/* construct sockaddr for data socket */
@@ -740,8 +746,8 @@ _ftp_transfer(conn_t *conn, const char *oper, const char *file,
 			if (e == FTP_EPASSIVE_MODE)
 				sin6->sin6_port = htons(port);
 			else {
-				bcopy(addr + 2, (char *)&sin6->sin6_addr, 16);
-				bcopy(addr + 19, (char *)&sin6->sin6_port, 2);
+				memcpy(&sin6->sin6_addr, addr + 2, 16);
+				memcpy(&sin6->sin6_port, addr + 19, 2);
 			}
 			break;
 		case AF_INET:
@@ -749,8 +755,8 @@ _ftp_transfer(conn_t *conn, const char *oper, const char *file,
 			if (e == FTP_EPASSIVE_MODE)
 				sin4->sin_port = htons(port);
 			else {
-				bcopy(addr, (char *)&sin4->sin_addr, 4);
-				bcopy(addr + 4, (char *)&sin4->sin_port, 2);
+				memcpy(&sin4->sin_addr, addr, 4);
+				memcpy(&sin4->sin_port, addr + 4, 2);
 			}
 			break;
 		default:
@@ -760,18 +766,18 @@ _ftp_transfer(conn_t *conn, const char *oper, const char *file,
 
 		/* connect to data port */
 		if (verbose)
-			_fetch_info("opening data connection");
+			fetch_info("opening data connection");
 		bindaddr = getenv("FETCH_BIND_ADDRESS");
 		if (bindaddr != NULL && *bindaddr != '\0' &&
-		    _fetch_bind(sd, sa.ss_family, bindaddr) != 0)
+		    fetch_bind(sd, sa.ss_family, bindaddr) != 0)
 			goto sysouch;
 		if (connect(sd, (struct sockaddr *)&sa, sa.ss_len) == -1)
 			goto sysouch;
 
 		/* make the server initiate the transfer */
 		if (verbose)
-			_fetch_info("initiating transfer");
-		e = _ftp_cmd(conn, "%s %.*s", oper, filenamelen, filename);
+			fetch_info("initiating transfer");
+		e = ftp_cmd(conn, "%s %.*s", oper, filenamelen, filename);
 		if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION)
 			goto ouch;
 
@@ -801,7 +807,7 @@ _ftp_transfer(conn_t *conn, const char *oper, const char *file,
 			break;
 		}
 		if (verbose)
-			_fetch_info("binding data socket");
+			fetch_info("binding data socket");
 		if (bind(sd, (struct sockaddr *)&sa, sa.ss_len) == -1)
 			goto sysouch;
 		if (listen(sd, 1) == -1)
@@ -815,7 +821,7 @@ _ftp_transfer(conn_t *conn, const char *oper, const char *file,
 			sin4 = (struct sockaddr_in *)&sa;
 			a = ntohl(sin4->sin_addr.s_addr);
 			p = ntohs(sin4->sin_port);
-			e = _ftp_cmd(conn, "PORT %d,%d,%d,%d,%d,%d",
+			e = ftp_cmd(conn, "PORT %d,%d,%d,%d,%d,%d",
 			    (a >> 24) & 0xff, (a >> 16) & 0xff,
 			    (a >> 8) & 0xff, a & 0xff,
 			    (p >> 8) & 0xff, p & 0xff);
@@ -828,14 +834,14 @@ _ftp_transfer(conn_t *conn, const char *oper, const char *file,
 			if (getnameinfo((struct sockaddr *)&sa, sa.ss_len,
 				hname, sizeof(hname),
 				NULL, 0, NI_NUMERICHOST) == 0) {
-				e = _ftp_cmd(conn, "EPRT |%d|%s|%d|", 2, hname,
+				e = ftp_cmd(conn, "EPRT |%d|%s|%d|", 2, hname,
 				    htons(sin6->sin6_port));
 				if (e == -1)
 					goto ouch;
 			}
 			if (e != FTP_OK) {
 				ap = (char *)&sin6->sin6_addr;
-				e = _ftp_cmd(conn,
+				e = ftp_cmd(conn,
 				    "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
 				    6, 16,
 				    UC(ap[0]), UC(ap[1]), UC(ap[2]), UC(ap[3]),
@@ -856,13 +862,13 @@ _ftp_transfer(conn_t *conn, const char *oper, const char *file,
 
 		/* seek to required offset */
 		if (offset)
-			if (_ftp_cmd(conn, "REST %ju", (uintmax_t)offset) != FTP_FILE_OK)
+			if (ftp_cmd(conn, "REST %ju", (uintmax_t)offset) != FTP_FILE_OK)
 				goto sysouch;
 
 		/* make the server initiate the transfer */
 		if (verbose)
-			_fetch_info("initiating transfer");
-		e = _ftp_cmd(conn, "%s %.*s", oper, filenamelen, filename);
+			fetch_info("initiating transfer");
+		e = ftp_cmd(conn, "%s %.*s", oper, filenamelen, filename);
 		if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION)
 			goto ouch;
 
@@ -873,19 +879,19 @@ _ftp_transfer(conn_t *conn, const char *oper, const char *file,
 		sd = d;
 	}
 
-	if ((df = _ftp_setup(conn, _fetch_reopen(sd), mode)) == NULL)
+	if ((df = ftp_setup(conn, fetch_reopen(sd), mode)) == NULL)
 		goto sysouch;
 	return (df);
 
 sysouch:
-	_fetch_syserr();
+	fetch_syserr();
 	if (sd >= 0)
 		close(sd);
 	return (NULL);
 
 ouch:
 	if (e != -1)
-		_ftp_seterr(e);
+		ftp_seterr(e);
 	if (sd >= 0)
 		close(sd);
 	return (NULL);
@@ -895,7 +901,7 @@ ouch:
  * Authenticate
  */
 static int
-_ftp_authenticate(conn_t *conn, struct url *url, struct url *purl)
+ftp_authenticate(conn_t *conn, struct url *url, struct url *purl)
 {
 	const char *user, *pwd, *logname;
 	char pbuf[MAXHOSTNAMELEN + MAXLOGNAME + 1];
@@ -905,18 +911,18 @@ _ftp_authenticate(conn_t *conn, struct url *url, struct url *purl)
 
 	/* send user name and password */
 	if (url->user[0] == '\0')
-		_fetch_netrc_auth(url);
+		fetch_netrc_auth(url);
 	user = url->user;
 	if (*user == '\0')
 		user = getenv("FTP_LOGIN");
 	if (user == NULL || *user == '\0')
 		user = FTP_ANONYMOUS_USER;
-	if (purl && url->port == _fetch_default_port(url->scheme))
-		e = _ftp_cmd(conn, "USER %s@%s", user, url->host);
+	if (purl && url->port == fetch_default_port(url->scheme))
+		e = ftp_cmd(conn, "USER %s@%s", user, url->host);
 	else if (purl)
-		e = _ftp_cmd(conn, "USER %s@%s@%d", user, url->host, url->port);
+		e = ftp_cmd(conn, "USER %s@%s@%d", user, url->host, url->port);
 	else
-		e = _ftp_cmd(conn, "USER %s", user);
+		e = ftp_cmd(conn, "USER %s", user);
 
 	/* did the server request a password? */
 	if (e == FTP_NEED_PASSWORD) {
@@ -933,7 +939,7 @@ _ftp_authenticate(conn_t *conn, struct url *url, struct url *purl)
 			gethostname(pbuf + len, sizeof(pbuf) - len);
 			pwd = pbuf;
 		}
-		e = _ftp_cmd(conn, "PASS %s", pwd);
+		e = ftp_cmd(conn, "PASS %s", pwd);
 	}
 
 	return (e);
@@ -943,7 +949,7 @@ _ftp_authenticate(conn_t *conn, struct url *url, struct url *purl)
  * Log on to FTP server
  */
 static conn_t *
-_ftp_connect(struct url *url, struct url *purl, const char *flags)
+ftp_connect(struct url *url, struct url *purl, const char *flags)
 {
 	conn_t *conn;
 	int e, direct, verbose;
@@ -966,24 +972,24 @@ _ftp_connect(struct url *url, struct url *purl, const char *flags)
 	/* check for proxy */
 	if (purl) {
 		/* XXX proxy authentication! */
-		conn = _fetch_connect(purl->host, purl->port, af, verbose);
+		conn = fetch_connect(purl->host, purl->port, af, verbose);
 	} else {
 		/* no proxy, go straight to target */
-		conn = _fetch_connect(url->host, url->port, af, verbose);
+		conn = fetch_connect(url->host, url->port, af, verbose);
 		purl = NULL;
 	}
 
 	/* check connection */
 	if (conn == NULL)
-		/* _fetch_connect() has already set an error code */
+		/* fetch_connect() has already set an error code */
 		return (NULL);
 
 	/* expect welcome message */
-	if ((e = _ftp_chkerr(conn)) != FTP_SERVICE_READY)
+	if ((e = ftp_chkerr(conn)) != FTP_SERVICE_READY)
 		goto fouch;
 
 	/* authenticate */
-	if ((e = _ftp_authenticate(conn, url, purl)) != FTP_LOGGED_IN)
+	if ((e = ftp_authenticate(conn, url, purl)) != FTP_LOGGED_IN)
 		goto fouch;
 
 	/* TODO: Request extended features supported, if any (RFC 3659). */
@@ -993,8 +999,8 @@ _ftp_connect(struct url *url, struct url *purl, const char *flags)
 
 fouch:
 	if (e != -1)
-		_ftp_seterr(e);
-	_fetch_close(conn);
+		ftp_seterr(e);
+	fetch_close(conn);
 	return (NULL);
 }
 
@@ -1002,19 +1008,19 @@ fouch:
  * Disconnect from server
  */
 static void
-_ftp_disconnect(conn_t *conn)
+ftp_disconnect(conn_t *conn)
 {
-	(void)_ftp_cmd(conn, "QUIT");
+	(void)ftp_cmd(conn, "QUIT");
 	if (conn == cached_connection && conn->ref == 1)
 		cached_connection = NULL;
-	_fetch_close(conn);
+	fetch_close(conn);
 }
 
 /*
  * Check if we're already connected
  */
 static int
-_ftp_isconnected(struct url *url)
+ftp_isconnected(struct url *url)
 {
 	return (cached_connection
 	    && (strcmp(url->host, cached_host.host) == 0)
@@ -1027,28 +1033,28 @@ _ftp_isconnected(struct url *url)
  * Check the cache, reconnect if no luck
  */
 static conn_t *
-_ftp_cached_connect(struct url *url, struct url *purl, const char *flags)
+ftp_cached_connect(struct url *url, struct url *purl, const char *flags)
 {
 	conn_t *conn;
 	int e;
 
 	/* set default port */
 	if (!url->port)
-		url->port = _fetch_default_port(url->scheme);
+		url->port = fetch_default_port(url->scheme);
 
 	/* try to use previously cached connection */
-	if (_ftp_isconnected(url)) {
-		e = _ftp_cmd(cached_connection, "NOOP");
+	if (ftp_isconnected(url)) {
+		e = ftp_cmd(cached_connection, "NOOP");
 		if (e == FTP_OK || e == FTP_SYNTAX_ERROR)
-			return (_fetch_ref(cached_connection));
+			return (fetch_ref(cached_connection));
 	}
 
 	/* connect to server */
-	if ((conn = _ftp_connect(url, purl, flags)) == NULL)
+	if ((conn = ftp_connect(url, purl, flags)) == NULL)
 		return (NULL);
 	if (cached_connection)
-		_ftp_disconnect(cached_connection);
-	cached_connection = _fetch_ref(conn);
+		ftp_disconnect(cached_connection);
+	cached_connection = fetch_ref(conn);
 	memcpy(&cached_host, url, sizeof(*url));
 	return (conn);
 }
@@ -1057,13 +1063,15 @@ _ftp_cached_connect(struct url *url, struct url *purl, const char *flags)
  * Check the proxy settings
  */
 static struct url *
-_ftp_get_proxy(const char *flags)
+ftp_get_proxy(struct url * url, const char *flags)
 {
 	struct url *purl;
 	char *p;
 
 	if (flags != NULL && strchr(flags, 'd') != NULL)
 		return (NULL);
+	if (fetch_no_proxy_match(url->host))
+		return (NULL);
 	if (((p = getenv("FTP_PROXY")) || (p = getenv("ftp_proxy")) ||
 		(p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) &&
 	    *p && (purl = fetchParseURL(p)) != NULL) {
@@ -1074,7 +1082,7 @@ _ftp_get_proxy(const char *flags)
 				strcpy(purl->scheme, SCHEME_HTTP);
 		}
 		if (!purl->port)
-			purl->port = _fetch_default_proxy_port(purl->scheme);
+			purl->port = fetch_default_proxy_port(purl->scheme);
 		if (strcasecmp(purl->scheme, SCHEME_FTP) == 0 ||
 		    strcasecmp(purl->scheme, SCHEME_HTTP) == 0)
 			return (purl);
@@ -1087,7 +1095,7 @@ _ftp_get_proxy(const char *flags)
  * Process an FTP request
  */
 FILE *
-_ftp_request(struct url *url, const char *op, struct url_stat *us,
+ftp_request(struct url *url, const char *op, struct url_stat *us,
     struct url *purl, const char *flags)
 {
 	conn_t *conn;
@@ -1096,9 +1104,9 @@ _ftp_request(struct url *url, const char *op, struct url_stat *us,
 	/* check if we should use HTTP instead */
 	if (purl && strcasecmp(purl->scheme, SCHEME_HTTP) == 0) {
 		if (strcmp(op, "STAT") == 0)
-			return (_http_request(url, "HEAD", us, purl, flags));
+			return (http_request(url, "HEAD", us, purl, flags));
 		else if (strcmp(op, "RETR") == 0)
-			return (_http_request(url, "GET", us, purl, flags));
+			return (http_request(url, "GET", us, purl, flags));
 		/*
 		 * Our HTTP code doesn't support PUT requests yet, so try
 		 * a direct connection.
@@ -1106,18 +1114,18 @@ _ftp_request(struct url *url, const char *op, struct url_stat *us,
 	}
 
 	/* connect to server */
-	conn = _ftp_cached_connect(url, purl, flags);
+	conn = ftp_cached_connect(url, purl, flags);
 	if (purl)
 		fetchFreeURL(purl);
 	if (conn == NULL)
 		return (NULL);
 
 	/* change directory */
-	if (_ftp_cwd(conn, url->doc) == -1)
+	if (ftp_cwd(conn, url->doc) == -1)
 		return (NULL);
 
 	/* stat file */
-	if (us && _ftp_stat(conn, url->doc, us) == -1
+	if (us && ftp_stat(conn, url->doc, us) == -1
 	    && fetchLastErrCode != FETCH_PROTO
 	    && fetchLastErrCode != FETCH_UNAVAIL)
 		return (NULL);
@@ -1131,7 +1139,7 @@ _ftp_request(struct url *url, const char *op, struct url_stat *us,
 		oflag = O_RDONLY;
 
 	/* initiate the transfer */
-	return (_ftp_transfer(conn, op, url->doc, oflag, url->offset, flags));
+	return (ftp_transfer(conn, op, url->doc, oflag, url->offset, flags));
 }
 
 /*
@@ -1140,7 +1148,7 @@ _ftp_request(struct url *url, const char *op, struct url_stat *us,
 FILE *
 fetchXGetFTP(struct url *url, struct url_stat *us, const char *flags)
 {
-	return (_ftp_request(url, "RETR", us, _ftp_get_proxy(flags), flags));
+	return (ftp_request(url, "RETR", us, ftp_get_proxy(url, flags), flags));
 }
 
 /*
@@ -1158,9 +1166,8 @@ fetchGetFTP(struct url *url, const char *flags)
 FILE *
 fetchPutFTP(struct url *url, const char *flags)
 {
-
-	return (_ftp_request(url, CHECK_FLAG('a') ? "APPE" : "STOR", NULL,
-	    _ftp_get_proxy(flags), flags));
+	return (ftp_request(url, CHECK_FLAG('a') ? "APPE" : "STOR", NULL,
+	    ftp_get_proxy(url, flags), flags));
 }
 
 /*
@@ -1171,9 +1178,14 @@ fetchStatFTP(struct url *url, struct url_stat *us, const char *flags)
 {
 	FILE *f;
 
-	f = _ftp_request(url, "STAT", us, _ftp_get_proxy(flags), flags);
+	f = ftp_request(url, "STAT", us, ftp_get_proxy(url, flags), flags);
 	if (f == NULL)
 		return (-1);
+	/*
+	 * When op is "STAT", ftp_request() will return either NULL or
+	 * (FILE *)1, never a valid FILE *, so we mustn't fclose(f) before
+	 * returning, as it would cause a segfault.
+	 */
 	return (0);
 }
 
diff --git a/lib/libfetch/http.c b/lib/libfetch/http.c
index f9ce043..86b3e23 100644
--- a/lib/libfetch/http.c
+++ b/lib/libfetch/http.c
@@ -25,7 +25,7 @@
  * (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: src/lib/libfetch/http.c,v 1.78 2007/05/08 19:28:03 des Exp $
+ * $FreeBSD: src/lib/libfetch/http.c,v 1.86 2008/12/15 08:27:44 murray Exp $
  * $DragonFly: src/lib/libfetch/http.c,v 1.4 2007/08/05 21:48:12 swildner Exp $
  */
 
@@ -63,6 +63,7 @@
 
 #include <sys/param.h>
 #include <sys/socket.h>
+#include <sys/time.h>
 
 #include <ctype.h>
 #include <err.h>
@@ -92,6 +93,7 @@
 #define HTTP_MOVED_PERM		301
 #define HTTP_MOVED_TEMP		302
 #define HTTP_SEE_OTHER		303
+#define HTTP_NOT_MODIFIED	304
 #define HTTP_TEMP_REDIRECT	307
 #define HTTP_NEED_AUTH		401
 #define HTTP_NEED_PROXY_AUTH	407
@@ -130,27 +132,27 @@ struct httpio
  * Get next chunk header
  */
 static int
-_http_new_chunk(struct httpio *io)
+http_new_chunk(struct httpio *io)
 {
 	char *p;
 
-	if (_fetch_getln(io->conn) == -1)
+	if (fetch_getln(io->conn) == -1)
 		return (-1);
 
-	if (io->conn->buflen < 2 || !isxdigit(*io->conn->buf))
+	if (io->conn->buflen < 2 || !isxdigit((unsigned char)*io->conn->buf))
 		return (-1);
 
-	for (p = io->conn->buf; *p && !isspace(*p); ++p) {
+	for (p = io->conn->buf; *p && !isspace((unsigned char)*p); ++p) {
 		if (*p == ';')
 			break;
-		if (!isxdigit(*p))
+		if (!isxdigit((unsigned char)*p))
 			return (-1);
-		if (isdigit(*p)) {
+		if (isdigit((unsigned char)*p)) {
 			io->chunksize = io->chunksize * 16 +
 			    *p - '0';
 		} else {
 			io->chunksize = io->chunksize * 16 +
-			    10 + tolower(*p) - 'a';
+			    10 + tolower((unsigned char)*p) - 'a';
 		}
 	}
 
@@ -173,7 +175,7 @@ _http_new_chunk(struct httpio *io)
  * Grow the input buffer to at least len bytes
  */
 static inline int
-_http_growbuf(struct httpio *io, size_t len)
+http_growbuf(struct httpio *io, size_t len)
 {
 	char *tmp;
 
@@ -191,7 +193,7 @@ _http_growbuf(struct httpio *io, size_t len)
  * Fill the input buffer, do chunk decoding on the fly
  */
 static int
-_http_fillbuf(struct httpio *io, size_t len)
+http_fillbuf(struct httpio *io, size_t len)
 {
 	if (io->error)
 		return (-1);
@@ -199,9 +201,9 @@ _http_fillbuf(struct httpio *io, size_t len)
 		return (0);
 
 	if (io->chunked == 0) {
-		if (_http_growbuf(io, len) == -1)
+		if (http_growbuf(io, len) == -1)
 			return (-1);
-		if ((io->buflen = _fetch_read(io->conn, io->buf, len)) == -1) {
+		if ((io->buflen = fetch_read(io->conn, io->buf, len)) == -1) {
 			io->error = 1;
 			return (-1);
 		}
@@ -210,7 +212,7 @@ _http_fillbuf(struct httpio *io, size_t len)
 	}
 
 	if (io->chunksize == 0) {
-		switch (_http_new_chunk(io)) {
+		switch (http_new_chunk(io)) {
 		case -1:
 			io->error = 1;
 			return (-1);
@@ -222,9 +224,9 @@ _http_fillbuf(struct httpio *io, size_t len)
 
 	if (len > io->chunksize)
 		len = io->chunksize;
-	if (_http_growbuf(io, len) == -1)
+	if (http_growbuf(io, len) == -1)
 		return (-1);
-	if ((io->buflen = _fetch_read(io->conn, io->buf, len)) == -1) {
+	if ((io->buflen = fetch_read(io->conn, io->buf, len)) == -1) {
 		io->error = 1;
 		return (-1);
 	}
@@ -233,7 +235,7 @@ _http_fillbuf(struct httpio *io, size_t len)
 	if (io->chunksize == 0) {
 		char endl[2];
 
-		if (_fetch_read(io->conn, endl, 2) != 2 ||
+		if (fetch_read(io->conn, endl, 2) != 2 ||
 		    endl[0] != '\r' || endl[1] != '\n')
 			return (-1);
 	}
@@ -247,7 +249,7 @@ _http_fillbuf(struct httpio *io, size_t len)
  * Read function
  */
 static int
-_http_readfn(void *v, char *buf, int len)
+http_readfn(void *v, char *buf, int len)
 {
 	struct httpio *io = (struct httpio *)v;
 	int l, pos;
@@ -260,12 +262,12 @@ _http_readfn(void *v, char *buf, int len)
 	for (pos = 0; len > 0; pos += l, len -= l) {
 		/* empty buffer */
 		if (!io->buf || io->bufpos == io->buflen)
-			if (_http_fillbuf(io, len) < 1)
+			if (http_fillbuf(io, len) < 1)
 				break;
 		l = io->buflen - io->bufpos;
 		if (len < l)
 			l = len;
-		bcopy(io->buf + io->bufpos, buf + pos, l);
+		memcpy(buf + pos, io->buf + io->bufpos, l);
 		io->bufpos += l;
 	}
 
@@ -278,23 +280,23 @@ _http_readfn(void *v, char *buf, int len)
  * Write function
  */
 static int
-_http_writefn(void *v, const char *buf, int len)
+http_writefn(void *v, const char *buf, int len)
 {
 	struct httpio *io = (struct httpio *)v;
 
-	return (_fetch_write(io->conn, buf, len));
+	return (fetch_write(io->conn, buf, len));
 }
 
 /*
  * Close function
  */
 static int
-_http_closefn(void *v)
+http_closefn(void *v)
 {
 	struct httpio *io = (struct httpio *)v;
 	int r;
 
-	r = _fetch_close(io->conn);
+	r = fetch_close(io->conn);
 	if (io->buf)
 		free(io->buf);
 	free(io);
@@ -305,20 +307,20 @@ _http_closefn(void *v)
  * Wrap a file descriptor up
  */
 static FILE *
-_http_funopen(conn_t *conn, int chunked)
+http_funopen(conn_t *conn, int chunked)
 {
 	struct httpio *io;
 	FILE *f;
 
 	if ((io = calloc(1, sizeof(*io))) == NULL) {
-		_fetch_syserr();
+		fetch_syserr();
 		return (NULL);
 	}
 	io->conn = conn;
 	io->chunked = chunked;
-	f = funopen(io, _http_readfn, _http_writefn, NULL, _http_closefn);
+	f = funopen(io, http_readfn, http_writefn, NULL, http_closefn);
 	if (f == NULL) {
-		_fetch_syserr();
+		fetch_syserr();
 		free(io);
 		return (NULL);
 	}
@@ -362,7 +364,7 @@ static struct {
  * Send a formatted line; optionally echo to terminal
  */
 static int
-_http_cmd(conn_t *conn, const char *fmt, ...)
+http_cmd(conn_t *conn, const char *fmt, ...)
 {
 	va_list ap;
 	size_t len;
@@ -375,15 +377,15 @@ _http_cmd(conn_t *conn, const char *fmt, ...)
 
 	if (msg == NULL) {
 		errno = ENOMEM;
-		_fetch_syserr();
+		fetch_syserr();
 		return (-1);
 	}
 
-	r = _fetch_putln(conn, msg, len);
+	r = fetch_putln(conn, msg, len);
 	free(msg);
 
 	if (r == -1) {
-		_fetch_syserr();
+		fetch_syserr();
 		return (-1);
 	}
 
@@ -394,11 +396,11 @@ _http_cmd(conn_t *conn, const char *fmt, ...)
  * Get and parse status line
  */
 static int
-_http_get_reply(conn_t *conn)
+http_get_reply(conn_t *conn)
 {
 	char *p;
 
-	if (_fetch_getln(conn) == -1)
+	if (fetch_getln(conn) == -1)
 		return (-1);
 	/*
 	 * A valid status line looks like "HTTP/m.n xyz reason" where m
@@ -417,7 +419,10 @@ _http_get_reply(conn_t *conn)
 			return (HTTP_PROTOCOL_ERROR);
 		p += 4;
 	}
-	if (*p != ' ' || !isdigit(p[1]) || !isdigit(p[2]) || !isdigit(p[3]))
+	if (*p != ' ' ||
+	    !isdigit((unsigned char)p[1]) ||
+	    !isdigit((unsigned char)p[2]) ||
+	    !isdigit((unsigned char)p[3]))
 		return (HTTP_PROTOCOL_ERROR);
 
 	conn->err = (p[1] - '0') * 100 + (p[2] - '0') * 10 + (p[3] - '0');
@@ -429,13 +434,14 @@ _http_get_reply(conn_t *conn)
  * to the beginning of the value.
  */
 static const char *
-_http_match(const char *str, const char *hdr)
+http_match(const char *str, const char *hdr)
 {
-	while (*str && *hdr && tolower(*str++) == tolower(*hdr++))
+	while (*str && *hdr &&
+	    tolower((unsigned char)*str++) == tolower((unsigned char)*hdr++))
 		/* nothing */;
 	if (*str || *hdr != ':')
 		return (NULL);
-	while (*hdr && isspace(*++hdr))
+	while (*hdr && isspace((unsigned char)*++hdr))
 		/* nothing */;
 	return (hdr);
 }
@@ -444,13 +450,13 @@ _http_match(const char *str, const char *hdr)
  * Get the next header and return the appropriate symbolic code.
  */
 static hdr_t
-_http_next_header(conn_t *conn, const char **p)
+http_next_header(conn_t *conn, const char **p)
 {
 	int i;
 
-	if (_fetch_getln(conn) == -1)
+	if (fetch_getln(conn) == -1)
 		return (hdr_syserror);
-	while (conn->buflen && isspace(conn->buf[conn->buflen - 1]))
+	while (conn->buflen && isspace((unsigned char)conn->buf[conn->buflen - 1]))
 		conn->buflen--;
 	conn->buf[conn->buflen] = '\0';
 	if (conn->buflen == 0)
@@ -462,7 +468,7 @@ _http_next_header(conn_t *conn, const char **p)
 	 * characters except "()<>@,;:\\\"{}".
 	 */
 	for (i = 0; hdr_names[i].num != hdr_unknown; i++)
-		if ((*p = _http_match(hdr_names[i].name, conn->buf)) != NULL)
+		if ((*p = http_match(hdr_names[i].name, conn->buf)) != NULL)
 			return (hdr_names[i].num);
 	return (hdr_unknown);
 }
@@ -471,7 +477,7 @@ _http_next_header(conn_t *conn, const char **p)
  * Parse a last-modified header
  */
 static int
-_http_parse_mtime(const char *p, time_t *mtime)
+http_parse_mtime(const char *p, time_t *mtime)
 {
 	char locale[64], *r;
 	struct tm tm;
@@ -495,11 +501,11 @@ _http_parse_mtime(const char *p, time_t *mtime)
  * Parse a content-length header
  */
 static int
-_http_parse_length(const char *p, off_t *length)
+http_parse_length(const char *p, off_t *length)
 {
 	off_t len;
 
-	for (len = 0; *p && isdigit(*p); ++p)
+	for (len = 0; *p && isdigit((unsigned char)*p); ++p)
 		len = len * 10 + (*p - '0');
 	if (*p)
 		return (-1);
@@ -513,7 +519,7 @@ _http_parse_length(const char *p, off_t *length)
  * Parse a content-range header
  */
 static int
-_http_parse_range(const char *p, off_t *offset, off_t *length, off_t *size)
+http_parse_range(const char *p, off_t *offset, off_t *length, off_t *size)
 {
 	off_t first, last, len;
 
@@ -524,16 +530,16 @@ _http_parse_range(const char *p, off_t *offset, off_t *length, off_t *size)
 		first = last = -1;
 		++p;
 	} else {
-		for (first = 0; *p && isdigit(*p); ++p)
+		for (first = 0; *p && isdigit((unsigned char)*p); ++p)
 			first = first * 10 + *p - '0';
 		if (*p != '-')
 			return (-1);
-		for (last = 0, ++p; *p && isdigit(*p); ++p)
+		for (last = 0, ++p; *p && isdigit((unsigned char)*p); ++p)
 			last = last * 10 + *p - '0';
 	}
 	if (first > last || *p != '/')
 		return (-1);
-	for (len = 0, ++p; *p && isdigit(*p); ++p)
+	for (len = 0, ++p; *p && isdigit((unsigned char)*p); ++p)
 		len = len * 10 + *p - '0';
 	if (*p || len < last - first + 1)
 		return (-1);
@@ -560,7 +566,7 @@ _http_parse_range(const char *p, off_t *offset, off_t *length, off_t *size)
  * Base64 encoding
  */
 static char *
-_http_base64(const char *src)
+http_base64(const char *src)
 {
 	static const char base64[] =
 	    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
@@ -616,7 +622,7 @@ _http_base64(const char *src)
  * Encode username and password
  */
 static int
-_http_basic_auth(conn_t *conn, const char *hdr, const char *usr, const char *pwd)
+http_basic_auth(conn_t *conn, const char *hdr, const char *usr, const char *pwd)
 {
 	char *upw, *auth;
 	int r;
@@ -625,11 +631,11 @@ _http_basic_auth(conn_t *conn, const char *hdr, const char *usr, const char *pwd
 	DEBUG(fprintf(stderr, "pwd: [%s]\n", pwd));
 	if (asprintf(&upw, "%s:%s", usr, pwd) == -1)
 		return (-1);
-	auth = _http_base64(upw);
+	auth = http_base64(upw);
 	free(upw);
 	if (auth == NULL)
 		return (-1);
-	r = _http_cmd(conn, "%s: Basic %s", hdr, auth);
+	r = http_cmd(conn, "%s: Basic %s", hdr, auth);
 	free(auth);
 	return (r);
 }
@@ -638,7 +644,7 @@ _http_basic_auth(conn_t *conn, const char *hdr, const char *usr, const char *pwd
  * Send an authorization header
  */
 static int
-_http_authorize(conn_t *conn, const char *hdr, const char *p)
+http_authorize(conn_t *conn, const char *hdr, const char *p)
 {
 	/* basic authorization */
 	if (strncasecmp(p, "basic:", 6) == 0) {
@@ -655,7 +661,7 @@ _http_authorize(conn_t *conn, const char *hdr, const char *p)
 		user = str;
 		pwd = strchr(str, ':');
 		*pwd++ = '\0';
-		r = _http_basic_auth(conn, hdr, user, pwd);
+		r = http_basic_auth(conn, hdr, user, pwd);
 		free(str);
 		return (r);
 	}
@@ -671,7 +677,7 @@ _http_authorize(conn_t *conn, const char *hdr, const char *p)
  * Connect to the correct HTTP server or proxy.
  */
 static conn_t *
-_http_connect(struct url *URL, struct url *purl, const char *flags)
+http_connect(struct url *URL, struct url *purl, const char *flags)
 {
 	conn_t *conn;
 	int verbose;
@@ -699,15 +705,15 @@ _http_connect(struct url *URL, struct url *purl, const char *flags)
 		return (NULL);
 	}
 
-	if ((conn = _fetch_connect(URL->host, URL->port, af, verbose)) == NULL)
-		/* _fetch_connect() has already set an error code */
+	if ((conn = fetch_connect(URL->host, URL->port, af, verbose)) == NULL)
+		/* fetch_connect() has already set an error code */
 		return (NULL);
 	if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0 &&
-	    _fetch_ssl(conn, verbose) == -1) {
-		_fetch_close(conn);
+	    fetch_ssl(conn, verbose) == -1) {
+		fetch_close(conn);
 		/* grrr */
 		errno = EAUTH;
-		_fetch_syserr();
+		fetch_syserr();
 		return (NULL);
 	}
 
@@ -718,19 +724,21 @@ _http_connect(struct url *URL, struct url *purl, const char *flags)
 }
 
 static struct url *
-_http_get_proxy(const char *flags)
+http_get_proxy(struct url * url, const char *flags)
 {
 	struct url *purl;
 	char *p;
 
 	if (flags != NULL && strchr(flags, 'd') != NULL)
 		return (NULL);
+	if (fetch_no_proxy_match(url->host))
+		return (NULL);
 	if (((p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) &&
 	    *p && (purl = fetchParseURL(p))) {
 		if (!*purl->scheme)
 			strcpy(purl->scheme, SCHEME_HTTP);
 		if (!purl->port)
-			purl->port = _fetch_default_proxy_port(purl->scheme);
+			purl->port = fetch_default_proxy_port(purl->scheme);
 		if (strcasecmp(purl->scheme, SCHEME_HTTP) == 0)
 			return (purl);
 		fetchFreeURL(purl);
@@ -739,7 +747,7 @@ _http_get_proxy(const char *flags)
 }
 
 static void
-_http_print_html(FILE *out, FILE *in)
+http_print_html(FILE *out, FILE *in)
 {
 	size_t len;
 	char *line, *p, *q;
@@ -747,7 +755,7 @@ _http_print_html(FILE *out, FILE *in)
 
 	comment = tag = 0;
 	while ((line = fgetln(in, &len)) != NULL) {
-		while (len && isspace(line[len - 1]))
+		while (len && isspace((unsigned char)line[len - 1]))
 			--len;
 		for (p = q = line; q < line + len; ++q) {
 			if (comment && *q == '-') {
@@ -788,23 +796,26 @@ _http_print_html(FILE *out, FILE *in)
  * XXX off into a separate function.
  */
 FILE *
-_http_request(struct url *URL, const char *op, struct url_stat *us,
+http_request(struct url *URL, const char *op, struct url_stat *us,
     struct url *purl, const char *flags)
 {
+	char timebuf[80];
+	char hbuf[MAXHOSTNAMELEN + 7], *host;
 	conn_t *conn;
 	struct url *url, *new;
-	int chunked, direct, need_auth, noredirect, verbose;
+	int chunked, direct, ims, need_auth, noredirect, verbose;
 	int e, i, n, val;
 	off_t offset, clength, length, size;
 	time_t mtime;
 	const char *p;
 	FILE *f;
 	hdr_t h;
-	char hbuf[MAXHOSTNAMELEN + 7], *host;
+	struct tm *timestruct;
 
 	direct = CHECK_FLAG('d');
 	noredirect = CHECK_FLAG('A');
 	verbose = CHECK_FLAG('v');
+	ims = CHECK_FLAG('i');
 
 	if (direct && purl) {
 		fetchFreeURL(purl);
@@ -831,18 +842,18 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
 
 		/* check port */
 		if (!url->port)
-			url->port = _fetch_default_port(url->scheme);
+			url->port = fetch_default_port(url->scheme);
 
 		/* were we redirected to an FTP URL? */
 		if (purl == NULL && strcmp(url->scheme, SCHEME_FTP) == 0) {
 			if (strcmp(op, "GET") == 0)
-				return (_ftp_request(url, "RETR", us, purl, flags));
+				return (ftp_request(url, "RETR", us, purl, flags));
 			else if (strcmp(op, "HEAD") == 0)
-				return (_ftp_request(url, "STAT", us, purl, flags));
+				return (ftp_request(url, "STAT", us, purl, flags));
 		}
 
 		/* connect to server or proxy */
-		if ((conn = _http_connect(url, purl, flags)) == NULL)
+		if ((conn = http_connect(url, purl, flags)) == NULL)
 			goto ouch;
 
 		host = url->host;
@@ -852,7 +863,7 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
 			host = hbuf;
 		}
 #endif
-		if (url->port != _fetch_default_port(url->scheme)) {
+		if (url->port != fetch_default_port(url->scheme)) {
 			if (host != hbuf) {
 				strcpy(hbuf, host);
 				host = hbuf;
@@ -863,38 +874,46 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
 
 		/* send request */
 		if (verbose)
-			_fetch_info("requesting %s://%s%s",
+			fetch_info("requesting %s://%s%s",
 			    url->scheme, host, url->doc);
 		if (purl) {
-			_http_cmd(conn, "%s %s://%s%s HTTP/1.1",
+			http_cmd(conn, "%s %s://%s%s HTTP/1.1",
 			    op, url->scheme, host, url->doc);
 		} else {
-			_http_cmd(conn, "%s %s HTTP/1.1",
+			http_cmd(conn, "%s %s HTTP/1.1",
 			    op, url->doc);
 		}
 
+		if (ims && url->ims_time) {
+			timestruct = gmtime((time_t *)&url->ims_time);
+			(void)strftime(timebuf, 80, "%a, %d %b %Y %T GMT",
+			    timestruct);
+			if (verbose)
+				fetch_info("If-Modified-Since: %s", timebuf);
+			http_cmd(conn, "If-Modified-Since: %s", timebuf);
+		}
 		/* virtual host */
-		_http_cmd(conn, "Host: %s", host);
+		http_cmd(conn, "Host: %s", host);
 
 		/* proxy authorization */
 		if (purl) {
 			if (*purl->user || *purl->pwd)
-				_http_basic_auth(conn, "Proxy-Authorization",
+				http_basic_auth(conn, "Proxy-Authorization",
 				    purl->user, purl->pwd);
 			else if ((p = getenv("HTTP_PROXY_AUTH")) != NULL && *p != '\0')
-				_http_authorize(conn, "Proxy-Authorization", p);
+				http_authorize(conn, "Proxy-Authorization", p);
 		}
 
 		/* server authorization */
 		if (need_auth || *url->user || *url->pwd) {
 			if (*url->user || *url->pwd)
-				_http_basic_auth(conn, "Authorization", url->user, url->pwd);
+				http_basic_auth(conn, "Authorization", url->user, url->pwd);
 			else if ((p = getenv("HTTP_AUTH")) != NULL && *p != '\0')
-				_http_authorize(conn, "Authorization", p);
+				http_authorize(conn, "Authorization", p);
 			else if (fetchAuthMethod && fetchAuthMethod(url) == 0) {
-				_http_basic_auth(conn, "Authorization", url->user, url->pwd);
+				http_basic_auth(conn, "Authorization", url->user, url->pwd);
 			} else {
-				_http_seterr(HTTP_NEED_AUTH);
+				http_seterr(HTTP_NEED_AUTH);
 				goto ouch;
 			}
 		}
@@ -902,19 +921,19 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
 		/* other headers */
 		if ((p = getenv("HTTP_REFERER")) != NULL && *p != '\0') {
 			if (strcasecmp(p, "auto") == 0)
-				_http_cmd(conn, "Referer: %s://%s%s",
+				http_cmd(conn, "Referer: %s://%s%s",
 				    url->scheme, host, url->doc);
 			else
-				_http_cmd(conn, "Referer: %s", p);
+				http_cmd(conn, "Referer: %s", p);
 		}
 		if ((p = getenv("HTTP_USER_AGENT")) != NULL && *p != '\0')
-			_http_cmd(conn, "User-Agent: %s", p);
+			http_cmd(conn, "User-Agent: %s", p);
 		else
-			_http_cmd(conn, "User-Agent: %s " _LIBFETCH_VER, getprogname());
+			http_cmd(conn, "User-Agent: %s " _LIBFETCH_VER, getprogname());
 		if (url->offset > 0)
-			_http_cmd(conn, "Range: bytes=%lld-", (long long)url->offset);
-		_http_cmd(conn, "Connection: close");
-		_http_cmd(conn, "");
+			http_cmd(conn, "Range: bytes=%lld-", (long long)url->offset);
+		http_cmd(conn, "Connection: close");
+		http_cmd(conn, "");
 
 		/*
 		 * Force the queued request to be dispatched.  Normally, one
@@ -931,9 +950,10 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
 			   sizeof(val));
 
 		/* get reply */
-		switch (_http_get_reply(conn)) {
+		switch (http_get_reply(conn)) {
 		case HTTP_OK:
 		case HTTP_PARTIAL:
+		case HTTP_NOT_MODIFIED:
 			/* fine */
 			break;
 		case HTTP_MOVED_PERM:
@@ -950,12 +970,12 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
 				 * We already sent out authorization code,
 				 * so there's nothing more we can do.
 				 */
-				_http_seterr(conn->err);
+				http_seterr(conn->err);
 				goto ouch;
 			}
 			/* try again, but send the password this time */
 			if (verbose)
-				_fetch_info("server requires authorization");
+				fetch_info("server requires authorization");
 			break;
 		case HTTP_NEED_PROXY_AUTH:
 			/*
@@ -963,7 +983,7 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
 			 * our proxy authorization code, so there's
 			 * nothing more we can do.
 			 */
-			_http_seterr(conn->err);
+			http_seterr(conn->err);
 			goto ouch;
 		case HTTP_BAD_RANGE:
 			/*
@@ -975,10 +995,10 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
 		case HTTP_PROTOCOL_ERROR:
 			/* fall through */
 		case -1:
-			_fetch_syserr();
+			fetch_syserr();
 			goto ouch;
 		default:
-			_http_seterr(conn->err);
+			http_seterr(conn->err);
 			if (!verbose)
 				goto ouch;
 			/* fall through so we can get the full error message */
@@ -986,21 +1006,21 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
 
 		/* get headers */
 		do {
-			switch ((h = _http_next_header(conn, &p))) {
+			switch ((h = http_next_header(conn, &p))) {
 			case hdr_syserror:
-				_fetch_syserr();
+				fetch_syserr();
 				goto ouch;
 			case hdr_error:
-				_http_seterr(HTTP_PROTOCOL_ERROR);
+				http_seterr(HTTP_PROTOCOL_ERROR);
 				goto ouch;
 			case hdr_content_length:
-				_http_parse_length(p, &clength);
+				http_parse_length(p, &clength);
 				break;
 			case hdr_content_range:
-				_http_parse_range(p, &offset, &length, &size);
+				http_parse_range(p, &offset, &length, &size);
 				break;
 			case hdr_last_modified:
-				_http_parse_mtime(p, &mtime);
+				http_parse_mtime(p, &mtime);
 				break;
 			case hdr_location:
 				if (!HTTP_REDIRECT(conn->err))
@@ -1008,7 +1028,7 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
 				if (new)
 					free(new);
 				if (verbose)
-					_fetch_info("%d redirect to %s", conn->err, p);
+					fetch_info("%d redirect to %s", conn->err, p);
 				if (*p == '/')
 					/* absolute path */
 					new = fetchMakeURL(url->scheme, url->host, url->port, p,
@@ -1048,7 +1068,7 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
 		if (conn->err == HTTP_NEED_AUTH) {
 			e = conn->err;
 			need_auth = 1;
-			_fetch_close(conn);
+			fetch_close(conn);
 			conn = NULL;
 			continue;
 		}
@@ -1058,22 +1078,26 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
 			if (url->offset == size && url->length == 0) {
 				/* asked for 0 bytes; fake it */
 				offset = url->offset;
+				clength = -1;
 				conn->err = HTTP_OK;
 				break;
 			} else {
-				_http_seterr(conn->err);
+				http_seterr(conn->err);
 				goto ouch;
 			}
 		}
 
 		/* we have a hit or an error */
-		if (conn->err == HTTP_OK || conn->err == HTTP_PARTIAL || HTTP_ERROR(conn->err))
+		if (conn->err == HTTP_OK
+		    || conn->err == HTTP_NOT_MODIFIED
+		    || conn->err == HTTP_PARTIAL
+		    || HTTP_ERROR(conn->err))
 			break;
 
 		/* all other cases: we got a redirect */
 		e = conn->err;
 		need_auth = 0;
-		_fetch_close(conn);
+		fetch_close(conn);
 		conn = NULL;
 		if (!new) {
 			DEBUG(fprintf(stderr, "redirect with no new location\n"));
@@ -1086,7 +1110,7 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
 
 	/* we failed, or ran out of retries */
 	if (conn == NULL) {
-		_http_seterr(e);
+		http_seterr(e);
 		goto ouch;
 	}
 
@@ -1095,9 +1119,14 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
 		  (long long)offset, (long long)length,
 		  (long long)size, (long long)clength));
 
+	if (conn->err == HTTP_NOT_MODIFIED) {
+		http_seterr(HTTP_NOT_MODIFIED);
+		return (NULL);
+	}
+
 	/* check for inconsistencies */
 	if (clength != -1 && length != -1 && clength != length) {
-		_http_seterr(HTTP_PROTOCOL_ERROR);
+		http_seterr(HTTP_PROTOCOL_ERROR);
 		goto ouch;
 	}
 	if (clength == -1)
@@ -1105,7 +1134,7 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
 	if (clength != -1)
 		length = offset + clength;
 	if (length != -1 && size != -1 && length != size) {
-		_http_seterr(HTTP_PROTOCOL_ERROR);
+		http_seterr(HTTP_PROTOCOL_ERROR);
 		goto ouch;
 	}
 	if (size == -1)
@@ -1119,7 +1148,7 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
 
 	/* too far? */
 	if (URL->offset > 0 && offset > URL->offset) {
-		_http_seterr(HTTP_PROTOCOL_ERROR);
+		http_seterr(HTTP_PROTOCOL_ERROR);
 		goto ouch;
 	}
 
@@ -1128,8 +1157,8 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
 	URL->length = clength;
 
 	/* wrap it up in a FILE */
-	if ((f = _http_funopen(conn, chunked)) == NULL) {
-		_fetch_syserr();
+	if ((f = http_funopen(conn, chunked)) == NULL) {
+		fetch_syserr();
 		goto ouch;
 	}
 
@@ -1139,7 +1168,7 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
 		fetchFreeURL(purl);
 
 	if (HTTP_ERROR(conn->err)) {
-		_http_print_html(stderr, f);
+		http_print_html(stderr, f);
 		fclose(f);
 		f = NULL;
 	}
@@ -1152,7 +1181,7 @@ ouch:
 	if (purl)
 		fetchFreeURL(purl);
 	if (conn != NULL)
-		_fetch_close(conn);
+		fetch_close(conn);
 	return (NULL);
 }
 
@@ -1167,7 +1196,7 @@ ouch:
 FILE *
 fetchXGetHTTP(struct url *URL, struct url_stat *us, const char *flags)
 {
-	return (_http_request(URL, "GET", us, _http_get_proxy(flags), flags));
+	return (http_request(URL, "GET", us, http_get_proxy(URL, flags), flags));
 }
 
 /*
@@ -1197,7 +1226,7 @@ fetchStatHTTP(struct url *URL, struct url_stat *us, const char *flags)
 {
 	FILE *f;
 
-	f = _http_request(URL, "HEAD", us, _http_get_proxy(flags), flags);
+	f = http_request(URL, "HEAD", us, http_get_proxy(URL, flags), flags);
 	if (f == NULL)
 		return (-1);
 	fclose(f);
diff --git a/usr.bin/fetch/fetch.1 b/usr.bin/fetch/fetch.1
index 68d4a17..e8da991 100644
--- a/usr.bin/fetch/fetch.1
+++ b/usr.bin/fetch/fetch.1
@@ -27,10 +27,10 @@
 .\" (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: src/usr.bin/fetch/fetch.1,v 1.67 2006/04/22 03:04:24 jkoshy Exp $
+.\"      $FreeBSD: src/usr.bin/fetch/fetch.1,v 1.70 2008/12/15 08:27:44 murray Exp $
 .\"      $DragonFly: src/usr.bin/fetch/fetch.1,v 1.4 2007/08/05 21:48:12 swildner Exp $
 .\"
-.Dd August 5, 2007
+.Dd May 10, 2009
 .Dt FETCH 1
 .Os
 .Sh NAME
@@ -38,17 +38,25 @@
 .Nd retrieve a file by Uniform Resource Locator
 .Sh SYNOPSIS
 .Nm
-.Op Fl 146AFMPRUadlmnpqrsv
+.Op Fl 146AadFlMmnPpqRrsUv
 .Op Fl B Ar bytes
+.Op Fl i Ar file
+.Op Fl N Ar file
+.Op Fl o Ar file
 .Op Fl S Ar bytes
 .Op Fl T Ar seconds
+.Op Fl w Ar seconds
+.Ar URL ...
+.Nm
+.Op Fl 146AadFlMmnPpqRrsUv
+.Op Fl B Ar bytes
+.Op Fl i Ar file
 .Op Fl N Ar file
 .Op Fl o Ar file
+.Op Fl S Ar bytes
+.Op Fl T Ar seconds
 .Op Fl w Ar seconds
-.Op Fl h Ar host
-.Op Fl c Ar dir
-.Op Fl f Ar file
-.Op Ar URL ...
+.Fl h Ar host Fl f Ar file Oo Fl c Ar dir Oc
 .Sh DESCRIPTION
 The
 .Nm
@@ -60,7 +68,7 @@ command line.
 .Pp
 The following options are available:
 .Bl -tag -width Fl
-.It Fl \&1
+.It Fl 1
 Stop and return exit code 0 at the first successfully retrieved file.
 .It Fl 4
 Forces
@@ -111,6 +119,12 @@ The file to retrieve is located on the host
 .Ar host .
 This option is deprecated and is provided for backward compatibility
 only.
+.It Fl i Ar file
+If-Modified-Since mode: the remote file will only be retrieved if it
+is newer than
+.Ar file
+on the local host.
+(HTTP only)
 .It Fl l
 If the target is a file-scheme URL, make a symbolic link to the target
 rather than trying to copy it.
@@ -213,19 +227,29 @@ message.
 .Sh ENVIRONMENT
 .Bl -tag -width HTTP_TIMEOUT
 .It Ev FTP_TIMEOUT
-maximum time, in seconds, to wait before aborting an FTP connection.
+Maximum time, in seconds, to wait before aborting an FTP connection.
 .It Ev HTTP_TIMEOUT
-maximum time, in seconds, to wait before aborting an HTTP connection.
+Maximum time, in seconds, to wait before aborting an HTTP connection.
 .El
 .Pp
-All environment variables mentioned in the documentation for the
-.Xr fetch 3
-library are supported.
-A number of these are quite important to the proper operation of
-.Nm ;
-you are strongly encouraged to read
+See
 .Xr fetch 3
-as well.
+for a description of additional environment variables, including
+.Ev FETCH_BIND_ADDRESS ,
+.Ev FTP_LOGIN ,
+.Ev FTP_PASSIVE_MODE ,
+.Ev FTP_PASSWORD ,
+.Ev FTP_PROXY ,
+.Ev ftp_proxy ,
+.Ev HTTP_AUTH ,
+.Ev HTTP_PROXY ,
+.Ev http_proxy ,
+.Ev HTTP_PROXY_AUTH ,
+.Ev HTTP_REFERER ,
+.Ev HTTP_USER_AGENT ,
+.Ev NETRC ,
+.Ev NO_PROXY and
+.Ev no_proxy .
 .Sh EXIT STATUS
 The
 .Nm
@@ -234,6 +258,12 @@ If multiple URLs are listed on the command line,
 .Nm
 will attempt to retrieve each one of them in turn, and will return
 zero only if they were all successfully retrieved.
+.Pp
+If the
+.Fl i
+argument is used and the remote file is not newer than the
+specified file then the command will still return success,
+although no file is transferred.
 .Sh SEE ALSO
 .Xr fetch 3
 .Sh HISTORY
diff --git a/usr.bin/fetch/fetch.c b/usr.bin/fetch/fetch.c
index 817219f..a3f32b0 100644
--- a/usr.bin/fetch/fetch.c
+++ b/usr.bin/fetch/fetch.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2000-2004 Dag-Erling Co�dan Sm�rgrav
+ * Copyright (c) 2000-2004 Dag-Erling Coïdan Smørgrav
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -25,7 +25,7 @@
  * (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: src/usr.bin/fetch/fetch.c,v 1.78 2006/11/10 22:05:41 des Exp $
+ * $FreeBSD: src/usr.bin/fetch/fetch.c,v 1.84 2009/01/17 13:34:56 des Exp $
  * $DragonFly: src/usr.bin/fetch/fetch.c,v 1.8 2007/08/05 21:48:12 swildner Exp $
  */
 
@@ -42,13 +42,13 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sysexits.h>
 #include <termios.h>
 #include <unistd.h>
 
 #include <fetch.h>
 
 #define MINBUFSIZE	4096
+#define TIMEOUT		120
 
 /* Option flags */
 int	 A_flag;	/*    -A: do not follow 302 redirects */
@@ -60,6 +60,8 @@ int	 d_flag;	/*    -d: direct connection */
 int	 F_flag;	/*    -F: restart without checking mtime  */
 char	*f_filename;	/*    -f: file to fetch */
 char	*h_hostname;	/*    -h: host to fetch from */
+int	 i_flag;	/*    -i: specify input file for mtime comparison */
+char	*i_filename;	/*        name of input file */
 int	 l_flag;	/*    -l: link rather than copy file: URLs */
 int	 m_flag;	/* -[Mm]: mirror mode */
 char	*N_filename;	/*    -N: netrc file name */
@@ -74,7 +76,7 @@ int	 R_flag;	/*    -R: don't delete partially transferred files */
 int	 r_flag;	/*    -r: restart previously interrupted transfer */
 off_t	 S_size;        /*    -S: require size to match */
 int	 s_flag;        /*    -s: show size, don't fetch */
-long	 T_secs = 120;	/*    -T: transfer timeout in seconds */
+long	 T_secs;	/*    -T: transfer timeout in seconds */
 int	 t_flag;	/*!   -t: workaround TCP bug */
 int	 U_flag;	/*    -U: do not use high ports */
 int	 v_level = 1;	/*    -v: verbosity level */
@@ -87,8 +89,8 @@ int	 sigalrm;	/* SIGALRM received */
 int	 siginfo;	/* SIGINFO received */
 int	 sigint;	/* SIGINT received */
 
-long	 ftp_timeout;	/* default timeout for FTP transfers */
-long	 http_timeout;	/* default timeout for HTTP transfers */
+long	 ftp_timeout = TIMEOUT;		/* default timeout for FTP transfers */
+long	 http_timeout = TIMEOUT;	/* default timeout for HTTP transfers */
 char	*buf;		/* transfer buffer */
 
 
@@ -364,7 +366,7 @@ fetch(char *URL, const char *path)
 	}
 
 	/* FTP specific flags */
-	if (strcmp(url->scheme, "ftp") == 0) {
+	if (strcmp(url->scheme, SCHEME_FTP) == 0) {
 		if (p_flag)
 			strcat(flags, "p");
 		if (d_flag)
@@ -375,12 +377,21 @@ fetch(char *URL, const char *path)
 	}
 
 	/* HTTP specific flags */
-	if (strcmp(url->scheme, "http") == 0) {
+	if (strcmp(url->scheme, SCHEME_HTTP) == 0 ||
+	    strcmp(url->scheme, SCHEME_HTTPS) == 0) {
 		if (d_flag)
 			strcat(flags, "d");
 		if (A_flag)
 			strcat(flags, "A");
 		timeout = T_secs ? T_secs : http_timeout;
+		if (i_flag) {
+			if (stat(i_filename, &sb)) {
+				warn("%s: stat()", i_filename);
+				goto failure;
+			}
+			url->ims_time = sb.st_mtime;
+			strcat(flags, "i");
+		}
 	}
 
 	/* set the protocol timeout. */
@@ -448,7 +459,14 @@ fetch(char *URL, const char *path)
 		goto signal;
 	if (f == NULL) {
 		warnx("%s: %s", URL, fetchLastErrString);
-		goto failure;
+		if (i_flag && strcmp(url->scheme, SCHEME_HTTP) == 0
+		    && fetchLastErrCode == FETCH_OK
+		    && strcmp(fetchLastErrString, "Not Modified") == 0) {
+			/* HTTP Not Modified Response, return OK. */
+			r = 0;
+			goto done;
+		} else
+			goto failure;
 	}
 	if (sigint)
 		goto signal;
@@ -714,10 +732,11 @@ fetch(char *URL, const char *path)
 static void
 usage(void)
 {
-	fprintf(stderr, "%s\n%s\n%s\n",
-	    "usage: fetch [-146AFMPRUadlmnpqrsv] [-N netrc] [-o outputfile]",
-	    "             [-S bytes] [-B bytes] [-T seconds] [-w seconds]",
-	    "             [-h host -f file [-c dir] | URL ...]");
+	fprintf(stderr, "%s\n%s\n%s\n%s\n",
+"usage: fetch [-146AadFlMmnPpqRrsUv] [-B bytes] [-N file] [-o file] [-S bytes]",
+"       [-T seconds] [-w seconds] [-i file] URL ...",
+"       fetch [-146AadFlMmnPpqRrsUv] [-B bytes] [-N file] [-o file] [-S bytes]",
+"       [-T seconds] [-w seconds] [-i file] -h host -f file [-c dir]");
 }
 
 
@@ -734,7 +753,7 @@ main(int argc, char *argv[])
 	int c, e, r;
 
 	while ((c = getopt(argc, argv,
-	    "146AaB:bc:dFf:Hh:lMmN:nPpo:qRrS:sT:tUvw:")) != -1)
+	    "146AaB:bc:dFf:Hh:i:lMmN:nPpo:qRrS:sT:tUvw:")) != -1)
 		switch (c) {
 		case '1':
 			once_flag = 1;
@@ -779,6 +798,10 @@ main(int argc, char *argv[])
 		case 'h':
 			h_hostname = optarg;
 			break;
+		case 'i':
+			i_flag = 1;
+			i_filename = optarg;
+			break;
 		case 'l':
 			l_flag = 1;
 			break;
@@ -846,7 +869,7 @@ main(int argc, char *argv[])
 			break;
 		default:
 			usage();
-			exit(EX_USAGE);
+			exit(1);
 		}
 
 	argc -= optind;
@@ -855,7 +878,7 @@ main(int argc, char *argv[])
 	if (h_hostname || f_filename || c_dirname) {
 		if (!h_hostname || !f_filename || argc) {
 			usage();
-			exit(EX_USAGE);
+			exit(1);
 		}
 		/* XXX this is a hack. */
 		if (strcspn(h_hostname, "@:/") != strlen(h_hostname))
@@ -868,7 +891,7 @@ main(int argc, char *argv[])
 
 	if (!argc) {
 		usage();
-		exit(EX_USAGE);
+		exit(1);
 	}
 
 	/* allocate buffer */
@@ -909,10 +932,10 @@ main(int argc, char *argv[])
 		} else if (stat(o_filename, &sb) == -1) {
 			if (errno == ENOENT) {
 				if (argc > 1)
-					errx(EX_USAGE, "%s is not a directory",
+					errx(1, "%s is not a directory",
 					    o_filename);
 			} else {
-				err(EX_IOERR, "%s", o_filename);
+				err(1, "%s", o_filename);
 			}
 		} else {
 			if (sb.st_mode & S_IFDIR)
