Module Name:    src
Committed By:   martin
Date:           Mon Sep 12 17:08:13 UTC 2022

Modified Files:
        src/usr.bin/ftp [netbsd-8]: Makefile cmds.c complete.c domacro.c
            extern.h fetch.c ftp.1 ftp.c ftp_var.h main.c progressbar.c
            progressbar.h ssl.c ssl.h util.c version.h

Log Message:
Catch up to current, requested by christos in ticket #1763:

        usr.bin/ftp/Makefile                            up to 1.39
        usr.bin/ftp/cmds.c                              up to 1.141
        usr.bin/ftp/complete.c                          up to 1.47
        usr.bin/ftp/domacro.c                           up to 1.23
        usr.bin/ftp/extern.h                            up to 1.82
        usr.bin/ftp/fetch.c                             up to 1.235
        usr.bin/ftp/ftp.1                               up to 1.147
        usr.bin/ftp/ftp.c                               up to 1.174
        usr.bin/ftp/ftp_var.h                           up to 1.86
        usr.bin/ftp/main.c                              up to 1.128
        usr.bin/ftp/progressbar.c                       up to 1.24
        usr.bin/ftp/progressbar.h                       up to 1.9
        usr.bin/ftp/ssl.c                               up to 1.12
        usr.bin/ftp/ssl.h                               up to 1.5
        usr.bin/ftp/util.c                              up to 1.164
        usr.bin/ftp/version.h                           up to 1.94

ftp(1): validate address from PASV and LPSV response.
ftp(1): use raw write(2) instead of fwrite(3) to avoid stream
corruption because of the progress bar interrupts.
Fixes for PR 56219 and PR 55857.
PR 57003: Support relative redirects.


To generate a diff of this commit:
cvs rdiff -u -r1.37.2.2 -r1.37.2.3 src/usr.bin/ftp/Makefile
cvs rdiff -u -r1.137.8.2 -r1.137.8.3 src/usr.bin/ftp/cmds.c
cvs rdiff -u -r1.46.38.2 -r1.46.38.3 src/usr.bin/ftp/complete.c
cvs rdiff -u -r1.22.38.2 -r1.22.38.3 src/usr.bin/ftp/domacro.c
cvs rdiff -u -r1.80.24.2 -r1.80.24.3 src/usr.bin/ftp/extern.h
cvs rdiff -u -r1.228.4.2 -r1.228.4.3 src/usr.bin/ftp/fetch.c
cvs rdiff -u -r1.135.8.2 -r1.135.8.3 src/usr.bin/ftp/ftp.1
cvs rdiff -u -r1.167.6.2 -r1.167.6.3 src/usr.bin/ftp/ftp.c
cvs rdiff -u -r1.84.8.2 -r1.84.8.3 src/usr.bin/ftp/ftp_var.h
cvs rdiff -u -r1.123.8.2 -r1.123.8.3 src/usr.bin/ftp/main.c
cvs rdiff -u -r1.22.24.2 -r1.22.24.3 src/usr.bin/ftp/progressbar.c
cvs rdiff -u -r1.8.38.2 -r1.8.38.3 src/usr.bin/ftp/progressbar.h
cvs rdiff -u -r1.5.8.2 -r1.5.8.3 src/usr.bin/ftp/ssl.c
cvs rdiff -u -r1.3.8.2 -r1.3.8.3 src/usr.bin/ftp/ssl.h
cvs rdiff -u -r1.158.22.2 -r1.158.22.3 src/usr.bin/ftp/util.c
cvs rdiff -u -r1.87.8.2 -r1.87.8.3 src/usr.bin/ftp/version.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/usr.bin/ftp/Makefile
diff -u src/usr.bin/ftp/Makefile:1.37.2.2 src/usr.bin/ftp/Makefile:1.37.2.3
--- src/usr.bin/ftp/Makefile:1.37.2.2	Mon Sep 12 15:05:21 2022
+++ src/usr.bin/ftp/Makefile	Mon Sep 12 17:08:13 2022
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile,v 1.37.2.2 2022/09/12 15:05:21 martin Exp $
+#	$NetBSD: Makefile,v 1.37.2.3 2022/09/12 17:08:13 martin Exp $
 #	from: @(#)Makefile	8.2 (Berkeley) 4/3/94
 
 .include <bsd.own.mk>
@@ -8,6 +8,7 @@ USE_FORT?= yes	# network client
 PROG=	ftp
 SRCS=	cmds.c cmdtab.c complete.c domacro.c fetch.c ftp.c main.c \
 	progressbar.c ruserpass.c util.c
+SRCS+=	ssl.c
 
 # Uncomment the following to provide defaults for gate-ftp operation
 #
@@ -19,7 +20,6 @@ CPPFLAGS+=-DNO_EDITCOMPLETE -DNO_ABOUT -
 LDADD+=	-ledit -lterminfo
 DPADD+=	${LIBEDIT} ${LIBTERMINFO}
 CPPFLAGS+= -DWITH_SSL
-SRCS+=ssl.c
 LDADD+= -lssl -lcrypto
 DPADD+= ${LIBSSL} ${LIBCRYPTO}
 .endif
@@ -31,4 +31,6 @@ CPPFLAGS+= -DINET6
 cmds.o fetch.o: version.h
 main.o:	ftp_var.h
 
+CWARNFLAGS.gcc+=	${GCC_NO_FORMAT_OVERFLOW}
+
 .include <bsd.prog.mk>

Index: src/usr.bin/ftp/cmds.c
diff -u src/usr.bin/ftp/cmds.c:1.137.8.2 src/usr.bin/ftp/cmds.c:1.137.8.3
--- src/usr.bin/ftp/cmds.c:1.137.8.2	Mon Sep 12 15:05:21 2022
+++ src/usr.bin/ftp/cmds.c	Mon Sep 12 17:08:13 2022
@@ -1,7 +1,7 @@
-/*	$NetBSD: cmds.c,v 1.137.8.2 2022/09/12 15:05:21 martin Exp $	*/
+/*	$NetBSD: cmds.c,v 1.137.8.3 2022/09/12 17:08:13 martin Exp $	*/
 
 /*-
- * Copyright (c) 1996-2009 The NetBSD Foundation, Inc.
+ * Copyright (c) 1996-2021 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This code is derived from software contributed to The NetBSD Foundation
@@ -96,7 +96,7 @@
 #if 0
 static char sccsid[] = "@(#)cmds.c	8.6 (Berkeley) 10/9/94";
 #else
-__RCSID("$NetBSD: cmds.c,v 1.137.8.2 2022/09/12 15:05:21 martin Exp $");
+__RCSID("$NetBSD: cmds.c,v 1.137.8.3 2022/09/12 17:08:13 martin Exp $");
 #endif
 #endif /* not lint */
 
@@ -1131,7 +1131,7 @@ setdebug(int argc, char *argv[])
 		options |= SO_DEBUG;
 	else
 		options &= ~SO_DEBUG;
-	fprintf(ttyout, "Debugging %s (ftp_debug=%d).\n", onoff(ftp_debug), ftp_debug);
+	fprintf(ttyout, "Debugging %s (debug=%d).\n", onoff(ftp_debug), ftp_debug);
 	code = ftp_debug > 0;
 }
 
@@ -1158,7 +1158,8 @@ cd(int argc, char *argv[])
 	}
 	if (r == COMPLETE) {
 		dirchange = 1;
-		updateremotecwd();
+		remotecwd[0] = '\0';
+		remcwdvalid = 0;
 	}
 }
 
@@ -1544,9 +1545,9 @@ pwd(int argc, char *argv[])
 		UPRINTF("usage: %s\n", argv[0]);
 		return;
 	}
-	if (! remotecwd[0])
+	if (!remcwdvalid || remotecwd[0] == '\0')
 		updateremotecwd();
-	if (! remotecwd[0])
+	if (remotecwd[0] == '\0')
 		fprintf(ttyout, "Unable to determine remote directory\n");
 	else {
 		fprintf(ttyout, "Remote directory: %s\n", remotecwd);
@@ -1775,6 +1776,18 @@ quit(int argc, char *argv[])
 	exit(0);
 }
 
+void __dead
+justquit(void)
+{
+
+	quit(0, NULL);
+	/*
+	 * quit is not __dead, but for our invocation it never will return,
+	 * but some compilers are not smart enough to find this out.
+	 */
+	exit(0);
+}
+
 /*
  * Terminate session, but don't exit.
  * May be called with 0, NULL.
@@ -2184,7 +2197,7 @@ LOOP:
 					}
 					break;
 				}
-				/* intentional drop through */
+				/* FALLTHROUGH */
 			default:
 				*cp2++ = *cp1;
 				break;
@@ -2359,7 +2372,8 @@ cdup(int argc, char *argv[])
 	}
 	if (r == COMPLETE) {
 		dirchange = 1;
-		updateremotecwd();
+		remotecwd[0] = '\0';
+		remcwdvalid = 0;
 	}
 }
 

Index: src/usr.bin/ftp/complete.c
diff -u src/usr.bin/ftp/complete.c:1.46.38.2 src/usr.bin/ftp/complete.c:1.46.38.3
--- src/usr.bin/ftp/complete.c:1.46.38.2	Mon Sep 12 15:05:21 2022
+++ src/usr.bin/ftp/complete.c	Mon Sep 12 17:08:13 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: complete.c,v 1.46.38.2 2022/09/12 15:05:21 martin Exp $	*/
+/*	$NetBSD: complete.c,v 1.46.38.3 2022/09/12 17:08:13 martin Exp $	*/
 
 /*-
  * Copyright (c) 1997-2009 The NetBSD Foundation, Inc.
@@ -31,7 +31,7 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID("$NetBSD: complete.c,v 1.46.38.2 2022/09/12 15:05:21 martin Exp $");
+__RCSID("$NetBSD: complete.c,v 1.46.38.3 2022/09/12 17:08:13 martin Exp $");
 #endif /* not lint */
 
 /*
@@ -99,11 +99,10 @@ complete_ambiguous(char *word, int list,
 	}
 
 	if (!list) {
-		matchlen = 0;
 		lastmatch = words->sl_str[0];
 		matchlen = strlen(lastmatch);
 		for (i = 1 ; i < words->sl_cur ; i++) {
-			for (j = wordlen ; j < strlen(words->sl_str[i]); j++)
+			for (j = wordlen; j < strlen(words->sl_str[i]); j++)
 				if (lastmatch[j] != words->sl_str[i][j])
 					break;
 			if (j < matchlen)

Index: src/usr.bin/ftp/domacro.c
diff -u src/usr.bin/ftp/domacro.c:1.22.38.2 src/usr.bin/ftp/domacro.c:1.22.38.3
--- src/usr.bin/ftp/domacro.c:1.22.38.2	Mon Sep 12 15:05:21 2022
+++ src/usr.bin/ftp/domacro.c	Mon Sep 12 17:08:13 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: domacro.c,v 1.22.38.2 2022/09/12 15:05:21 martin Exp $	*/
+/*	$NetBSD: domacro.c,v 1.22.38.3 2022/09/12 17:08:13 martin Exp $	*/
 
 /*
  * Copyright (c) 1985, 1993, 1994
@@ -34,7 +34,7 @@
 #if 0
 static char sccsid[] = "@(#)domacro.c	8.3 (Berkeley) 4/2/94";
 #else
-__RCSID("$NetBSD: domacro.c,v 1.22.38.2 2022/09/12 15:05:21 martin Exp $");
+__RCSID("$NetBSD: domacro.c,v 1.22.38.3 2022/09/12 17:08:13 martin Exp $");
 #endif
 #endif /* not lint */
 
@@ -102,7 +102,7 @@ domacro(int argc, char *argv[])
 					}
 					break;
 				}
-				/* intentional drop through */
+				/* FALLTHROUGH */
 			default:
 				*cp2++ = *cp1;
 				break;

Index: src/usr.bin/ftp/extern.h
diff -u src/usr.bin/ftp/extern.h:1.80.24.2 src/usr.bin/ftp/extern.h:1.80.24.3
--- src/usr.bin/ftp/extern.h:1.80.24.2	Mon Sep 12 15:05:21 2022
+++ src/usr.bin/ftp/extern.h	Mon Sep 12 17:08:13 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: extern.h,v 1.80.24.2 2022/09/12 15:05:21 martin Exp $	*/
+/*	$NetBSD: extern.h,v 1.80.24.3 2022/09/12 17:08:13 martin Exp $	*/
 
 /*-
  * Copyright (c) 1996-2009 The NetBSD Foundation, Inc.
@@ -173,6 +173,7 @@ void	pswitch(int);
 void	put(int, char **);
 void	pwd(int, char **);
 void	quit(int, char **);
+void	justquit(void) __dead;
 void	quote(int, char **);
 void	quote1(const char *, int, char **);
 void	recvrequest(const char *, const char *, const char *,
@@ -242,7 +243,14 @@ void	user(int, char **);
 int	ftp_connect(int, const struct sockaddr *, socklen_t, int);
 int	ftp_listen(int, int);
 int	ftp_poll(struct pollfd *, int, int);
+#ifndef SMALL
 void   *ftp_malloc(size_t);
 StringList *ftp_sl_init(void);
 void	ftp_sl_add(StringList *, char *);
 char   *ftp_strdup(const char *);
+#else
+#define	ftp_malloc(a)	malloc(a);
+#define ftp_sl_init()	sl_init()
+#define ftp_sl_add(a, b)	sl_add((a), (b))
+#define ftp_strdup(a)	strdup(a)
+#endif

Index: src/usr.bin/ftp/fetch.c
diff -u src/usr.bin/ftp/fetch.c:1.228.4.2 src/usr.bin/ftp/fetch.c:1.228.4.3
--- src/usr.bin/ftp/fetch.c:1.228.4.2	Mon Sep 12 15:05:21 2022
+++ src/usr.bin/ftp/fetch.c	Mon Sep 12 17:08:13 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: fetch.c,v 1.228.4.2 2022/09/12 15:05:21 martin Exp $	*/
+/*	$NetBSD: fetch.c,v 1.228.4.3 2022/09/12 17:08:13 martin Exp $	*/
 
 /*-
  * Copyright (c) 1997-2015 The NetBSD Foundation, Inc.
@@ -37,7 +37,7 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID("$NetBSD: fetch.c,v 1.228.4.2 2022/09/12 15:05:21 martin Exp $");
+__RCSID("$NetBSD: fetch.c,v 1.228.4.3 2022/09/12 17:08:13 martin Exp $");
 #endif /* not lint */
 
 /*
@@ -106,12 +106,13 @@ __dead static void	timeouthttp(int);
 static int	auth_url(const char *, char **, const struct authinfo *);
 static void	base64_encode(const unsigned char *, size_t, unsigned char *);
 #endif
-static int	go_fetch(const char *);
+static int	go_fetch(const char *, struct urlinfo *);
 static int	fetch_ftp(const char *);
-static int	fetch_url(const char *, const char *, char *, char *);
+static int	fetch_url(const char *, const char *, char *, char *,
+    struct urlinfo *);
 static const char *match_token(const char **, const char *);
 static int	parse_url(const char *, const char *, struct urlinfo *,
-    struct authinfo *);
+    struct authinfo *, struct urlinfo *);
 static void	url_decode(char *);
 static void	freeauthinfo(struct authinfo *);
 static void	freeurlinfo(struct urlinfo *);
@@ -138,6 +139,43 @@ static int	redirect_loop;
 	((urltype) == HTTP_URL_T)
 #endif
 
+/**
+ * fwrite(3) replacement that just uses write(2). Many stdio implementations
+ * don't handle interrupts properly and corrupt the output. We are taking
+ * alarm interrupts because of the progress bar.
+ *
+ * Assumes `fp' is pristine with no prior I/O calls on it.
+ */
+static size_t
+maxwrite(const void *buf, size_t size, size_t nmemb, FILE *fp)
+{
+	const char *p = buf;
+	ssize_t nwr = 0;
+	ssize_t n;
+	int fd = fileno(fp);
+
+	size *= nmemb;	/* assume no overflow */
+
+	while (size > 0) {
+		if ((n = write(fd, p, size)) == -1) {
+			switch (errno) {
+			case EINTR:
+			case EAGAIN:
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+			case EWOULDBLOCK:
+#endif
+				continue;
+			default:
+				return nwr;
+			}
+		}
+		p += n;
+		nwr += n;
+		size -= n;
+	}
+	return nwr;
+}
+
 /*
  * Determine if token is the next word in buf (case insensitive).
  * If so, advance buf past the token and any trailing LWS, and
@@ -237,7 +275,7 @@ auth_url(const char *challenge, char **r
 	scheme = "Basic";	/* only support Basic authentication */
 	gotpass = NULL;
 
-	DPRINTF("auth_url: challenge `%s'\n", challenge);
+	DPRINTF("%s: challenge `%s'\n", __func__, challenge);
 
 	if (! match_token(&cp, scheme)) {
 		warnx("Unsupported authentication challenge `%s'",
@@ -299,7 +337,7 @@ auth_url(const char *challenge, char **r
 	*response = ftp_malloc(rlen);
 	(void)strlcpy(*response, scheme, rlen);
 	len = strlcat(*response, " ", rlen);
-			/* use  `clen - 1'  to not encode the trailing NUL */
+			/* use	`clen - 1'  to not encode the trailing NUL */
 	base64_encode((unsigned char *)clear, clen - 1,
 	    (unsigned char *)*response + len);
 	memset(clear, 0, clen);
@@ -330,7 +368,7 @@ base64_encode(const unsigned char *clear
 			    | ((clear[i + 1] >> 4) & 0x0f)];
 		*(cp++) = enc[((clear[i + 1] << 2) & 0x3c)
 			    | ((clear[i + 2] >> 6) & 0x03)];
-		*(cp++) = enc[((clear[i + 2]     ) & 0x3f)];
+		*(cp++) = enc[((clear[i + 2]	 ) & 0x3f)];
 	}
 	*cp = '\0';
 	while (i-- > len)
@@ -363,6 +401,42 @@ url_decode(char *url)
 	*q = '\0';
 }
 
+static const char *
+get_port(const struct urlinfo *ui)
+{
+
+	switch(ui->utype) {
+	case HTTP_URL_T:
+		return httpport;
+	case FTP_URL_T:
+		return ftpport;
+	case FILE_URL_T:
+		return "";
+#ifdef WITH_SSL
+	case HTTPS_URL_T:
+		return httpsport;
+#endif
+	default:
+		return NULL;
+	}
+}
+
+static int
+use_relative(const struct urlinfo *ui)
+{
+	if (ui == NULL)
+		return 0;
+	switch (ui->utype) {
+	case HTTP_URL_T:
+	case FILE_URL_T:
+#ifdef WITH_SSL
+	case HTTPS_URL_T:
+#endif
+		return 1;
+	default:
+		return 0;
+	}
+}
 
 /*
  * Parse URL of form (per RFC 3986):
@@ -398,7 +472,7 @@ url_decode(char *url)
 
 static int
 parse_url(const char *url, const char *desc, struct urlinfo *ui,
-    struct authinfo *auth) 
+    struct authinfo *auth, struct urlinfo *rui)
 {
 	const char	*origurl, *tport;
 	char		*cp, *ep, *thost;
@@ -409,29 +483,26 @@ parse_url(const char *url, const char *d
 	DPRINTF("parse_url: %s `%s'\n", desc, url);
 
 	origurl = url;
-	tport = NULL;
 
 	if (STRNEQUAL(url, HTTP_URL)) {
 		url += sizeof(HTTP_URL) - 1;
 		ui->utype = HTTP_URL_T;
 		ui->portnum = HTTP_PORT;
-		tport = httpport;
 	} else if (STRNEQUAL(url, FTP_URL)) {
 		url += sizeof(FTP_URL) - 1;
 		ui->utype = FTP_URL_T;
 		ui->portnum = FTP_PORT;
-		tport = ftpport;
 	} else if (STRNEQUAL(url, FILE_URL)) {
 		url += sizeof(FILE_URL) - 1;
 		ui->utype = FILE_URL_T;
-		tport = "";
 #ifdef WITH_SSL
 	} else if (STRNEQUAL(url, HTTPS_URL)) {
 		url += sizeof(HTTPS_URL) - 1;
 		ui->utype = HTTPS_URL_T;
 		ui->portnum = HTTPS_PORT;
-		tport = httpsport;
 #endif
+	} else if (rui != NULL) {
+		copyurlinfo(ui, rui);
 	} else {
 		warnx("Invalid %s `%s'", desc, url);
  cleanup_parse_url:
@@ -440,6 +511,7 @@ parse_url(const char *url, const char *d
 		return (-1);
 	}
 
+
 	if (*url == '\0')
 		return (0);
 
@@ -504,7 +576,8 @@ parse_url(const char *url, const char *d
 #endif /* INET6 */
 		if ((cp = strchr(thost, ':')) != NULL)
 			*cp++ = '\0';
-	ui->host = thost;
+	if (*thost != '\0')
+		ui->host = thost;
 
 			/* look for [:port] */
 	if (cp != NULL) {
@@ -519,7 +592,9 @@ parse_url(const char *url, const char *d
 		}
 		ui->portnum = nport;
 		tport = cp;
-	}
+	} else
+		tport = get_port(ui);
+
 
 	if (tport != NULL)
 		ui->port = ftp_strdup(tport);
@@ -530,8 +605,8 @@ parse_url(const char *url, const char *d
 		ui->path = ftp_strdup(emptypath);
 	}
 
-	DPRINTF("parse_url: user `%s' pass `%s' host %s port %s(%d) "
-	    "path `%s'\n",
+	DPRINTF("%s: user `%s' pass `%s' host %s port %s(%d) "
+	    "path `%s'\n", __func__,
 	    STRorNULL(auth->user), STRorNULL(auth->pass),
 	    STRorNULL(ui->host), STRorNULL(ui->port),
 	    ui->portnum ? ui->portnum : -1, STRorNULL(ui->path));
@@ -544,7 +619,7 @@ sigjmp_buf	httpabort;
 static int
 ftp_socket(const struct urlinfo *ui, void **ssl)
 {
-	struct addrinfo	hints, *res, *res0 = NULL;
+	struct addrinfo hints, *res, *res0 = NULL;
 	int error;
 	int s;
 	const char *host = ui->host;
@@ -649,7 +724,7 @@ handle_noproxy(const char *host, in_port
 		if (*cp == '\0')
 			continue;
 		if ((np = strrchr(cp, ':')) != NULL) {
-			*np++ =  '\0';
+			*np++ =	 '\0';
 			np_port = strtoul(np, &ep, 10);
 			if (*np == '\0' || *ep != '\0')
 				continue;
@@ -681,7 +756,7 @@ handle_proxy(const char *url, const char
 	}
 
 	initurlinfo(&pui);
-	if (parse_url(penv, "proxy URL", &pui, pauth) == -1)
+	if (parse_url(penv, "proxy URL", &pui, pauth, NULL) == -1)
 		return -1;
 
 	if ((!IS_HTTP_TYPE(pui.utype) && pui.utype != FTP_URL_T) ||
@@ -852,9 +927,9 @@ print_connect(FETCH *fin, const struct u
 }
 #endif
 
-#define C_OK 0
-#define C_CLEANUP 1
-#define C_IMPROPER 2
+#define	C_OK 0
+#define	C_CLEANUP 1
+#define	C_IMPROPER 2
 
 static int
 getresponseline(FETCH *fin, char *buf, size_t buflen, int *len)
@@ -953,7 +1028,7 @@ parse_posinfo(const char **cp, struct po
 static void
 do_auth(int hcode, const char *url, const char *penv, struct authinfo *wauth,
     struct authinfo *pauth, char **auth, const char *message,
-    volatile int *rval)
+    volatile int *rval, struct urlinfo *ui)
 {
 	struct authinfo aauth;
 	char *response;
@@ -988,7 +1063,8 @@ do_auth(int hcode, const char *url, cons
 	if (auth_url(*auth, &response, &aauth) == 0) {
 		*rval = fetch_url(url, penv,
 		    hcode == 401 ? pauth->auth : response,
-		    hcode == 401 ? response: wauth->auth);
+		    hcode == 401 ? response : wauth->auth,
+		    ui);
 		memset(response, 0, strlen(response));
 		FREEPTR(response);
 	}
@@ -999,12 +1075,12 @@ static int
 negotiate_connection(FETCH *fin, const char *url, const char *penv,
     struct posinfo *pi, time_t *mtime, struct authinfo *wauth,
     struct authinfo *pauth, volatile int *rval, volatile int *ischunked,
-    char **auth)
+    char **auth, struct urlinfo *ui)
 {
 	int			len, hcode, rv;
 	char			buf[FTPBUFLEN], *ep;
 	const char		*cp, *token;
-	char 			*location, *message;
+	char			*location, *message;
 
 	*auth = message = location = NULL;
 
@@ -1119,18 +1195,19 @@ negotiate_connection(FETCH *fin, const c
 				fprintf(ttyout, "Redirected via %s\n",
 				    location);
 			*rval = fetch_url(url, location,
-			    pauth->auth, wauth->auth);
+			    pauth->auth, wauth->auth, ui);
 		} else {
 			if (verbose)
 				fprintf(ttyout, "Redirected to %s\n",
 				    location);
-			*rval = go_fetch(location);
+			*rval = go_fetch(location, ui);
 		}
 		goto cleanup_fetch_url;
 #ifndef NO_AUTH
 	case 401:
 	case 407:
-		do_auth(hcode, url, penv, wauth, pauth, auth, message, rval);
+		do_auth(hcode, url, penv, wauth, pauth, auth, message, rval,
+		    ui);
 		goto cleanup_fetch_url;
 #endif
 	default:
@@ -1195,7 +1272,7 @@ connectmethod(FETCH *fin, const char *ur
 		message = ftp_strdup(ep);
 		break;
 	}
-		
+
 	for (;;) {
 		int len;
 		if (getresponseline(fin, buf, sizeof(buf), &len) != C_OK)
@@ -1224,7 +1301,8 @@ connectmethod(FETCH *fin, const char *ur
 		break;
 #ifndef NO_AUTH
 	case 407:
-		do_auth(hcode, url, penv, wauth, pauth, auth, message, rval);
+		do_auth(hcode, url, penv, wauth, pauth, auth, message, rval,
+		    ui);
 		goto cleanup_fetch_url;
 #endif
 	default:
@@ -1262,7 +1340,8 @@ out:
  * is still open (e.g, ftp xfer with trailing /)
  */
 static int
-fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth)
+fetch_url(const char *url, const char *proxyenv, char *proxyauth,
+    char *wwwauth, struct urlinfo *rui)
 {
 	sigfunc volatile	oldint;
 	sigfunc volatile	oldpipe;
@@ -1271,18 +1350,18 @@ fetch_url(const char *url, const char *p
 	int volatile		s;
 	struct stat		sb;
 	int volatile		isproxy;
-	int volatile 		rval, ischunked;
+	int volatile		rval, ischunked;
 	size_t			flen;
 	static size_t		bufsize;
 	static char		*xferbuf;
 	const char		*cp;
 	char			*ep;
-	char			*auth;
+	char			*volatile auth;
 	char			*volatile savefile;
 	char			*volatile location;
 	char			*volatile message;
 	char			*volatile decodedpath;
-	struct authinfo 	wauth, pauth;
+	struct authinfo		wauth, pauth;
 	struct posinfo		pi;
 	off_t			hashbytes;
 	int			(*volatile closefunc)(FILE *);
@@ -1295,7 +1374,7 @@ fetch_url(const char *url, const char *p
 
 	DPRINTF("%s: `%s' proxyenv `%s'\n", __func__, url, STRorNULL(penv));
 
-	oldquit = oldalrm = oldint = oldpipe = NULL;
+	oldquit = oldalrm = oldint = oldpipe = SIG_ERR;
 	closefunc = NULL;
 	fin = NULL;
 	fout = NULL;
@@ -1315,7 +1394,7 @@ fetch_url(const char *url, const char *p
 	if (sigsetjmp(httpabort, 1))
 		goto cleanup_fetch_url;
 
-	if (parse_url(url, "URL", &ui, &wauth) == -1)
+	if (parse_url(url, "URL", &ui, &wauth, rui) == -1)
 		goto cleanup_fetch_url;
 
 	copyurlinfo(&oui, &ui);
@@ -1331,7 +1410,7 @@ fetch_url(const char *url, const char *p
 			rval = fetch_ftp(url);
 			goto cleanup_fetch_url;
 		}
-		if (!IS_HTTP_TYPE(ui.utype) || outfile == NULL)  {
+		if (!IS_HTTP_TYPE(ui.utype) || outfile == NULL)	 {
 			warnx("Invalid URL (no file after host) `%s'", url);
 			goto cleanup_fetch_url;
 		}
@@ -1386,7 +1465,8 @@ fetch_url(const char *url, const char *p
 			filesize = sb.st_size;
 		}
 		if (restart_point) {
-			if (lseek(fetch_fileno(fin), restart_point, SEEK_SET) < 0) {
+			if (lseek(fetch_fileno(fin), restart_point, SEEK_SET) 
+			    < 0) {
 				warn("Can't seek to restart `%s'",
 				    decodedpath);
 				goto cleanup_fetch_url;
@@ -1460,7 +1540,8 @@ fetch_url(const char *url, const char *p
 #ifdef WITH_SSL
 		if (isproxy && oui.utype == HTTPS_URL_T) {
 			switch (connectmethod(fin, url, penv, &oui, &ui,
-			    &wauth, &pauth, &auth, &hasleading, &rval)) {
+			    &wauth, &pauth, __UNVOLATILE(&auth), &hasleading,
+			    &rval)) {
 			case C_CLEANUP:
 				goto cleanup_fetch_url;
 			case C_IMPROPER:
@@ -1496,7 +1577,8 @@ fetch_url(const char *url, const char *p
 		alarmtimer(0);
 
 		switch (negotiate_connection(fin, url, penv, &pi,
-		    &mtime, &wauth, &pauth, &rval, &ischunked, &auth)) {
+		    &mtime, &wauth, &pauth, &rval, &ischunked,
+		    __UNVOLATILE(&auth), &ui)) {
 		case C_OK:
 			break;
 		case C_CLEANUP:
@@ -1570,9 +1652,9 @@ fetch_url(const char *url, const char *p
 
 	bytes = 0;
 	hashbytes = mark;
-	if (oldalrm) {
+	if (oldalrm != SIG_ERR) {
 		(void)xsignal(SIGALRM, oldalrm);
-		oldalrm = NULL;
+		oldalrm = SIG_ERR;
 	}
 	progressmeter(-1);
 
@@ -1603,7 +1685,7 @@ fetch_url(const char *url, const char *p
 			}
 
 				/*
-				 * XXX:	Work around bug in Apache 1.3.9 and
+				 * XXX: Work around bug in Apache 1.3.9 and
 				 *	1.3.11, which incorrectly put trailing
 				 *	space after the chunk-size.
 				 */
@@ -1638,13 +1720,17 @@ fetch_url(const char *url, const char *p
 			if (ischunked)
 				bufrem = MIN(chunksize, bufrem);
 			while (bufrem > 0) {
+				size_t nr = MIN((off_t)bufsize, bufrem);
 				flen = fetch_read(xferbuf, sizeof(char),
-				    MIN((off_t)bufsize, bufrem), fin);
-				if (flen <= 0)
+				    nr, fin);
+				if (flen == 0) {
+					if (fetch_error(fin))
+						goto chunkerror;
 					goto chunkdone;
+				}
 				bytes += flen;
 				bufrem -= flen;
-				if (fwrite(xferbuf, sizeof(char), flen, fout)
+				if (maxwrite(xferbuf, sizeof(char), flen, fout)
 				    != flen) {
 					warn("Writing `%s'", savefile);
 					goto cleanup_fetch_url;
@@ -1692,7 +1778,7 @@ fetch_url(const char *url, const char *p
 	} while (ischunked);
 
 /* XXX: deal with optional trailer & CRLF here? */
-
+chunkerror:
 	if (hash && !progress && bytes > 0) {
 		if (bytes < mark)
 			(void)putc('#', ttyout);
@@ -1730,14 +1816,14 @@ fetch_url(const char *url, const char *p
 	warnx("Improper response from `%s:%s'", ui.host, ui.port);
 
  cleanup_fetch_url:
-	if (oldint)
+	if (oldint != SIG_ERR)
 		(void)xsignal(SIGINT, oldint);
-	if (oldpipe)
+	if (oldpipe != SIG_ERR)
 		(void)xsignal(SIGPIPE, oldpipe);
-	if (oldalrm)
+	if (oldalrm != SIG_ERR)
 		(void)xsignal(SIGALRM, oldalrm);
-	if (oldquit)
-		(void)xsignal(SIGQUIT, oldpipe);
+	if (oldquit != SIG_ERR)
+		(void)xsignal(SIGQUIT, oldquit);
 	if (fin != NULL)
 		fetch_close(fin);
 	else if (s != -1)
@@ -1807,10 +1893,10 @@ fetch_ftp(const char *url)
 	char		 dirbuf[4];
 	int		 dirhasglob, filehasglob, rval, transtype, xargc;
 	int		 oanonftp, oautologin;
-	struct authinfo  auth;
+	struct authinfo	 auth;
 	struct urlinfo	 ui;
 
-	DPRINTF("fetch_ftp: `%s'\n", url);
+	DPRINTF("%s: `%s'\n", __func__, url);
 	dir = file = NULL;
 	rval = 1;
 	transtype = TYPE_I;
@@ -1819,7 +1905,7 @@ fetch_ftp(const char *url)
 	initauthinfo(&auth, NULL);
 
 	if (STRNEQUAL(url, FTP_URL)) {
-		if ((parse_url(url, "URL", &ui, &auth) == -1) ||
+		if ((parse_url(url, "URL", &ui, &auth, NULL) == -1) ||
 		    (auth.user != NULL && *auth.user == '\0') ||
 		    EMPTYSTRING(ui.host)) {
 			warnx("Invalid URL `%s'", url);
@@ -1831,7 +1917,8 @@ fetch_ftp(const char *url)
 		 */
 
 					/* check for trailing ';type=[aid]' */
-		if (! EMPTYSTRING(ui.path) && (cp = strrchr(ui.path, ';')) != NULL) {
+		if (! EMPTYSTRING(ui.path)
+		    && (cp = strrchr(ui.path, ';')) != NULL) {
 			if (strcasecmp(cp, ";type=a") == 0)
 				transtype = TYPE_A;
 			else if (strcasecmp(cp, ";type=i") == 0)
@@ -1873,12 +1960,12 @@ fetch_ftp(const char *url)
 		 * If we are dealing with classic `[user@]host:[path]' syntax,
 		 * then a path of the form `/file' (resulting from input of the
 		 * form `host:/file') means that we should do "CWD /" before
-		 * retrieving the file.  So we set dir="/" and file="file".
+		 * retrieving the file.	 So we set dir="/" and file="file".
 		 *
 		 * But if we are dealing with URLs like `ftp://host/path' then
 		 * a path of the form `/file' (resulting from a URL of the form
 		 * `ftp://host//file') means that we should do `CWD ' (with an
-		 * empty argument) before retrieving the file.  So we set
+		 * empty argument) before retrieving the file.	So we set
 		 * dir="" and file="file".
 		 *
 		 * If the path does not contain / at all, we set dir=NULL.
@@ -1909,8 +1996,8 @@ fetch_ftp(const char *url)
 		url_decode(file);
 		/* but still don't url_decode(dir) */
 	}
-	DPRINTF("fetch_ftp: user `%s' pass `%s' host %s port %s "
-	    "path `%s' dir `%s' file `%s'\n",
+	DPRINTF("%s: user `%s' pass `%s' host %s port %s "
+	    "path `%s' dir `%s' file `%s'\n", __func__,
 	    STRorNULL(auth.user), STRorNULL(auth.pass),
 	    STRorNULL(ui.host), STRorNULL(ui.port),
 	    STRorNULL(ui.path), STRorNULL(dir), STRorNULL(file));
@@ -1959,7 +2046,7 @@ fetch_ftp(const char *url)
 		setbinary(1, xargv);
 		break;
 	default:
-		errx(1, "fetch_ftp: unknown transfer type %d", transtype);
+		errx(1, "%s: unknown transfer type %d", __func__, transtype);
 	}
 
 		/*
@@ -1981,7 +2068,7 @@ fetch_ftp(const char *url)
 		 * (urltype is FTP_URL_T), then RFC 3986 says we need to
 		 * send a separate CWD command for each unescaped "/"
 		 * in the path, and we have to interpret %hex escaping
-		 * *after* we find the slashes.  It's possible to get
+		 * *after* we find the slashes.	 It's possible to get
 		 * empty components here, (from multiple adjacent
 		 * slashes in the path) and RFC 3986 says that we should
 		 * still do `CWD ' (with a null argument) in such cases.
@@ -2024,7 +2111,7 @@ fetch_ftp(const char *url)
 		 *		"CWD /", "CWD foo", "CWD bar", "RETR file"
 		 * ftp://host/%2Ffoo/bar/file	dir="%2Ffoo/bar"
 		 *		"CWD /foo", "CWD bar", "RETR file"
-		 * ftp://host/%2Ffoo%2Fbar/file	dir="%2Ffoo%2Fbar"
+		 * ftp://host/%2Ffoo%2Fbar/file dir="%2Ffoo%2Fbar"
 		 *		"CWD /foo/bar", "RETR file"
 		 * ftp://host/%2Ffoo%2Fbar%2Ffile	dir=NULL
 		 *		"RETR /foo/bar/file"
@@ -2041,7 +2128,7 @@ fetch_ftp(const char *url)
 				url_decode(dir);
 			} else
 				nextpart = NULL;
-			DPRINTF("fetch_ftp: dir `%s', nextpart `%s'\n",
+			DPRINTF("%s: dir `%s', nextpart `%s'\n", __func__,
 			    STRorNULL(dir), STRorNULL(nextpart));
 			if (ui.utype == FTP_URL_T || *dir != '\0') {
 				(void)strlcpy(cmdbuf, "cd", sizeof(cmdbuf));
@@ -2097,14 +2184,15 @@ fetch_ftp(const char *url)
 		mget(xargc, xargv);
 		interactive = ointeractive;
 	} else {
-		if (outfile == NULL) {
+		char *destfile = outfile;
+		if (destfile == NULL) {
 			cp = strrchr(file, '/');	/* find savefile */
 			if (cp != NULL)
-				outfile = cp + 1;
+				destfile = cp + 1;
 			else
-				outfile = file;
+				destfile = file;
 		}
-		xargv[2] = (char *)outfile;
+		xargv[2] = (char *)destfile;
 		xargv[3] = NULL;
 		xargc++;
 		if (restartautofetch)
@@ -2135,7 +2223,7 @@ fetch_ftp(const char *url)
  * is still open (e.g, ftp xfer with trailing /)
  */
 static int
-go_fetch(const char *url)
+go_fetch(const char *url, struct urlinfo *rui)
 {
 	char *proxyenv;
 	char *p;
@@ -2184,7 +2272,7 @@ go_fetch(const char *url)
 	    || STRNEQUAL(url, HTTPS_URL)
 #endif
 	    || STRNEQUAL(url, FILE_URL))
-		return (fetch_url(url, NULL, NULL, NULL));
+		return (fetch_url(url, NULL, NULL, NULL, rui));
 
 	/*
 	 * If it contains "://" but does not begin with ftp://
@@ -2199,13 +2287,20 @@ go_fetch(const char *url)
 		errx(1, "Unsupported URL scheme `%.*s'", (int)(p - url), url);
 
 	/*
+	 * Refer to previous urlinfo if provided. This makes relative
+	 * redirects work.
+	 */
+	if (use_relative(rui))
+	    return fetch_url(url, NULL, NULL, NULL, rui);
+
+	/*
 	 * Try FTP URL-style and host:file arguments next.
 	 * If ftpproxy is set with an FTP URL, use fetch_url()
-	 * Othewise, use fetch_ftp().
+	 * Otherwise, use fetch_ftp().
 	 */
 	proxyenv = getoptionvalue("ftp_proxy");
 	if (!EMPTYSTRING(proxyenv) && STRNEQUAL(url, FTP_URL))
-		return (fetch_url(url, NULL, NULL, NULL));
+		return (fetch_url(url, NULL, NULL, NULL, rui));
 
 	return (fetch_ftp(url));
 }
@@ -2248,10 +2343,11 @@ auto_fetch(int argc, char *argv[])
 		redirect_loop = 0;
 		if (!anonftp)
 			anonftp = 2;	/* Handle "automatic" transfers. */
-		rval = go_fetch(argv[argpos]);
+		rval = go_fetch(argv[argpos], NULL);
 		if (outfile != NULL && strcmp(outfile, "-") != 0
-		    && outfile[0] != '|')
-			outfile = NULL;
+		    && outfile[0] != '|') {
+			FREEPTR(outfile);
+		}
 		if (rval > 0)
 			rval = argpos + 1;
 	}
@@ -2286,7 +2382,7 @@ auto_put(int argc, char **argv, const ch
 	pathsep = NULL;
 	rval = 1;
 
-	DPRINTF("auto_put: target `%s'\n", uploadserver);
+	DPRINTF("%s: target `%s'\n", __func__, uploadserver);
 
 	path = ftp_strdup(uploadserver);
 	len = strlen(path);
@@ -2295,7 +2391,7 @@ auto_put(int argc, char **argv, const ch
 			 * make sure we always pass a directory to auto_fetch
 			 */
 		if (argc > 1) {		/* more than one file to upload */
-			len = strlen(uploadserver) + 2;	/* path + "/" + "\0" */
+			len = strlen(uploadserver) + 2; /* path + "/" + "\0" */
 			free(path);
 			path = (char *)ftp_malloc(len);
 			(void)strlcpy(path, uploadserver, len);
@@ -2319,7 +2415,7 @@ auto_put(int argc, char **argv, const ch
 			uargc++;
 		}
 	}
-	DPRINTF("auto_put: URL `%s' argv[2] `%s'\n",
+	DPRINTF("%s: URL `%s' argv[2] `%s'\n", __func__,
 	    path, STRorNULL(uargv[2]));
 
 			/* connect and cwd */

Index: src/usr.bin/ftp/ftp.1
diff -u src/usr.bin/ftp/ftp.1:1.135.8.2 src/usr.bin/ftp/ftp.1:1.135.8.3
--- src/usr.bin/ftp/ftp.1:1.135.8.2	Mon Sep 12 15:05:21 2022
+++ src/usr.bin/ftp/ftp.1	Mon Sep 12 17:08:13 2022
@@ -1,6 +1,6 @@
-.\" 	$NetBSD: ftp.1,v 1.135.8.2 2022/09/12 15:05:21 martin Exp $
+.\" 	$NetBSD: ftp.1,v 1.135.8.3 2022/09/12 17:08:13 martin Exp $
 .\"
-.\" Copyright (c) 1996-2015 The NetBSD Foundation, Inc.
+.\" Copyright (c) 1996-2021 The NetBSD Foundation, Inc.
 .\" All rights reserved.
 .\"
 .\" This code is derived from software contributed to The NetBSD Foundation
@@ -57,7 +57,7 @@
 .\"
 .\"	@(#)ftp.1	8.3 (Berkeley) 10/9/94
 .\"
-.Dd April 24, 2015
+.Dd August 29, 2022
 .Dt FTP 1
 .Os
 .Sh NAME
@@ -65,7 +65,7 @@
 .Nd Internet file transfer program
 .Sh SYNOPSIS
 .Nm
-.Op Fl 46AadefginpRtVv
+.Op Fl 46AadefginpRtVv?
 .Op Fl N Ar netrc
 .Op Fl o Ar output
 .Op Fl P Ar port
@@ -84,12 +84,11 @@
 .Xc
 .Oc
 .Ek
-.Op Fl x Ar xferbufsize
+.Op Fl x Ar xfersize
 .Bk -words
 .\" [[user@]host [port]]
 .Oo
-.Oo Ar user Ns Li \&@ Oc Ns Ar host
-.Op Ar port
+.Oo Ar user Ns Li \&@ Oc Ns Ar host Oo Ar port Oc
 .Oc
 .Ek
 .Bk -words
@@ -122,7 +121,7 @@
 .Ar host Oo Li \&: Ar port Oc
 .Li / Ar path
 .Op Li /
-.Op Li ;type= Ar X
+.Op Li ;type= Ar type
 .Oc
 .Sm on
 .Ek
@@ -139,12 +138,24 @@
 .Oc
 .Sm on
 .Ek
-.Op Ar \&.\&.\&.
+.Bk -words
+.\" [https://[user[:password]@]host[:port]/path]
+.Sm off
+.Oo
+.Li https://
+.Oo Ar user
+.Op Li \&: Ar password
+.Li \&@ Oc
+.Ar host Oo Li \&: Ar port Oc
+.Li / Ar path
+.Oc
+.Sm on
+.Ek
+.Ar \&...
 .Nm
 .Bk -words
-.Fl u Ar URL Ar file
+.Fl u Ar url Ar
 .Ek
-.Op Ar \&.\&.\&.
 .Sh DESCRIPTION
 .Nm
 is the user interface to the Internet standard File Transfer Protocol.
@@ -173,10 +184,13 @@ Forces
 .Nm
 to only use IPv6 addresses.
 .It Fl A
-Force active mode ftp.
+Force active mode
+.Tn FTP .
 By default,
 .Nm
-will try to use passive mode ftp and fall back to active mode
+will try to use passive mode
+.Tn FTP
+and fall back to active mode
 if passive is not supported by the server.
 This option causes
 .Nm
@@ -284,12 +298,14 @@ bytes/second.
 Refer to
 .Ic rate
 for more information.
-.It Fl u Ar URL file Op \&.\&.\&.
+.It Fl u Ar url Ar
 Upload files on the command line to
-.Ar URL
+.Ar url
 where
-.Ar URL
-is one of the ftp URL types as supported by auto-fetch
+.Ar url
+is one of the
+.Sq Li ftp://
+URL types as supported by auto-fetch
 (with an optional target filename for single file uploads), and
 .Ar file
 is one or more local files to be uploaded.
@@ -312,12 +328,14 @@ Forces
 .Nm
 to show all responses from the remote server, as well
 as report on data transfer statistics.
-.It Fl x Ar xferbufsize
+.It Fl x Ar xfersize
 Set the size of the socket send and receive buffers to
-.Ar xferbufsize .
+.Ar xfersize .
 Refer to
 .Ic xferbuf
 for more information.
+.It Fl ?
+Display help to stdout, and exit.
 .El
 .Pp
 The client host with which
@@ -334,7 +352,7 @@ from the user.
 When
 .Nm
 is awaiting commands from the user the prompt
-.Ql ftp\*[Gt]
+.Ql ftp>
 is provided to the user.
 The following commands are recognized
 by
@@ -442,6 +460,16 @@ when an ascii type transfer is made, the
 distinguished from a record delimiter only when
 .Ic \&cr
 is off.
+.It Ic debug Op Ar debug-value
+Toggle debugging mode.
+If an optional
+.Ar debug-value
+is specified it is used to set the debugging level.
+When debugging is on,
+.Nm
+prints each command sent to the remote machine, preceded
+by the string
+.Ql \-\-> .
 .It Ic delete Ar remote-file
 Delete the file
 .Ar remote-file
@@ -477,18 +505,16 @@ Toggle command line editing, and context
 completion.
 This is automatically enabled if input is from a terminal, and
 disabled otherwise.
-.It Ic epsv epsv4 epsv6
+.It Ic epsv , epsv4 , epsv6
 Toggle the use of the extended
 .Dv EPSV
 and
 .Dv EPRT
 commands on all IP, IPv4, and IPv6 connections respectively.
 First try
-.Dv EPSV /
-.Dv EPRT ,
+.Dv EPSV Ns \^/\^ Ns Dv EPRT ,
 and then
-.Dv PASV /
-.Dv PORT .
+.Dv PASV Ns \^/\^ Ns Dv PORT .
 This is enabled by default.
 If an extended command fails then this option will be temporarily
 disabled for the duration of the current connection, or until
@@ -519,19 +545,11 @@ format is
 .It Ic ftp Ar host Op Ar port
 A synonym for
 .Ic open .
-.It Ic ftp_debug Op Ar ftp_debug-value
-Toggle debugging mode.
-If an optional
-.Ar ftp_debug-value
-is specified it is used to set the debugging level.
-When debugging is on,
-.Nm
-prints each command sent to the remote machine, preceded
-by the string
-.Ql \-\-\*[Gt] .
 .It Ic gate Op Ar host Op Ar port
 Toggle gate-ftp mode, which used to connect through the
-TIS FWTK and Gauntlet ftp proxies.
+TIS FWTK and Gauntlet
+.Tn FTP
+proxies.
 This will not be permitted if the gate-ftp server hasn't been set
 (either explicitly by the user, or from the
 .Ev FTPSERVER
@@ -589,9 +607,11 @@ each remote file name is expanded
 separately on the remote machine and the lists are not merged.
 Expansion of a directory name is likely to be
 different from expansion of the name of an ordinary file:
-the exact result depends on the foreign operating system and ftp server,
+the exact result depends on the foreign operating system and
+.Tn FTP
+server,
 and can be previewed by doing
-.Ql mls remote-files \-
+.Sq Li mls remote-files \- .
 Note:
 .Ic mget ,
 .Ic mput
@@ -672,32 +692,32 @@ To invoke a macro, use the
 command (see above).
 .Pp
 The macro processor interprets
-.Sq $
+.Ql $
 and
-.Sq \e
+.Ql \e
 as special characters.
 A
-.Sq $
+.Ql $
 followed by a number (or numbers) is replaced by the
 corresponding argument on the macro invocation command line.
 A
-.Sq $
+.Ql $
 followed by an
-.Sq i
+.Ql i
 signals the macro processor that the executing macro is to be
 looped.
 On the first pass
-.Dq $i
+.Ql $i
 is replaced by the first argument on the macro invocation command
 line, on the second pass it is replaced by the second argument,
 and so on.
 A
-.Sq \e
+.Ql \e
 followed by any character is replaced by that character.
 Use the
-.Sq \e
+.Ql \e
 to prevent special treatment of the
-.Sq $ .
+.Ql $ .
 .It Ic mdelete Op Ar remote-files
 Delete the
 .Ar remote-files
@@ -732,7 +752,7 @@ Files are transferred into the local wor
 which can be changed with
 .Ql lcd directory ;
 new local directories can be created with
-.Ql "\&! mkdir directory" .
+.Sq Li "\&! mkdir directory" .
 .It Ic mkdir Ar directory-name
 Make a directory on the remote machine.
 .It Ic mls Ar remote-files local-file
@@ -755,7 +775,7 @@ Display the contents of
 in a machine-parsable form, using
 .Dv MLSD .
 The format of display can be changed with
-.Sq "remopts mlst ..." .
+.Sq Li "remopts mlst ..." .
 .It Ic mlst Op Ar remote-path
 Display the details about
 .Ar remote-path
@@ -763,7 +783,7 @@ Display the details about
 in a machine-parsable form, using
 .Dv MLST .
 The format of display can be changed with
-.Sq "remopts mlst ..." .
+.Sq Li "remopts mlst ..." .
 .It Ic mode Ar mode-name
 Set the file transfer
 .Ic mode
@@ -809,7 +829,7 @@ If the file does not
 exist on the current system, the remote file is considered
 .Ic newer .
 Otherwise, this command is identical to
-.Ar get .
+.Ic get .
 .It Ic nlist Op Ar remote-path Op Ar local-file
 A synonym for
 .Ic ls .
@@ -834,7 +854,8 @@ The mapping follows the pattern set by
 .Ar inpattern
 and
 .Ar outpattern .
-.Op Ar Inpattern
+.Pp
+.Ar inpattern
 is a template for incoming filenames (which may have already been
 processed according to the
 .Ic ntrans
@@ -843,16 +864,16 @@ and
 settings).
 Variable templating is accomplished by including the
 sequences
-.Dq $1 ,
-.Dq $2 ,
-\&...
-.Dq $9
+.Ql $1 ,
+.Ql $2 ,
+\&...\|,
+.Ql $9
 in
 .Ar inpattern .
 Use
-.Sq \e
+.Ql \e
 to prevent this special treatment of the
-.Sq $
+.Ql $
 character.
 All other characters are treated literally, and are used to determine the
 .Ic nmap
@@ -860,53 +881,72 @@ All other characters are treated literal
 variable values.
 For example, given
 .Ar inpattern
-$1.$2 and the remote file name "mydata.data", $1 would have the value
-"mydata", and $2 would have the value "data".
+.Sq Li $1.$2
+and the remote file name
+.Sq Li mydata.data ,
+.Ql $1
+would have the value
+.Sq Li mydata ,
+and
+.Ql $2
+would have the value
+.Sq Li data .
+.Pp
 The
 .Ar outpattern
 determines the resulting mapped filename.
 The sequences
-.Dq $1 ,
-.Dq $2 ,
-\&...
-.Dq $9
+.Ql $1 ,
+.Ql $2 ,
+\&...\|,
+.Ql $9
 are replaced by any value resulting from the
 .Ar inpattern
 template.
 The sequence
-.Dq $0
+.Ql $0
 is replaced by the original filename.
 Additionally, the sequence
 .Dq Op Ar seq1 , Ar seq2
 is replaced by
-.Op Ar seq1
+.Ar seq1
 if
 .Ar seq1
 is not a null string; otherwise it is replaced by
 .Ar seq2 .
 For example, the command
 .Pp
-.Bd -literal -offset indent -compact
-nmap $1.$2.$3 [$1,$2].[$2,file]
-.Ed
+.Dl nmap $1.$2.$3 [$1,$2].[$2,file]
 .Pp
 would yield
-the output filename "myfile.data" for input filenames "myfile.data" and
-"myfile.data.old", "myfile.file" for the input filename "myfile", and
-"myfile.myfile" for the input filename ".myfile".
+the output filename
+.Sq Li myfile.data
+for input filenames
+.Sq Li myfile.data
+and
+.Sq Li myfile.data.old ,
+.Sq Li myfile.file
+for the input filename
+.Sq Li myfile ,
+and
+.Sq Li myfile.myfile
+for the input filename
+.Sq Li "\&.myfile" .
 Spaces may be included in
 .Ar outpattern  ,
 as in the example:
-.Dl nmap $1 sed "s/  *$//" \*[Gt] $1
+.Pp
+.Dl nmap $1 sed "s/  *$//" > $1
+.Pp
 Use the
-.Sq \e
+.Ql \e
 character to prevent special treatment
 of the
-.Sq $ ,
-.Sq \&[ ,
-.Sq \&] ,
+.Ql $ ,
+.Ql \&[ ,
+.Ql \&] ,
 and
-.Sq \&,
+.Ql \&,
 characters.
 .It Ic ntrans Op Ar inchars Op Ar outchars
 Set or unset the filename character translation mechanism.
@@ -1027,7 +1067,7 @@ The progress bar will be disabled for a 
 as
 .Sq Fl
 or a command that starts with
-.Sq \&| .
+.Ql \&| .
 Refer to
 .Sx FILE NAMING CONVENTIONS
 for more information.
@@ -1081,7 +1121,9 @@ Any other response will answer
 .Sq yes
 to the current file.
 .It Ic proxy Ar ftp-command
-Execute an ftp command on a secondary control connection.
+Execute an
+.Tn FTP
+command on a secondary control connection.
 This command allows simultaneous connection to two remote
 .Tn FTP
 servers for transferring files between the two servers.
@@ -1090,11 +1132,13 @@ The first
 command should be an
 .Ic open  ,
 to establish the secondary control connection.
-Enter the command "proxy ?" to see other
+Enter the command
+.Sq Li "proxy ?"
+to see other
 .Tn FTP
 commands executable on the secondary connection.
 The following commands behave differently when prefaced by
-.Ic proxy  :
+.Ic proxy\^  :
 .Ic open
 will not define new macros during the auto-login process,
 .Ic close
@@ -1139,7 +1183,7 @@ machine.
 .It Ic quit
 A synonym for
 .Ic bye .
-.It Ic quote Ar arg1 arg2 ...
+.It Ic quote Op Ar arg ...
 The arguments specified are sent, verbatim, to the remote
 .Tn FTP
 server.
@@ -1153,7 +1197,7 @@ is 0, disable the throttle.
 .Pp
 .Ar direction
 may be one of:
-.Bl -tag -width "all" -offset indent -compact
+.Bl -tag -width ".Cm all" -offset indent -compact
 .It Cm all
 Both directions.
 .It Cm get
@@ -1166,7 +1210,7 @@ Outgoing transfers.
 can be modified on the fly by
 .Ar increment
 bytes (default: 1024) each time a given signal is received:
-.Bl -tag -width "SIGUSR1" -offset indent
+.Bl -tag -width ".Dv SIGUSR1" -offset indent
 .It Dv SIGUSR1
 Increment
 .Ar maximum
@@ -1224,7 +1268,7 @@ to
 Remote
 .Tn FTP
 commands known to support options include:
-.Sq MLST
+.Dv MLST
 (used for
 .Dv MLSD
 and
@@ -1277,10 +1321,16 @@ local filename for a
 .Ic get
 or
 .Ic mget
-command, a ".1" is appended to the name.
+command, a
+.Ql \&.1
+is appended to the name.
 If the resulting name matches another existing file,
-a ".2" is appended to the original name.
-If this process continues up to ".99", an error
+a
+.Ql \&.2
+is appended to the original name.
+If this process continues up to
+.Ql .99 ,
+an error
 message is printed, and the transfer does not take place.
 The generated unique filename will be reported.
 Note that
@@ -1358,7 +1408,7 @@ Defaults to
 Defaults to
 .Ev $FTPRPROMPT .
 .El
-.It Ic site Ar arg1 arg2 ...
+.It Ic site Op Ar arg ...
 The arguments specified are sent, verbatim, to the remote
 .Tn FTP
 server as a
@@ -1479,7 +1529,7 @@ A synonym for
 .Pp
 Command arguments which have embedded spaces may be quoted with
 quote
-.Sq \&"
+.Ql \&\(dq
 marks.
 .Pp
 Commands which toggle settings can take an explicit
@@ -1514,7 +1564,7 @@ If
 receives a
 .Dv SIGINFO
 (see the
-.Dq status
+.Cm status
 argument of
 .Xr stty 1 )
 or
@@ -1544,7 +1594,7 @@ contains a glob character and globbing i
 (see
 .Ic glob ) ,
 then the equivalent of
-.Ql mget path
+.Sq Li mget path
 is performed.
 .Pp
 If the directory component of
@@ -1557,10 +1607,10 @@ of
 in the current directory.
 Otherwise, the full remote name is used as the local name,
 relative to the local root directory.
-.\" ftp://[user[:password]@]host[:port]/path[/][;type=X]
+.\" ftp://[user[:password]@]host[:port]/path[/][;type=type]
 .It Li ftp:// Ns Oo Ar user Ns Oo Ns Li \&: Ns Ar password Oc Ns Li \&@ Oc \
 Ns Ar host Ns Oo Li \&: Ns Ar port Oc Ns Li / Ns Ar path Ns Oo Li / Oc \
-Ns Oo Li ;type= Ns Ar X Oc
+Ns Oo Li ;type= Ns Ar type Oc
 An
 .Tn FTP
 URL, retrieved using the
@@ -1583,9 +1633,9 @@ In this case, use
 if supplied, otherwise prompt the user for one.
 .Pp
 If a suffix of
-.Sq ;type=A
+.Sq Li \&;type=A
 or
-.Sq ;type=I
+.Sq Li \&;type=I
 is supplied, then the transfer type will take place as
 ascii or binary (respectively).
 The default transfer type is binary.
@@ -1596,7 +1646,7 @@ In order to be compliant with
 interprets the
 .Ar path
 part of an
-.Dq ftp://
+.Sq Li ftp://
 auto-fetch URL as follows:
 .Bl -bullet
 .It
@@ -1676,20 +1726,20 @@ user.
 If the
 .Pa /
 directory is required, use a leading path of
-.Dq %2F .
+.Sq Li \&%2F .
 If a user's home directory is required (and the remote server supports
 the syntax), use a leading path of
-.Dq %7Euser/ .
+.Sq Li \&%7E Ns Ar user Ns Li / .
 For example, to retrieve
 .Pa /etc/motd
 from
-.Sq localhost
+.Sq Li localhost
 as the user
-.Sq myname
+.Sq Li myname
 with the password
-.Sq mypass ,
+.Sq Li mypass ,
 use
-.Dq ftp://myname:mypass@localhost/%2fetc/motd
+.Sq Li ftp://myname:mypass@localhost/%2fetc/motd
 .It
 The exact
 .Ic cd
@@ -1697,11 +1747,11 @@ and
 .Ic get
 commands can be controlled by careful choice of
 where to use
-.Sq /
+.Sq Li /
 and where to use
-.Sq %2F
+.Sq Li \&%2F
 (or
-.Sq %2f ) .
+.Sq Li %2f ) .
 For example, the following URLs correspond to the
 equivalents of the indicated commands:
 .Bl -tag -width "ftp://host/%2Fdir1%2Fdir2%2Ffile";
@@ -1748,9 +1798,9 @@ If
 authorization is required to retrieve
 .Ar path ,
 and
-.Sq user
+.Ar user
 (and optionally
-.Sq password )
+.Ar password\^ )
 is in the URL, use them for the first attempt to authenticate.
 .\" https://[user[:password]@]host[:port]/path
 .It Li https:// Ns Oo Ar user Ns Oo Li \&: Ns Ar password Oc Ns Li \&@ Oc \
@@ -1770,9 +1820,9 @@ If
 authorization is required to retrieve
 .Ar path ,
 and
-.Sq user
+.Ar user
 (and optionally
-.Sq password )
+.Ar password\^ )
 is in the URL, use them for the first attempt to authenticate.
 There is currently no certificate validation and verification.
 .\" file:///path
@@ -1815,7 +1865,7 @@ is recommended, to avoid writing to unex
 If a classic format or an
 .Tn FTP
 URL format has a trailing
-.Sq /
+.Ql /
 or an empty
 .Ar path
 component, then
@@ -1847,9 +1897,9 @@ proxies will be restarted.
 For
 .Tn FTP ,
 this is implemented by using
-.Nm reget
+.Ic reget
 instead of
-.Nm get .
+.Ic get .
 For
 .Tn HTTP ,
 this is implemented by using the
@@ -1863,7 +1913,7 @@ to enter a username and password to auth
 When specifying IPv6 numeric addresses in a URL, you need to
 surround the address in square brackets.
 E.g.:
-.Dq ftp://[::1]:21/ .
+.Sq Li ftp://[::1]:21/ .
 This is because colons are used in IPv6 numeric address as well as
 being the separator for the port number.
 .Sh ABORTING A FILE TRANSFER
@@ -1886,7 +1936,9 @@ sending the requested file.
 .Pp
 If the terminal interrupt key sequence is used whilst
 .Nm
-is awaiting a reply from the remote server for the ABOR processing,
+is awaiting a reply from the remote server for the
+.Dv ABOR
+processing,
 then the connection will be closed.
 This is different from the traditional behaviour (which ignores the
 terminal interrupt during this phase), but is considered more useful.
@@ -1899,13 +1951,13 @@ commands are processed according to the 
 If the file name
 .Sq Fl
 is specified, the
-.Ar stdin
+.Va stdin
 (for reading) or
-.Ar stdout
+.Va stdout
 (for writing) is used.
 .It
 If the first character of the file name is
-.Sq \&| ,
+.Ql \&| ,
 the
 remainder of the argument is interpreted as a shell command.
 .Nm
@@ -1915,13 +1967,12 @@ with the argument supplied, and reads (w
 (stdin).
 If the shell command includes spaces, the argument
 must be quoted; e.g.
-.Dq Qq Li \&| ls\ \-lt .
+.Sq Li \(dq|\~ls\~\-lt\(dq .
 A particularly
 useful example of this mechanism is:
-.Dq Li dir \&"\&" \&|more .
+.Sq Li dir\~\(dq\(dq\~|more .
 .It
-Failing the above checks, if
-.Dq globbing
+Failing the above checks, if globbing
 is enabled, local file names are expanded according to the rules
 used in the
 .Xr csh 1 ;
@@ -1932,7 +1983,7 @@ If the
 .Nm
 command expects a single local file (e.g.
 .Ic put  ) ,
-only the first filename generated by the "globbing" operation is used.
+only the first filename generated by the globbing operation is used.
 .It
 For
 .Ic mget
@@ -2133,7 +2184,7 @@ The
 .Xr editline 3
 library is configured with a
 .Pa .editrc
-file - refer to
+file \(em refer to
 .Xr editrc 5
 for more information.
 .Pp
@@ -2150,7 +2201,7 @@ By default, this is bound to the TAB key
 By default,
 .Nm
 displays a command line prompt of
-.Dq "ftp\*[Gt] "
+.Sq Li ftp>\~
 to the user.
 This can be changed with the
 .Ic "set prompt"
@@ -2167,42 +2218,42 @@ information:
 .It Li \&%/
 The current remote working directory.
 .\" %c[[0]n], %.[[0]n]
-.It \&%c Ns Oo Oo Li 0 Oc Ns Ar n Oc , Ns Li \&%. Ns Oo Oo Li 0 Oc Ns Ar n Oc
+.It Li \&%c Ns Oo Oo Li 0 Oc Ns Ar n Oc , Li \&%. Ns Oo Oo Li 0 Oc Ns Ar n Oc
 The trailing component of the current remote working directory, or
-.Em n
+.Ar n
 trailing components if a digit
-.Em n
+.Ar n
 is given.
 If
-.Em n
+.Ar n
 begins with
-.Sq 0 ,
+.Ql 0 ,
 the number of skipped components precede the trailing component(s) in
 the format
 .\" ``/<number>trailing''
 .Do
 .Sm off
-.Li / Li \*[Lt] Va number Li \*[Gt]
-.Va trailing
+.Li / Li < Ar number Li >
+.Ar trailing
 .Sm on
 .Dc
 (for
-.Sq \&%c )
+.Ql \&%c )
 or
 .\" ``...trailing''
-.Dq Li \&... Ns Va trailing
+.Dq Li \&... Ns Ar trailing
 (for
-.Sq \&%. ) .
+.Ql \&%. ) .
 .It Li \&%M
 The remote host name.
 .It Li \&%m
-The remote host name, up to the first
-.Sq \&. .
+The remote host name, up to the first dot
+.Ql \&. .
 .It Li \&%n
 The remote user name.
 .It Li \&%%
-A single
-.Sq % .
+A single percent character
+.Ql % .
 .El
 .Sh ENVIRONMENT
 .Nm
@@ -2213,7 +2264,7 @@ Password to send in an anonymous
 .Tn FTP
 transfer.
 Defaults to
-.Dq Li `whoami`@ .
+.Dq Li \&\`whoami\`@ .
 .It Ev FTPMODE
 Overrides the default operation mode.
 Support values are:
@@ -2234,14 +2285,13 @@ only
 .It Ev FTPPROMPT
 Command-line prompt to use.
 Defaults to
-.Dq "ftp\*[Gt] " .
+.Sq Li ftp>\~ .
 Refer to
 .Sx COMMAND LINE PROMPT
 for more information.
 .It Ev FTPRPROMPT
 Command-line right side prompt to use.
-Defaults to
-.Dq "" .
+Defaults to empty string.
 Refer to
 .Sx COMMAND LINE PROMPT
 for more information.
@@ -2254,9 +2304,9 @@ Port to use when connecting to gate-ftp 
 .Ic gate
 is enabled.
 Default is port returned by a
-.Fn getservbyname
+.Xr getservbyname 3
 lookup of
-.Dq ftpgate/tcp .
+.Dq Li ftpgate/tcp .
 .It Ev FTPUSERAGENT
 The value to send for the
 .Tn HTTP
@@ -2270,6 +2320,8 @@ file, if one exists.
 An alternate location of the
 .Pa .netrc
 file.
+.It Ev NO_CERT_VERIFY
+Don't verify SSL certificates.
 .It Ev PAGER
 Used by various commands to display files.
 Defaults to
@@ -2304,9 +2356,9 @@ If
 .Dq unsafe
 URL characters are required in the username or password
 (for example
-.Sq @
+.Ql @
 or
-.Sq / ) ,
+.Ql / ) ,
 encode them with
 .Li RFC 3986
 .Sq Li \&% Ns Ar XX
@@ -2323,10 +2375,22 @@ may be incompatible with other programs 
 .Em NOTE :
 this is not used for interactive sessions, only for command-line
 fetches.
+.It Ev https_proxy
+URL of
+.Tn HTTPS
+proxy to use when making
+.Tn HTTPS
+URL requests.
+.Pp
+See
+.Ev http_proxy
+for further notes about proxy use.
 .It Ev no_proxy
 A space or comma separated list of hosts (or domains) for which
 proxying is not to be used.
-Each entry may have an optional trailing ":port", which restricts
+Each entry may have an optional trailing
+.Sq Li \&: Ns Ar port ,
+which restricts
 the matching to connections to that port.
 .El
 .Sh EXTENDED PASSIVE MODE AND FIREWALLS

Index: src/usr.bin/ftp/ftp.c
diff -u src/usr.bin/ftp/ftp.c:1.167.6.2 src/usr.bin/ftp/ftp.c:1.167.6.3
--- src/usr.bin/ftp/ftp.c:1.167.6.2	Mon Sep 12 15:05:21 2022
+++ src/usr.bin/ftp/ftp.c	Mon Sep 12 17:08:13 2022
@@ -1,7 +1,7 @@
-/*	$NetBSD: ftp.c,v 1.167.6.2 2022/09/12 15:05:21 martin Exp $	*/
+/*	$NetBSD: ftp.c,v 1.167.6.3 2022/09/12 17:08:13 martin Exp $	*/
 
 /*-
- * Copyright (c) 1996-2009 The NetBSD Foundation, Inc.
+ * Copyright (c) 1996-2021 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This code is derived from software contributed to The NetBSD Foundation
@@ -92,7 +92,7 @@
 #if 0
 static char sccsid[] = "@(#)ftp.c	8.6 (Berkeley) 10/27/94";
 #else
-__RCSID("$NetBSD: ftp.c,v 1.167.6.2 2022/09/12 15:05:21 martin Exp $");
+__RCSID("$NetBSD: ftp.c,v 1.167.6.3 2022/09/12 17:08:13 martin Exp $");
 #endif
 #endif /* not lint */
 
@@ -280,6 +280,11 @@ hookup(const char *host, const char *por
 		goto bad;
 	}
 
+	if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
+			(void *)&on, sizeof(on)) == -1) {
+		DWARN("setsockopt %s (ignored)", "SO_KEEPALIVE");
+	}
+
 	if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE,
 			(void *)&on, sizeof(on)) == -1) {
 		DWARN("setsockopt %s (ignored)", "SO_OOBINLINE");
@@ -320,6 +325,17 @@ cmdtimeout(int notused)
 	errno = oerrno;
 }
 
+static int
+issighandler(sigfunc func)
+{
+	return (func != SIG_IGN &&
+		func != SIG_DFL &&
+#ifdef SIG_HOLD
+		func != SIG_HOLD &&
+#endif
+		func != SIG_ERR);
+}
+
 /*VARARGS*/
 int
 command(const char *fmt, ...)
@@ -359,8 +375,9 @@ command(const char *fmt, ...)
 	(void)fflush(cout);
 	cpend = 1;
 	r = getreply(!strcmp(fmt, "QUIT"));
-	if (abrtflag && oldsigint != SIG_IGN)
+	if (abrtflag && issighandler(oldsigint)) {
 		(*oldsigint)(SIGINT);
+	}
 	(void)xsignal(SIGINT, oldsigint);
 	return (r);
 }
@@ -510,11 +527,14 @@ getreply(int expecteof)
 		(void)xsignal(SIGALRM, oldsigalrm);
 		if (code == 421 || originalcode == 421)
 			lostpeer(0);
-		if (abrtflag && oldsigint != cmdabort && oldsigint != SIG_IGN)
+		if (abrtflag && oldsigint != cmdabort &&
+		    issighandler(oldsigint)) {
 			(*oldsigint)(SIGINT);
+		}
 		if (timeoutflag && oldsigalrm != cmdtimeout &&
-		    oldsigalrm != SIG_IGN)
+		    issighandler(oldsigalrm)) {
 			(*oldsigalrm)(SIGINT);
+		}
 		return (n - '0');
 	}
 }
@@ -578,7 +598,7 @@ abortxfer(int notused)
 
 /*
  * Read data from infd & write to outfd, using buf/bufsize as the temporary
- * buffer, dealing with short writes.
+ * buffer, dealing with short reads or writes.
  * If rate_limit != 0, rate-limit the transfer.
  * If hash_interval != 0, fputc('c', ttyout) every hash_interval bytes.
  * Updates global variables: bytes.
@@ -612,15 +632,25 @@ copy_bytes(int infd, int outfd, char *bu
 		bufrem = bufchunk;
 		while (bufrem > 0) {
 			inc = read(infd, buf, MIN((off_t)bufsize, bufrem));
-			if (inc <= 0)
+			if (inc < 0) {
+				if (errno == EINTR || errno == EAGAIN) {
+					continue;
+				}
+				goto copy_done;
+			} else if (inc == 0) {
 				goto copy_done;
+			}
 			bytes += inc;
 			bufrem -= inc;
 			bufp = buf;
 			while (inc > 0) {
 				outc = write(outfd, bufp, inc);
-				if (outc < 0)
+				if (outc < 0) {
+					if (errno == EINTR || errno == EAGAIN) {
+						continue;
+					}
 					goto copy_done;
+				}
 				inc -= outc;
 				bufp += outc;
 			}
@@ -670,7 +700,7 @@ sendrequest(const char *cmd, const char 
 	FILE *volatile dout;
 	int (*volatile closefunc)(FILE *);
 	sigfunc volatile oldintr;
-	sigfunc volatile oldintp;
+	sigfunc volatile oldpipe;
 	off_t volatile hashbytes;
 	int hash_interval;
 	const char *lmode;
@@ -697,8 +727,8 @@ sendrequest(const char *cmd, const char 
 	if (curtype != type)
 		changetype(type, 0);
 	closefunc = NULL;
-	oldintr = NULL;
-	oldintp = NULL;
+	oldintr = SIG_ERR;
+	oldpipe = SIG_ERR;
 	lmode = "w";
 	if (sigsetjmp(xferabort, 1)) {
 		while (cpend)
@@ -712,7 +742,7 @@ sendrequest(const char *cmd, const char 
 		fin = stdin;
 		progress = 0;
 	} else if (*local == '|') {
-		oldintp = xsignal(SIGPIPE, SIG_IGN);
+		oldpipe = xsignal(SIGPIPE, SIG_IGN);
 		fin = popen(local + 1, "r");
 		if (fin == NULL) {
 			warn("Can't execute `%s'", local + 1);
@@ -786,7 +816,9 @@ sendrequest(const char *cmd, const char 
 	}
 
 	progressmeter(-1);
-	oldintp = xsignal(SIGPIPE, SIG_IGN);
+	if (oldpipe == SIG_ERR) {
+		oldpipe = xsignal(SIGPIPE, SIG_IGN);
+	}
 	hash_interval = (hash && (!progress || filesize < 0)) ? mark : 0;
 
 	switch (curtype) {
@@ -855,7 +887,7 @@ sendrequest(const char *cmd, const char 
 
  abort:
 	(void)xsignal(SIGINT, oldintr);
-	oldintr = NULL;
+	oldintr = SIG_ERR;
 	if (!cpend) {
 		code = -1;
 		goto cleanupsend;
@@ -874,10 +906,10 @@ sendrequest(const char *cmd, const char 
 		ptransfer(0);
 
  cleanupsend:
-	if (oldintr)
+	if (oldintr != SIG_ERR)
 		(void)xsignal(SIGINT, oldintr);
-	if (oldintp)
-		(void)xsignal(SIGPIPE, oldintp);
+	if (oldpipe != SIG_ERR)
+		(void)xsignal(SIGPIPE, oldpipe);
 	if (data >= 0) {
 		(void)close(data);
 		data = -1;
@@ -899,7 +931,7 @@ recvrequest(const char *cmd, const char 
 	FILE *volatile din;
 	int (*volatile closefunc)(FILE *);
 	sigfunc volatile oldintr;
-	sigfunc volatile oldintp;
+	sigfunc volatile oldpipe;
 	int c, d;
 	int volatile is_retr;
 	int volatile tcrflag;
@@ -935,8 +967,8 @@ recvrequest(const char *cmd, const char 
 		return;
 	}
 	closefunc = NULL;
-	oldintr = NULL;
-	oldintp = NULL;
+	oldintr = SIG_ERR;
+	oldpipe = SIG_ERR;
 	tcrflag = !crflag && is_retr;
 	if (sigsetjmp(xferabort, 1)) {
 		while (cpend)
@@ -1017,7 +1049,7 @@ recvrequest(const char *cmd, const char 
 		progress = 0;
 		preserve = 0;
 	} else if (!ignorespecial && *local == '|') {
-		oldintp = xsignal(SIGPIPE, SIG_IGN);
+		oldpipe = xsignal(SIGPIPE, SIG_IGN);
 		fout = popen(local + 1, "w");
 		if (fout == NULL) {
 			warn("Can't execute `%s'", local+1);
@@ -1183,10 +1215,10 @@ recvrequest(const char *cmd, const char 
 		ptransfer(0);
 
  cleanuprecv:
-	if (oldintr)
+	if (oldintr != SIG_ERR)
 		(void)xsignal(SIGINT, oldintr);
-	if (oldintp)
-		(void)xsignal(SIGPIPE, oldintp);
+	if (oldpipe != SIG_ERR)
+		(void)xsignal(SIGPIPE, oldpipe);
 	if (data >= 0) {
 		(void)close(data);
 		data = -1;
@@ -1349,13 +1381,11 @@ initconn(void)
 			if (data_addr.su_family != AF_INET) {
 				fputs(
     "Passive mode AF mismatch. Shouldn't happen!\n", ttyout);
-				error = 1;
 				goto bad;
 			}
 			if (code / 10 == 22 && code != 227) {
 				fputs("wrong server: return code must be 227\n",
 					ttyout);
-				error = 1;
 				goto bad;
 			}
 			error = sscanf(pasv, "%u,%u,%u,%u,%u,%u",
@@ -1364,21 +1394,24 @@ initconn(void)
 			if (error != 6) {
 				fputs(
 "Passive mode address scan failure. Shouldn't happen!\n", ttyout);
-				error = 1;
 				goto bad;
 			}
-			error = 0;
 			memset(&data_addr, 0, sizeof(data_addr));
 			data_addr.su_family = AF_INET;
 			data_addr.su_len = sizeof(struct sockaddr_in);
 			data_addr.si_su.su_sin.sin_addr.s_addr =
 			    htonl(pack4(addr, 0));
 			data_addr.su_port = htons(pack2(port, 0));
+			if (data_addr.si_su.su_sin.sin_addr.s_addr !=
+			    hisctladdr.si_su.su_sin.sin_addr.s_addr) {
+				fputs("Passive mode address mismatch.\n",
+				    ttyout);
+				goto bad;
+			}
 		} else if (strcmp(pasvcmd, "LPSV") == 0) {
 			if (code / 10 == 22 && code != 228) {
 				fputs("wrong server: return code must be 228\n",
 					ttyout);
-				error = 1;
 				goto bad;
 			}
 			switch (data_addr.su_family) {
@@ -1391,23 +1424,26 @@ initconn(void)
 				if (error != 9) {
 					fputs(
 "Passive mode address scan failure. Shouldn't happen!\n", ttyout);
-					error = 1;
 					goto bad;
 				}
 				if (af != 4 || hal != 4 || pal != 2) {
 					fputs(
 "Passive mode AF mismatch. Shouldn't happen!\n", ttyout);
-					error = 1;
 					goto bad;
 				}
 
-				error = 0;
 				memset(&data_addr, 0, sizeof(data_addr));
 				data_addr.su_family = AF_INET;
 				data_addr.su_len = sizeof(struct sockaddr_in);
 				data_addr.si_su.su_sin.sin_addr.s_addr =
 				    htonl(pack4(addr, 0));
 				data_addr.su_port = htons(pack2(port, 0));
+				if (data_addr.si_su.su_sin.sin_addr.s_addr !=
+				    hisctladdr.si_su.su_sin.sin_addr.s_addr) {
+					fputs("Passive mode address mismatch.\n",
+					    ttyout);
+					goto bad;
+				}
 				break;
 #ifdef INET6
 			case AF_INET6:
@@ -1423,17 +1459,14 @@ initconn(void)
 				if (error != 21) {
 					fputs(
 "Passive mode address scan failure. Shouldn't happen!\n", ttyout);
-					error = 1;
 					goto bad;
 				}
 				if (af != 6 || hal != 16 || pal != 2) {
 					fputs(
 "Passive mode AF mismatch. Shouldn't happen!\n", ttyout);
-					error = 1;
 					goto bad;
 				}
 
-				error = 0;
 				memset(&data_addr, 0, sizeof(data_addr));
 				data_addr.su_family = AF_INET6;
 				data_addr.su_len = sizeof(struct sockaddr_in6);
@@ -1445,10 +1478,19 @@ initconn(void)
 				}
 			    }
 				data_addr.su_port = htons(pack2(port, 0));
+				if (memcmp(
+				    &data_addr.si_su.su_sin6.sin6_addr,
+				    &hisctladdr.si_su.su_sin6.sin6_addr,
+				    sizeof(data_addr.si_su.su_sin6.sin6_addr))) {
+					fputs("Passive mode address mismatch.\n",
+					    ttyout);
+					goto bad;
+				}
 				break;
 #endif
 			default:
-				error = 1;
+				fputs("Unknown passive mode AF.\n", ttyout);
+				goto bad;
 			}
 		} else if (strcmp(pasvcmd, "EPSV") == 0) {
 			char delim[4];
@@ -1457,20 +1499,17 @@ initconn(void)
 			if (code / 10 == 22 && code != 229) {
 				fputs("wrong server: return code must be 229\n",
 					ttyout);
-				error = 1;
 				goto bad;
 			}
 			if (sscanf(pasv, "%c%c%c%d%c", &delim[0],
 					&delim[1], &delim[2], &port[1],
 					&delim[3]) != 5) {
 				fputs("parse error!\n", ttyout);
-				error = 1;
 				goto bad;
 			}
 			if (delim[0] != delim[1] || delim[0] != delim[2]
 			 || delim[0] != delim[3]) {
 				fputs("parse error!\n", ttyout);
-				error = 1;
 				goto bad;
 			}
 			data_addr = hisctladdr;
@@ -1553,8 +1592,8 @@ initconn(void)
 				result = COMPLETE + 1;
 				break;
 			}
-			/* FALLTHROUGH */
 #ifdef INET6
+			/* FALLTHROUGH */
 		case AF_INET6:
 			if (!epsv6 || epsv6bad) {
 				result = COMPLETE + 1;
@@ -1856,7 +1895,7 @@ proxtrans(const char *cmd, const char *l
 	int volatile secndflag;
 	const char *volatile cmd2;
 
-	oldintr = NULL;
+	oldintr = SIG_ERR;
 	secndflag = 0;
 	if (strcmp(cmd, "RETR"))
 		cmd2 = "RETR";
@@ -2047,7 +2086,7 @@ gunique(const char *local)
  *	needs to get back to a known state.
  */
 static void
-abort_squared(int dummy)
+abort_squared(int signo)
 {
 	char msgbuf[100];
 	size_t len;
@@ -2057,7 +2096,7 @@ abort_squared(int dummy)
 	len = strlcpy(msgbuf, "\nremote abort aborted; closing connection.\n",
 	    sizeof(msgbuf));
 	write(fileno(ttyout), msgbuf, len);
-	lostpeer(0);
+	lostpeer(signo);
 	siglongjmp(xferabort, 1);
 }
 

Index: src/usr.bin/ftp/ftp_var.h
diff -u src/usr.bin/ftp/ftp_var.h:1.84.8.2 src/usr.bin/ftp/ftp_var.h:1.84.8.3
--- src/usr.bin/ftp/ftp_var.h:1.84.8.2	Mon Sep 12 15:05:21 2022
+++ src/usr.bin/ftp/ftp_var.h	Mon Sep 12 17:08:13 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: ftp_var.h,v 1.84.8.2 2022/09/12 15:05:21 martin Exp $	*/
+/*	$NetBSD: ftp_var.h,v 1.84.8.3 2022/09/12 17:08:13 martin Exp $	*/
 
 /*-
  * Copyright (c) 1996-2009 The NetBSD Foundation, Inc.
@@ -269,6 +269,7 @@ GLOBAL	int	unix_server;	/* server is uni
 GLOBAL	int	unix_proxy;	/* proxy is unix, can use binary for ascii */
 GLOBAL	char	localcwd[MAXPATHLEN];	/* local dir */
 GLOBAL	char	remotecwd[MAXPATHLEN];	/* remote dir */
+GLOBAL	int	remcwdvalid;		/* remotecwd has been updated */
 GLOBAL	char   *username;	/* name of user logged in as. (dynamic) */
 
 GLOBAL	sa_family_t family;	/* address family to use for connections */
@@ -340,7 +341,7 @@ extern	struct option	optiontab[];
 #define DPRINTF(...)	(void)0
 #define DWARN(...)	(void)0
 #else
-#define DWFTP(a)	do a; while (/*CONSTCOND*/0)
+#define DWFTP(a)	do a; while (0)
 #define DPRINTF(...)	DWFTP(if (ftp_debug) (void)fprintf(ttyout, __VA_ARGS__))
 #define DWARN(...)	DWFTP(if (ftp_debug) warn(__VA_ARGS__))
 #endif

Index: src/usr.bin/ftp/main.c
diff -u src/usr.bin/ftp/main.c:1.123.8.2 src/usr.bin/ftp/main.c:1.123.8.3
--- src/usr.bin/ftp/main.c:1.123.8.2	Mon Sep 12 15:05:21 2022
+++ src/usr.bin/ftp/main.c	Mon Sep 12 17:08:13 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: main.c,v 1.123.8.2 2022/09/12 15:05:21 martin Exp $	*/
+/*	$NetBSD: main.c,v 1.123.8.3 2022/09/12 17:08:13 martin Exp $	*/
 
 /*-
  * Copyright (c) 1996-2015 The NetBSD Foundation, Inc.
@@ -98,7 +98,7 @@ __COPYRIGHT("@(#) Copyright (c) 1985, 19
 #if 0
 static char sccsid[] = "@(#)main.c	8.6 (Berkeley) 10/9/94";
 #else
-__RCSID("$NetBSD: main.c,v 1.123.8.2 2022/09/12 15:05:21 martin Exp $");
+__RCSID("$NetBSD: main.c,v 1.123.8.3 2022/09/12 17:08:13 martin Exp $");
 #endif
 #endif /* not lint */
 
@@ -130,7 +130,8 @@ __RCSID("$NetBSD: main.c,v 1.123.8.2 202
 #define	NO_PROXY	"no_proxy"	/* env var with list of non-proxied
 					 * hosts, comma or space separated */
 
-__dead static void	usage(void);
+static int	usage(void);
+static int	usage_help(void);
 static void	setupoption(const char *, const char *, const char *);
 
 int
@@ -266,7 +267,7 @@ main(int volatile argc, char **volatile 
 		}
 	}
 
-	while ((ch = getopt(argc, argv, "46AadefginN:o:pP:q:r:Rs:tT:u:vVx:")) != -1) {
+	while ((ch = getopt(argc, argv, ":46AadefginN:o:pP:q:r:Rs:tT:u:vVx:")) != -1) {
 		switch (ch) {
 		case '4':
 			family = AF_INET;
@@ -324,7 +325,7 @@ main(int volatile argc, char **volatile 
 			break;
 
 		case 'o':
-			outfile = optarg;
+			outfile = ftp_strdup(optarg);
 			if (strcmp(outfile, "-") == 0)
 				ttyout = stderr;
 			break;
@@ -378,15 +379,15 @@ main(int volatile argc, char **volatile 
 				if (*cp == '\0') {
 					warnx("Bad throttle value `%s'",
 					    optarg);
-					usage();
-					/* NOTREACHED */
+					return usage();
 				}
 				targv[targc++] = cp;
 				if (targc >= 5)
 					break;
 			}
-			if (parserate(targc, targv, 1) == -1)
-				usage();
+			if (parserate(targc, targv, 1) == -1) {
+				return usage();
+			}
 			free(oac);
 			break;
 		}
@@ -415,8 +416,19 @@ main(int volatile argc, char **volatile 
 			rcvbuf_size = sndbuf_size;
 			break;
 
+		case '?':
+			if (optopt == '?') {
+				return usage_help();
+			}
+			warnx("-%c: unknown option", optopt);
+			return usage();
+
+		case ':':
+			warnx("-%c: missing argument", optopt);
+			return usage();
+
 		default:
-			usage();
+			errx(1, "unimplemented option -%c", ch);
 		}
 	}
 			/* set line buffering on ttyout */
@@ -464,7 +476,6 @@ main(int volatile argc, char **volatile 
 		if (localhome == NULL && !EMPTYSTRING(pw->pw_dir))
 			localhome = ftp_strdup(pw->pw_dir);
 		localname = ftp_strdup(pw->pw_name);
-		anonuser = localname;
 	}
 	if (netrc[0] == '\0' && localhome != NULL) {
 		if (strlcpy(netrc, localhome, sizeof(netrc)) >= sizeof(netrc) ||
@@ -573,8 +584,9 @@ main(int volatile argc, char **volatile 
 			retry_connect = 0; /* connected, stop hiding msgs */
 		}
 	}
-	if (isupload)
-		usage();
+	if (isupload) {
+		return usage();
+	}
 
 #ifndef NO_EDITCOMPLETE
 	controlediting();
@@ -661,7 +673,7 @@ cmdscanner(void)
 			case -2:	/* error */
 				if (fromatty)
 					putc('\n', ttyout);
-				quit(0, NULL);
+				justquit();
 				/* NOTREACHED */
 			case -3:	/* too long; try again */
 				fputs("Sorry, input line is too long.\n",
@@ -683,7 +695,7 @@ cmdscanner(void)
 			if (buf == NULL || num == 0) {
 				if (fromatty)
 					putc('\n', ttyout);
-				quit(0, NULL);
+				justquit();
 			}
 			if (num >= sizeof(line)) {
 				fputs("Sorry, input line is too long.\n",
@@ -837,7 +849,6 @@ slurpstring(void)
 				slrflag++;
 				INC_CHKCURSOR(stringbase);
 				return ((*sb == '!') ? bangstr : dollarstr);
-				/* NOTREACHED */
 			case 1:
 				slrflag++;
 				altarg = stringbase;
@@ -966,7 +977,7 @@ help(int argc, char *argv[])
 	cmd = argv[0];
 	isusage = (strcmp(cmd, "usage") == 0);
 	if (argc == 0 || (isusage && argc == 1)) {
-		UPRINTF("usage: %s [command [...]]\n", cmd);
+		UPRINTF("usage: %s [command ...]\n", cmd);
 		return;
 	}
 	if (argc == 1) {
@@ -1045,20 +1056,69 @@ setupoption(const char *name, const char
 	set_option(name, value ? value : defaultvalue, 0);
 }
 
-void
-usage(void)
+static void
+synopsis(FILE * stream)
 {
-	const char *progname = getprogname();
+	const char * progname = getprogname();
 
-	(void)fprintf(stderr,
-"usage: %s [-46AadefginpRtVv] [-N netrc] [-o outfile] [-P port] [-q quittime]\n"
-"           [-r retry] [-s srcaddr] [-T dir,max[,inc]] [-x xferbufsize]\n"
-"           [[user@]host [port]] [host:path[/]] [file:///file]\n"
-"           [ftp://[user[:pass]@]host[:port]/path[/]]\n";
-"           [http://[user[:pass]@]host[:port]/path] [...]\n"
+	fprintf(stream,
+"usage: %s [-46AadefginpRtVv] [-N NETRC] [-o OUTPUT] [-P PORT] [-q QUITTIME]\n"
+"           [-r RETRY] [-s SRCADDR] [-T DIR,MAX[,INC]] [-x XFERSIZE]\n"
+"           [[USER@]HOST [PORT]]\n"
+"           [[USER@]HOST:[PATH][/]]\n"
+"           [file:///PATH]\n"
+"           [ftp://[USER[:PASSWORD]@]HOST[:PORT]/PATH[/][;type=TYPE]]\n";
+"           [http://[USER[:PASSWORD]@]HOST[:PORT]/PATH]\n";
 #ifdef WITH_SSL
-"           [https://[user[:pass]@]host[:port]/path] [...]\n"
+"           [https://[USER[:PASSWORD]@]HOST[:PORT]/PATH]\n";
 #endif
-"       %s -u URL file [...]\n", progname, progname);
-	exit(1);
+"           ...\n"
+"       %s -u URL FILE ...\n"
+"       %s -?\n",
+		progname, progname, progname);
+}
+
+static int
+usage_help(void)
+{
+	synopsis(stdout);
+#ifndef NO_USAGE
+	printf(
+"  -4            Only use IPv4 addresses\n"
+"  -6            Only use IPv6 addresses\n"
+"  -A            Force active mode\n"
+"  -a            Use anonymous login\n"
+"  -d            Enable debugging\n"
+"  -e            Disable command-line editing\n"
+"  -f            Force cache reload for FTP or HTTP proxy transfers\n"
+"  -g            Disable file name globbing\n"
+"  -i            Disable interactive prompt during multiple file transfers\n"
+"  -N NETRC      Use NETRC instead of ~/.netrc\n"
+"  -n            Disable auto-login\n"
+"  -o OUTPUT     Save auto-fetched files to OUTPUT\n"
+"  -P PORT       Use port PORT\n"
+"  -p            Force passive mode\n"
+"  -q QUITTIME   Quit if connection stalls for QUITTIME seconds\n"
+"  -R            Restart non-proxy auto-fetch\n"
+"  -r RETRY      Retry failed connection attempts after RETRY seconds\n"
+"  -s SRCADDR    Use source address SRCADDR\n"
+"  -t            Enable packet tracing\n"
+"  -T DIR,MAX[,INC]\n"
+"                Set maximum transfer rate for direction DIR to MAX bytes/s,\n"
+"                with optional increment INC bytes/s\n"
+"  -u URL        URL to upload file arguments to\n"
+"  -V            Disable verbose and progress\n"
+"  -v            Enable verbose and progress\n"
+"  -x XFERSIZE   Set socket send and receive size to XFERSIZE\n"
+"  -?            Display this help and exit\n"
+		);
+#endif
+	return EXIT_SUCCESS;
+}
+
+static int
+usage(void)
+{
+	synopsis(stderr);
+	return EXIT_FAILURE;
 }

Index: src/usr.bin/ftp/progressbar.c
diff -u src/usr.bin/ftp/progressbar.c:1.22.24.2 src/usr.bin/ftp/progressbar.c:1.22.24.3
--- src/usr.bin/ftp/progressbar.c:1.22.24.2	Mon Sep 12 15:05:21 2022
+++ src/usr.bin/ftp/progressbar.c	Mon Sep 12 17:08:13 2022
@@ -1,7 +1,7 @@
-/*	$NetBSD: progressbar.c,v 1.22.24.2 2022/09/12 15:05:21 martin Exp $	*/
+/*	$NetBSD: progressbar.c,v 1.22.24.3 2022/09/12 17:08:13 martin Exp $	*/
 
 /*-
- * Copyright (c) 1997-2009 The NetBSD Foundation, Inc.
+ * Copyright (c) 1997-2021 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This code is derived from software contributed to The NetBSD Foundation
@@ -31,7 +31,7 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID("$NetBSD: progressbar.c,v 1.22.24.2 2022/09/12 15:05:21 martin Exp $");
+__RCSID("$NetBSD: progressbar.c,v 1.22.24.3 2022/09/12 17:08:13 martin Exp $");
 #endif /* not lint */
 
 /*
@@ -87,6 +87,7 @@ updateprogressmeter(int dummy)
 /*
  * List of order of magnitude suffixes, per IEC 60027-2.
  */
+#if !defined(NO_PROGRESS) || !defined(STANDALONE_PROGRESS)
 static const char * const suffixes[] = {
 	"",	/* 2^0  (byte) */
 	"KiB",	/* 2^10 Kibibyte */
@@ -102,6 +103,7 @@ static const char * const suffixes[] = {
 #endif
 };
 #define NSUFFIXES	(int)(sizeof(suffixes) / sizeof(suffixes[0]))
+#endif
 
 /*
  * Display a transfer progress bar if progress is non-zero.
@@ -139,8 +141,10 @@ progressmeter(int flag)
 			 *	these appropriately.
 			 */
 #endif
+#if !defined(NO_PROGRESS) || !defined(STANDALONE_PROGRESS)
 	size_t		len;
 	char		buf[256];	/* workspace for progress bar */
+#endif
 #ifndef NO_PROGRESS
 #define	BAROVERHEAD	45		/* non `*' portion of progress bar */
 					/*
@@ -189,7 +193,7 @@ progressmeter(int flag)
 	if (quit_time > 0 || progress) {
 #endif /* !STANDALONE_PROGRESS */
 		if (flag == -1) {
-			(void)xsignal_restart(SIGALRM, updateprogressmeter, 1);
+			(void)xsignal(SIGALRM, updateprogressmeter);
 			alarmtimer(1);		/* set alarm timer for 1 Hz */
 		} else if (flag == 1) {
 			alarmtimer(0);
@@ -400,73 +404,21 @@ alarmtimer(int wait)
 	setitimer(ITIMER_REAL, &itv, NULL);
 }
 
-
 /*
- * Install a POSIX signal handler, allowing the invoker to set whether
- * the signal should be restartable or not
+ * Install a non-restartable POSIX signal handler.
  */
 sigfunc
-xsignal_restart(int sig, sigfunc func, int restartable)
+xsignal(int sig, sigfunc func)
 {
 	struct sigaction act, oact;
 	act.sa_handler = func;
 
 	sigemptyset(&act.sa_mask);
-#if defined(SA_RESTART)			/* 4.4BSD, Posix(?), SVR4 */
-	act.sa_flags = restartable ? SA_RESTART : 0;
-#elif defined(SA_INTERRUPT)		/* SunOS 4.x */
-	act.sa_flags = restartable ? 0 : SA_INTERRUPT;
-#else
-#error "system must have SA_RESTART or SA_INTERRUPT"
+	act.sa_flags = 0;
+#if defined(SA_INTERRUPT)		/* SunOS 4.x */
+	act.sa_flags = SA_INTERRUPT;
 #endif
 	if (sigaction(sig, &act, &oact) < 0)
 		return (SIG_ERR);
 	return (oact.sa_handler);
 }
-
-/*
- * Install a signal handler with the `restartable' flag set dependent upon
- * which signal is being set. (This is a wrapper to xsignal_restart())
- */
-sigfunc
-xsignal(int sig, sigfunc func)
-{
-	int restartable;
-
-	/*
-	 * Some signals print output or change the state of the process.
-	 * There should be restartable, so that reads and writes are
-	 * not affected.  Some signals should cause program flow to change;
-	 * these signals should not be restartable, so that the system call
-	 * will return with EINTR, and the program will go do something
-	 * different.  If the signal handler calls longjmp() or siglongjmp(),
-	 * it doesn't matter if it's restartable.
-	 */
-
-	switch(sig) {
-#ifdef SIGINFO
-	case SIGINFO:
-#endif
-	case SIGQUIT:
-	case SIGUSR1:
-	case SIGUSR2:
-	case SIGWINCH:
-		restartable = 1;
-		break;
-
-	case SIGALRM:
-	case SIGINT:
-	case SIGPIPE:
-		restartable = 0;
-		break;
-
-	default:
-		/*
-		 * This is unpleasant, but I don't know what would be better.
-		 * Right now, this "can't happen"
-		 */
-		errx(1, "xsignal_restart: called with signal %d", sig);
-	}
-
-	return(xsignal_restart(sig, func, restartable));
-}

Index: src/usr.bin/ftp/progressbar.h
diff -u src/usr.bin/ftp/progressbar.h:1.8.38.2 src/usr.bin/ftp/progressbar.h:1.8.38.3
--- src/usr.bin/ftp/progressbar.h:1.8.38.2	Mon Sep 12 15:05:21 2022
+++ src/usr.bin/ftp/progressbar.h	Mon Sep 12 17:08:13 2022
@@ -1,7 +1,7 @@
-/*	$NetBSD: progressbar.h,v 1.8.38.2 2022/09/12 15:05:21 martin Exp $	*/
+/*	$NetBSD: progressbar.h,v 1.8.38.3 2022/09/12 17:08:13 martin Exp $	*/
 
 /*-
- * Copyright (c) 1996-2009 The NetBSD Foundation, Inc.
+ * Copyright (c) 1996-2021 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This code is derived from software contributed to The NetBSD Foundation
@@ -68,7 +68,6 @@ int	foregroundproc(void);
 void	alarmtimer(int);
 void	progressmeter(int);
 sigfunc	xsignal(int, sigfunc);
-sigfunc	xsignal_restart(int, sigfunc, int);
 
 #ifndef STANDALONE_PROGRESS
 void	psummary(int);

Index: src/usr.bin/ftp/ssl.c
diff -u src/usr.bin/ftp/ssl.c:1.5.8.2 src/usr.bin/ftp/ssl.c:1.5.8.3
--- src/usr.bin/ftp/ssl.c:1.5.8.2	Mon Sep 12 15:05:21 2022
+++ src/usr.bin/ftp/ssl.c	Mon Sep 12 17:08:13 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: ssl.c,v 1.5.8.2 2022/09/12 15:05:21 martin Exp $	*/
+/*	$NetBSD: ssl.c,v 1.5.8.3 2022/09/12 17:08:13 martin Exp $	*/
 
 /*-
  * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav
@@ -34,12 +34,17 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID("$NetBSD: ssl.c,v 1.5.8.2 2022/09/12 15:05:21 martin Exp $");
+__RCSID("$NetBSD: ssl.c,v 1.5.8.3 2022/09/12 17:08:13 martin Exp $");
 #endif
 
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <time.h>
 #include <unistd.h>
-#include <fcntl.h>
 
 #include <sys/param.h>
 #include <sys/select.h>
@@ -47,11 +52,14 @@ __RCSID("$NetBSD: ssl.c,v 1.5.8.2 2022/0
 
 #include <netinet/tcp.h>
 #include <netinet/in.h>
+
+#ifdef WITH_SSL
 #include <openssl/crypto.h>
 #include <openssl/x509.h>
 #include <openssl/pem.h>
 #include <openssl/ssl.h>
 #include <openssl/err.h>
+#endif
 
 #include "ssl.h"
 
@@ -74,7 +82,9 @@ struct fetch_connect {
 	int 			 issock;
 	int			 iserr;
 	int			 iseof;
+#ifdef WITH_SSL
 	SSL			*ssl;		/* SSL handle */
+#endif
 };
 
 /*
@@ -87,6 +97,7 @@ fetch_writev(struct fetch_connect *conn,
 	struct timeval now, timeout, delta;
 	fd_set writefds;
 	ssize_t len, total;
+	int fd = conn->sd;
 	int r;
 
 	if (quit_time > 0) {
@@ -97,8 +108,8 @@ fetch_writev(struct fetch_connect *conn,
 
 	total = 0;
 	while (iovcnt > 0) {
-		while (quit_time > 0 && !FD_ISSET(conn->sd, &writefds)) {
-			FD_SET(conn->sd, &writefds);
+		while (quit_time > 0 && !FD_ISSET(fd, &writefds)) {
+			FD_SET(fd, &writefds);
 			gettimeofday(&now, NULL);
 			delta.tv_sec = timeout.tv_sec - now.tv_sec;
 			delta.tv_usec = timeout.tv_usec - now.tv_usec;
@@ -111,7 +122,7 @@ fetch_writev(struct fetch_connect *conn,
 				return -1;
 			}
 			errno = 0;
-			r = select(conn->sd + 1, NULL, &writefds, NULL, &delta);
+			r = select(fd + 1, NULL, &writefds, NULL, &delta);
 			if (r == -1) {
 				if (errno == EINTR)
 					continue;
@@ -119,10 +130,12 @@ fetch_writev(struct fetch_connect *conn,
 			}
 		}
 		errno = 0;
+#ifdef WITH_SSL
 		if (conn->ssl != NULL)
 			len = SSL_write(conn->ssl, iov->iov_base, iov->iov_len);
 		else
-			len = writev(conn->sd, iov, iovcnt);
+#endif
+			len = writev(fd, iov, iovcnt);
 		if (len == 0) {
 			/* we consider a short write a failure */
 			/* XXX perhaps we shouldn't in the SSL case */
@@ -130,7 +143,7 @@ fetch_writev(struct fetch_connect *conn,
 			return -1;
 		}
 		if (len < 0) {
-			if (errno == EINTR)
+			if (errno == EINTR || errno == EAGAIN)
 				continue;
 			return -1;
 		}
@@ -148,11 +161,8 @@ fetch_writev(struct fetch_connect *conn,
 	return total;
 }
 
-/*
- * Write to a connection w/ timeout
- */
-static int
-fetch_write(struct fetch_connect *conn, const char *str, size_t len)
+static ssize_t
+fetch_write(const void *str, size_t len, struct fetch_connect *conn)
 {
 	struct iovec iov[1];
 
@@ -181,7 +191,7 @@ fetch_printf(struct fetch_connect *conn,
 		return -1;
 	}
 
-	r = fetch_write(conn, msg, len);
+	r = fetch_write(msg, len, conn);
 	free(msg);
 	return r;
 }
@@ -210,15 +220,16 @@ fetch_clearerr(struct fetch_connect *con
 int
 fetch_flush(struct fetch_connect *conn)
 {
-	int v;
 
 	if (conn->issock) {
+		int fd = conn->sd;
+		int v;
 #ifdef TCP_NOPUSH
 		v = 0;
-		setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &v, sizeof(v));
+		setsockopt(fd, IPPROTO_TCP, TCP_NOPUSH, &v, sizeof(v));
 #endif
 		v = 1;
-		setsockopt(conn->sd, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v));
+		setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v));
 	}
 	return 0;
 }
@@ -271,44 +282,44 @@ fetch_fdopen(int sd, const char *fmode)
 int
 fetch_close(struct fetch_connect *conn)
 {
-	int rv = 0;
+	if (conn == NULL)
+		return 0;
 
-	if (conn != NULL) {
-		fetch_flush(conn);
-		SSL_free(conn->ssl);
-		rv = close(conn->sd);
-		if (rv < 0) {
-			errno = rv;
-			rv = EOF;
-		}
-		free(conn->cache.buf);
-		free(conn->buf);
-		free(conn);
-	}
-	return rv;
+	fetch_flush(conn);
+#ifdef WITH_SSL
+	SSL_free(conn->ssl);
+#endif
+	close(conn->sd);
+	free(conn->cache.buf);
+	free(conn->buf);
+	free(conn);
+	return 0;
 }
 
+#define FETCH_WRITE_WAIT	-3
 #define FETCH_READ_WAIT		-2
 #define FETCH_READ_ERROR	-1
 
+#ifdef WITH_SSL
 static ssize_t
 fetch_ssl_read(SSL *ssl, void *buf, size_t len)
 {
 	ssize_t rlen;
-	int ssl_err;
-
 	rlen = SSL_read(ssl, buf, len);
-	if (rlen < 0) {
-		ssl_err = SSL_get_error(ssl, rlen);
-		if (ssl_err == SSL_ERROR_WANT_READ ||
-		    ssl_err == SSL_ERROR_WANT_WRITE) {
-			return FETCH_READ_WAIT;
-		}
+	if (rlen >= 0)
+		return rlen;
+
+	switch (SSL_get_error(ssl, rlen)) {
+	case SSL_ERROR_WANT_READ:
+		return FETCH_READ_WAIT;
+	case SSL_ERROR_WANT_WRITE:
+		return FETCH_WRITE_WAIT;
+	default:
 		ERR_print_errors_fp(ttyout);
 		return FETCH_READ_ERROR;
 	}
-	return rlen;
 }
+#endif /* WITH_SSL */
 
 static ssize_t
 fetch_nonssl_read(int sd, void *buf, size_t len)
@@ -316,7 +327,7 @@ fetch_nonssl_read(int sd, void *buf, siz
 	ssize_t rlen;
 
 	rlen = read(sd, buf, len);
-	if (rlen < 0) {
+	if (rlen == -1) {
 		if (errno == EAGAIN || errno == EINTR)
 			return FETCH_READ_WAIT;
 		return FETCH_READ_ERROR;
@@ -347,14 +358,49 @@ fetch_cache_data(struct fetch_connect *c
 	return 0;
 }
 
-ssize_t
+static int
+fetch_wait(struct fetch_connect *conn, ssize_t rlen, struct timeval *timeout)
+{
+	struct timeval now, delta;
+	int fd = conn->sd;
+	fd_set fds;
+
+	FD_ZERO(&fds);
+	while (!FD_ISSET(fd, &fds)) {
+		FD_SET(fd, &fds);
+		if (quit_time > 0) {
+			gettimeofday(&now, NULL);
+			if (!timercmp(timeout, &now, >)) {
+				fprintf(ttyout, "\r\n%s: transfer aborted"
+				    " because stalled for %lu sec.\r\n",
+				    getprogname(), (unsigned long)quit_time);
+				errno = ETIMEDOUT;
+				conn->iserr = ETIMEDOUT;
+				return -1;
+			}
+			timersub(timeout, &now, &delta);
+		}
+		errno = 0;
+		if (select(fd + 1,
+			rlen == FETCH_READ_WAIT ? &fds : NULL,
+			rlen == FETCH_WRITE_WAIT ? &fds : NULL,
+			NULL, quit_time > 0 ? &delta : NULL) < 0) {
+			if (errno == EINTR)
+				continue;
+			conn->iserr = errno;
+			return -1;
+		}
+	}
+	return 0;
+}
+
+size_t
 fetch_read(void *ptr, size_t size, size_t nmemb, struct fetch_connect *conn)
 {
-	struct timeval now, timeout, delta;
-	fd_set readfds;
 	ssize_t rlen, total;
 	size_t len;
 	char *start, *buf;
+	struct timeval timeout;
 
 	if (quit_time > 0) {
 		gettimeofday(&timeout, NULL);
@@ -402,40 +448,31 @@ fetch_read(void *ptr, size_t size, size_
 		 * In the non-SSL case, it may improve performance (very
 		 * slightly) when reading small amounts of data.
 		 */
+#ifdef WITH_SSL
 		if (conn->ssl != NULL)
 			rlen = fetch_ssl_read(conn->ssl, buf, len);
 		else
+#endif
 			rlen = fetch_nonssl_read(conn->sd, buf, len);
-		if (rlen == 0) {
+		switch (rlen) {
+		case 0:
+			conn->iseof = 1;
+			return total;
+		case FETCH_READ_ERROR:
+			conn->iserr = errno;
+			if (errno == EINTR)
+				fetch_cache_data(conn, start, total);
+			return 0;
+		case FETCH_READ_WAIT:
+		case FETCH_WRITE_WAIT:
+			if (fetch_wait(conn, rlen, &timeout) == -1)
+				return 0;
 			break;
-		} else if (rlen > 0) {
+		default:
 			len -= rlen;
 			buf += rlen;
 			total += rlen;
-			continue;
-		} else if (rlen == FETCH_READ_ERROR) {
-			if (errno == EINTR)
-				fetch_cache_data(conn, start, total);
-			return -1;
-		}
-		FD_ZERO(&readfds);
-		while (!FD_ISSET(conn->sd, &readfds)) {
-			FD_SET(conn->sd, &readfds);
-			if (quit_time > 0) {
-				gettimeofday(&now, NULL);
-				if (!timercmp(&timeout, &now, >)) {
-					errno = ETIMEDOUT;
-					return -1;
-				}
-				timersub(&timeout, &now, &delta);
-			}
-			errno = 0;
-			if (select(conn->sd + 1, &readfds, NULL, NULL,
-				quit_time > 0 ? &delta : NULL) < 0) {
-				if (errno == EINTR)
-					continue;
-				return -1;
-			}
+			break;
 		}
 	}
 	return total;
@@ -450,7 +487,7 @@ char *
 fetch_getln(char *str, int size, struct fetch_connect *conn)
 {
 	size_t tmpsize;
-	ssize_t len;
+	size_t len;
 	char c;
 
 	if (conn->buf == NULL) {
@@ -473,13 +510,12 @@ fetch_getln(char *str, int size, struct 
 	conn->buflen = 0;
 	do {
 		len = fetch_read(&c, sizeof(c), 1, conn);
-		if (len == -1) {
-			conn->iserr = 1;
-			return NULL;
-		}
 		if (len == 0) {
-			conn->iseof = 1;
-			break;
+			if (conn->iserr)
+				return NULL;
+			if (conn->iseof)
+				break;
+			abort();
 		}
 		conn->buf[conn->buflen++] = c;
 		if (conn->buflen == conn->bufsize) {
@@ -531,8 +567,8 @@ fetch_getline(struct fetch_connect *conn
 	} else if (len == buflen - 1) {	/* line too long */
 		while (1) {
 			char c;
-			ssize_t rlen = fetch_read(&c, sizeof(c), 1, conn);
-			if (rlen <= 0 || c == '\n')
+			size_t rlen = fetch_read(&c, sizeof(c), 1, conn);
+			if (rlen == 0 || c == '\n')
 				break;
 		}
 		if (errormsg)
@@ -545,12 +581,15 @@ fetch_getline(struct fetch_connect *conn
 	return len;
 }
 
+#ifdef WITH_SSL
 void *
 fetch_start_ssl(int sock, const char *servername)
 {
 	SSL *ssl;
 	SSL_CTX *ctx;
+	X509_VERIFY_PARAM *param;
 	int ret, ssl_err;
+	int verify = 0;	// getenv("NO_CERT_VERIFY") == NULL;
 
 	/* Init the SSL library and context */
 	if (!SSL_library_init()){
@@ -562,6 +601,10 @@ fetch_start_ssl(int sock, const char *se
 
 	ctx = SSL_CTX_new(SSLv23_client_method());
 	SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
+	if (verify) {
+		SSL_CTX_set_default_verify_paths(ctx);
+		SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
+	}
 
 	ssl = SSL_new(ctx);
 	if (ssl == NULL){
@@ -569,6 +612,19 @@ fetch_start_ssl(int sock, const char *se
 		SSL_CTX_free(ctx);
 		return NULL;
 	}
+
+	if (verify) {
+		param = SSL_get0_param(ssl);
+		if (!X509_VERIFY_PARAM_set1_host(param, servername,
+		    strlen(servername))) {
+			fprintf(ttyout, "SSL verification setup failed\n");
+			return NULL;
+		}
+
+		/* Enable peer verification, (using the default callback) */
+		SSL_set_verify(ssl, SSL_VERIFY_PEER, NULL);
+	}
+
 	SSL_set_fd(ssl, sock);
 	if (!SSL_set_tlsext_host_name(ssl, __UNCONST(servername))) {
 		fprintf(ttyout, "SSL hostname setting failed\n");
@@ -605,10 +661,13 @@ fetch_start_ssl(int sock, const char *se
 
 	return ssl;
 }
+#endif /* WITH_SSL */
 
 
 void
 fetch_set_ssl(struct fetch_connect *conn, void *ssl)
 {
+#ifdef WITH_SSL
 	conn->ssl = ssl;
+#endif
 }

Index: src/usr.bin/ftp/ssl.h
diff -u src/usr.bin/ftp/ssl.h:1.3.8.2 src/usr.bin/ftp/ssl.h:1.3.8.3
--- src/usr.bin/ftp/ssl.h:1.3.8.2	Mon Sep 12 15:05:21 2022
+++ src/usr.bin/ftp/ssl.h	Mon Sep 12 17:08:13 2022
@@ -1,7 +1,7 @@
-/*	$NetBSD: ssl.h,v 1.3.8.2 2022/09/12 15:05:21 martin Exp $	*/
+/*	$NetBSD: ssl.h,v 1.3.8.3 2022/09/12 17:08:13 martin Exp $	*/
 
 /*-
- * Copyright (c) 2012 The NetBSD Foundation, Inc.
+ * Copyright (c) 2012-2021 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -25,7 +25,6 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  * POSSIBILITY OF SUCH DAMAGE.
  */
-#ifdef WITH_SSL
 
 #define FETCH struct fetch_connect
 struct fetch_connect;
@@ -38,26 +37,8 @@ int fetch_flush(struct fetch_connect *);
 struct fetch_connect *fetch_open(const char *, const char *);
 struct fetch_connect *fetch_fdopen(int, const char *);
 int fetch_close(struct fetch_connect *);
-ssize_t fetch_read(void *, size_t, size_t, struct fetch_connect *);
+size_t fetch_read(void *, size_t, size_t, struct fetch_connect *);
 char *fetch_getln(char *, int, struct fetch_connect *);
 int fetch_getline(struct fetch_connect *, char *, size_t, const char **);
 void fetch_set_ssl(struct fetch_connect *, void *);
 void *fetch_start_ssl(int, const char *);
-
-#else	/* !WITH_SSL */
-
-#define FETCH FILE
-
-#define	fetch_printf	fprintf
-#define	fetch_fileno	fileno
-#define	fetch_error	ferror
-#define	fetch_flush	fflush
-#define	fetch_open	fopen
-#define	fetch_fdopen	fdopen
-#define	fetch_close	fclose
-#define	fetch_read	fread
-#define	fetch_getln	fgets
-#define	fetch_getline	get_line
-#define	fetch_set_ssl(a, b)
-
-#endif	/* !WITH_SSL */

Index: src/usr.bin/ftp/util.c
diff -u src/usr.bin/ftp/util.c:1.158.22.2 src/usr.bin/ftp/util.c:1.158.22.3
--- src/usr.bin/ftp/util.c:1.158.22.2	Mon Sep 12 15:05:21 2022
+++ src/usr.bin/ftp/util.c	Mon Sep 12 17:08:13 2022
@@ -1,7 +1,7 @@
-/*	$NetBSD: util.c,v 1.158.22.2 2022/09/12 15:05:21 martin Exp $	*/
+/*	$NetBSD: util.c,v 1.158.22.3 2022/09/12 17:08:13 martin Exp $	*/
 
 /*-
- * Copyright (c) 1997-2009 The NetBSD Foundation, Inc.
+ * Copyright (c) 1997-2020 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This code is derived from software contributed to The NetBSD Foundation
@@ -64,7 +64,7 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID("$NetBSD: util.c,v 1.158.22.2 2022/09/12 15:05:21 martin Exp $");
+__RCSID("$NetBSD: util.c,v 1.158.22.3 2022/09/12 17:08:13 martin Exp $");
 #endif /* not lint */
 
 /*
@@ -171,7 +171,7 @@ parse_feat(const char *fline)
 			 * work-around broken ProFTPd servers that can't
 			 * even obey RFC 2389.
 			 */
-	while (*fline && isspace((int)*fline))
+	while (*fline && isspace((unsigned char)*fline))
 		fline++;
 
 	if (strcasecmp(fline, "MDTM") == 0)
@@ -324,9 +324,10 @@ intr(int signo)
 /*
  * Signal handler for lost connections; cleanup various elements of
  * the connection state, and call cleanuppeer() to finish it off.
+ * This function is not signal safe, so exit if called by a signal.
  */
 void
-lostpeer(int dummy)
+lostpeer(int signo)
 {
 	int oerrno = errno;
 
@@ -356,6 +357,9 @@ lostpeer(int dummy)
 	proxflag = 0;
 	pswitch(0);
 	cleanuppeer();
+	if (signo) {
+		errx(1, "lostpeer due to signal %d", signo);
+	}
 	errno = oerrno;
 }
 
@@ -478,7 +482,8 @@ ftp_login(const char *host, const char *
 		}
 	}
 	updatelocalcwd();
-	updateremotecwd();
+	remotecwd[0] = '\0';
+	remcwdvalid = 0;
 
  cleanup_ftp_login:
 	FREEPTR(fuser);
@@ -615,7 +620,7 @@ remglob(char *argv[], int doswitch, cons
  * return value. Can't control multiple values being expanded from the
  * expression, we return only the first.
  * Returns NULL on error, or a pointer to a buffer containing the filename
- * that's the caller's responsiblity to free(3) when finished with.
+ * that's the caller's responsibility to free(3) when finished with.
  */
 char *
 globulize(const char *pattern)
@@ -726,7 +731,7 @@ remotemodtime(const char *file, int nois
 			*frac++ = '\0';
 		if (strlen(timestr) == 15 && strncmp(timestr, "191", 3) == 0) {
 			/*
-			 * XXX:	Workaround for lame ftpd's that return
+			 * XXX:	Workaround for buggy ftp servers that return
 			 *	`19100' instead of `2000'
 			 */
 			fprintf(ttyout,
@@ -835,6 +840,7 @@ updateremotecwd(void)
 	size_t	 i;
 	char	*cp;
 
+	remcwdvalid = 1;	/* whether it works or not, we are done */
 	overbose = verbose;
 	ocode = code;
 	if (ftp_debug == 0)
@@ -1174,6 +1180,8 @@ formatbuf(char *buf, size_t len, const c
 		case '/':
 		case '.':
 		case 'c':
+			if (connected && !remcwdvalid)
+				updateremotecwd();
 			p2 = connected ? remotecwd : "";
 			updirs = pdirs = 0;
 
@@ -1487,6 +1495,7 @@ ftp_poll(struct pollfd *fds, int nfds, i
 	return poll(fds, nfds, timeout);
 }
 
+#ifndef SMALL
 /*
  * malloc() with inbuilt error checking
  */
@@ -1541,3 +1550,4 @@ ftp_strdup(const char *str)
 		err(1, "Unable to allocate memory for string copy");
 	return (s);
 }
+#endif

Index: src/usr.bin/ftp/version.h
diff -u src/usr.bin/ftp/version.h:1.87.8.2 src/usr.bin/ftp/version.h:1.87.8.3
--- src/usr.bin/ftp/version.h:1.87.8.2	Mon Sep 12 15:05:21 2022
+++ src/usr.bin/ftp/version.h	Mon Sep 12 17:08:13 2022
@@ -1,7 +1,7 @@
-/*	$NetBSD: version.h,v 1.87.8.2 2022/09/12 15:05:21 martin Exp $	*/
+/*	$NetBSD: version.h,v 1.87.8.3 2022/09/12 17:08:13 martin Exp $	*/
 
 /*-
- * Copyright (c) 1999-2015 The NetBSD Foundation, Inc.
+ * Copyright (c) 1999-2021 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This code is derived from software contributed to The NetBSD Foundation
@@ -34,5 +34,5 @@
 #endif
 
 #ifndef FTP_VERSION
-#define	FTP_VERSION	"20150912"
+#define	FTP_VERSION	"20210826"
 #endif

Reply via email to