summaryrefslogtreecommitdiff
path: root/usr.bin/ftp/util.c
diff options
context:
space:
mode:
authorTodd C. Miller <millert@cvs.openbsd.org>1997-02-03 01:02:44 +0000
committerTodd C. Miller <millert@cvs.openbsd.org>1997-02-03 01:02:44 +0000
commit608493bb3d8aaa88f2804c620ef8d208ba98b08a (patch)
tree23945a49ad1cb6c935d583e9738f80716c268e49 /usr.bin/ftp/util.c
parentb6a2ec9ce1490038560dc7ff89049580f7123f3c (diff)
Sync with NetBSD
Diffstat (limited to 'usr.bin/ftp/util.c')
-rw-r--r--usr.bin/ftp/util.c614
1 files changed, 614 insertions, 0 deletions
diff --git a/usr.bin/ftp/util.c b/usr.bin/ftp/util.c
new file mode 100644
index 00000000000..5d9c7ecd1fc
--- /dev/null
+++ b/usr.bin/ftp/util.c
@@ -0,0 +1,614 @@
+/* $NetBSD: util.c,v 1.4 1997/02/01 11:26:34 lukem Exp $ */
+
+/*
+ * Copyright (c) 1985, 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: util.c,v 1.4 1997/02/01 11:26:34 lukem Exp $";
+#endif /* not lint */
+
+/*
+ * FTP User Program -- Misc support routines
+ */
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <arpa/ftp.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "ftp_var.h"
+#include "pathnames.h"
+
+/*
+ * Connect to peer server and
+ * auto-login, if possible.
+ */
+void
+setpeer(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *host;
+ short port;
+
+ if (connected) {
+ printf("Already connected to %s, use close first.\n",
+ hostname);
+ code = -1;
+ return;
+ }
+ if (argc < 2)
+ (void) another(&argc, &argv, "to");
+ if (argc < 2 || argc > 3) {
+ printf("usage: %s host-name [port]\n", argv[0]);
+ code = -1;
+ return;
+ }
+ port = ftpport;
+ if (argc > 2) {
+ port = atoi(argv[2]);
+ if (port <= 0) {
+ printf("%s: bad port number-- %s\n", argv[1], argv[2]);
+ printf ("usage: %s host-name [port]\n", argv[0]);
+ code = -1;
+ return;
+ }
+ port = htons(port);
+ }
+ host = hookup(argv[1], port);
+ if (host) {
+ int overbose;
+
+ connected = 1;
+ /*
+ * Set up defaults for FTP.
+ */
+ (void) strcpy(typename, "ascii"), type = TYPE_A;
+ curtype = TYPE_A;
+ (void) strcpy(formname, "non-print"), form = FORM_N;
+ (void) strcpy(modename, "stream"), mode = MODE_S;
+ (void) strcpy(structname, "file"), stru = STRU_F;
+ (void) strcpy(bytename, "8"), bytesize = 8;
+ if (autologin)
+ (void) login(argv[1]);
+
+ overbose = verbose;
+ if (debug == 0)
+ verbose = -1;
+ if (command("SYST") == COMPLETE && overbose) {
+ char *cp, c;
+ c = 0;
+ cp = strchr(reply_string+4, ' ');
+ if (cp == NULL)
+ cp = strchr(reply_string+4, '\r');
+ if (cp) {
+ if (cp[-1] == '.')
+ cp--;
+ c = *cp;
+ *cp = '\0';
+ }
+
+ printf("Remote system type is %s.\n",
+ reply_string+4);
+ if (cp)
+ *cp = c;
+ }
+ if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) {
+ if (proxy)
+ unix_proxy = 1;
+ else
+ unix_server = 1;
+ /*
+ * Set type to 0 (not specified by user),
+ * meaning binary by default, but don't bother
+ * telling server. We can use binary
+ * for text files unless changed by the user.
+ */
+ type = 0;
+ (void) strcpy(typename, "binary");
+ if (overbose)
+ printf("Using %s mode to transfer files.\n",
+ typename);
+ } else {
+ if (proxy)
+ unix_proxy = 0;
+ else
+ unix_server = 0;
+ if (overbose &&
+ !strncmp(reply_string, "215 TOPS20", 10))
+ printf("Remember to set tenex mode when "
+ "transferring binary files from this "
+ "machine.\n");
+ }
+ verbose = overbose;
+ }
+}
+
+/*
+ * `Another' gets another argument, and stores the new argc and argv.
+ * It reverts to the top level (via main.c's intr()) on EOF/error.
+ *
+ * Returns false if no new arguments have been added.
+ */
+int
+another(pargc, pargv, prompt)
+ int *pargc;
+ char ***pargv;
+ const char *prompt;
+{
+ int len = strlen(line), ret;
+
+ if (len >= sizeof(line) - 3) {
+ printf("sorry, arguments too long\n");
+ intr();
+ }
+ printf("(%s) ", prompt);
+ line[len++] = ' ';
+ if (fgets(&line[len], sizeof(line) - len, stdin) == NULL)
+ intr();
+ len += strlen(&line[len]);
+ if (len > 0 && line[len - 1] == '\n')
+ line[len - 1] = '\0';
+ makeargv();
+ ret = margc > *pargc;
+ *pargc = margc;
+ *pargv = margv;
+ return (ret);
+}
+
+char *
+remglob(argv, doswitch)
+ char *argv[];
+ int doswitch;
+{
+ char temp[MAXPATHLEN];
+ static char buf[MAXPATHLEN];
+ static FILE *ftemp = NULL;
+ static char **args;
+ int oldverbose, oldhash, fd;
+ char *cp, *mode;
+
+ if (!mflag) {
+ if (!doglob) {
+ args = NULL;
+ }
+ else {
+ if (ftemp) {
+ (void) fclose(ftemp);
+ ftemp = NULL;
+ }
+ }
+ return (NULL);
+ }
+ if (!doglob) {
+ if (args == NULL)
+ args = argv;
+ if ((cp = *++args) == NULL)
+ args = NULL;
+ return (cp);
+ }
+ if (ftemp == NULL) {
+ (void) snprintf(temp, sizeof(temp), "%s%s", _PATH_TMP, TMPFILE)
+;
+ fd = mkstemp(temp);
+ if (fd < 0) {
+ warn("unable to create temporary file %s", temp);
+ return (NULL);
+ }
+ close(fd);
+ oldverbose = verbose, verbose = 0;
+ oldhash = hash, hash = 0;
+ if (doswitch) {
+ pswitch(!proxy);
+ }
+ for (mode = "w"; *++argv != NULL; mode = "a")
+ recvrequest ("NLST", temp, *argv, mode, 0);
+ if (doswitch) {
+ pswitch(!proxy);
+ }
+ verbose = oldverbose; hash = oldhash;
+ ftemp = fopen(temp, "r");
+ (void) unlink(temp);
+ if (ftemp == NULL) {
+ printf("can't find list of remote files, oops\n");
+ return (NULL);
+ }
+ }
+ if (fgets(buf, sizeof (buf), ftemp) == NULL) {
+ (void) fclose(ftemp), ftemp = NULL;
+ return (NULL);
+ }
+ if ((cp = strchr(buf, '\n')) != NULL)
+ *cp = '\0';
+ return (buf);
+}
+
+int
+confirm(cmd, file)
+ const char *cmd, *file;
+{
+ char line[BUFSIZ];
+
+ if (!interactive || confirmrest)
+ return (1);
+ printf("%s %s? ", cmd, file);
+ (void) fflush(stdout);
+ if (fgets(line, sizeof(line), stdin) == NULL)
+ return (0);
+ switch (tolower(*line)) {
+ case 'n':
+ return (0);
+ case 'p':
+ interactive = 0;
+ printf("Interactive mode: off\n");
+ break;
+ case 'a':
+ confirmrest = 1;
+ printf("Prompting off for duration of %s\n", cmd);
+ break;
+ }
+ return (1);
+}
+
+/*
+ * Glob a local file name specification with
+ * the expectation of a single return value.
+ * Can't control multiple values being expanded
+ * from the expression, we return only the first.
+ */
+int
+globulize(cpp)
+ char **cpp;
+{
+ glob_t gl;
+ int flags;
+
+ if (!doglob)
+ return (1);
+
+ flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
+ memset(&gl, 0, sizeof(gl));
+ if (glob(*cpp, flags, NULL, &gl) ||
+ gl.gl_pathc == 0) {
+ warnx("%s: not found", *cpp);
+ globfree(&gl);
+ return (0);
+ }
+ *cpp = strdup(gl.gl_pathv[0]); /* XXX - wasted memory */
+ globfree(&gl);
+ return (1);
+}
+
+/*
+ * determine size of remote file
+ */
+off_t
+remotesize(file, noisy)
+ const char *file;
+ int noisy;
+{
+ int overbose;
+ off_t size;
+
+ overbose = verbose;
+ size = -1;
+ if (debug == 0)
+ verbose = -1;
+ if (command("SIZE %s", file) == COMPLETE)
+ sscanf(reply_string, "%*s %qd", &size);
+ else if (noisy && debug == 0)
+ printf("%s\n", reply_string);
+ verbose = overbose;
+ return (size);
+}
+
+/*
+ * determine last modification time (in GMT) of remote file
+ */
+time_t
+remotemodtime(file, noisy)
+ const char *file;
+ int noisy;
+{
+ int overbose;
+ time_t rtime;
+
+ overbose = verbose;
+ rtime = -1;
+ if (debug == 0)
+ verbose = -1;
+ if (command("MDTM %s", file) == COMPLETE) {
+ struct tm timebuf;
+ int yy, mo, day, hour, min, sec;
+ sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo,
+ &day, &hour, &min, &sec);
+ memset(&timebuf, 0, sizeof(timebuf));
+ timebuf.tm_sec = sec;
+ timebuf.tm_min = min;
+ timebuf.tm_hour = hour;
+ timebuf.tm_mday = day;
+ timebuf.tm_mon = mo - 1;
+ timebuf.tm_year = yy - 1900;
+ timebuf.tm_isdst = -1;
+ rtime = mktime(&timebuf);
+ if (rtime == -1 && (noisy || debug != 0))
+ printf("Can't convert %s to a time\n", reply_string);
+ else
+ rtime += timebuf.tm_gmtoff; /* conv. local -> GMT */
+ } else if (noisy && debug == 0)
+ printf("%s\n", reply_string);
+ verbose = overbose;
+ return (rtime);
+}
+
+void
+updateprogressmeter()
+{
+
+ progressmeter(0);
+}
+
+/*
+ * Display a transfer progress bar if progress is non-zero.
+ * SIGALRM is hijacked for use by this function.
+ * - Before the transfer, set filesize to size of file (or -1 if unknown),
+ * and call with flag = -1. This starts the once per second timer,
+ * and a call to updateprogressmeter() upon SIGALRM.
+ * - During the transfer, updateprogressmeter will call progressmeter
+ * with flag = 0
+ * - After the transfer, call with flag = 1
+ */
+static struct timeval start;
+
+void
+progressmeter(flag)
+ int flag;
+{
+ /*
+ * List of order of magnitude prefixes.
+ * The last is `P', as 2^64 = 16384 Petabytes
+ */
+ static const char prefixes[] = " KMGTP";
+
+ static struct timeval lastupdate;
+ static off_t lastsize;
+ struct timeval now, td, wait;
+ off_t cursize, abbrevsize;
+ double elapsed;
+ int ratio, barlength, i, remaining;
+ char buf[256];
+
+ if (flag == -1) {
+ (void) gettimeofday(&start, (struct timezone *)0);
+ lastupdate = start;
+ lastsize = restart_point;
+ }
+ (void) gettimeofday(&now, (struct timezone *)0);
+ if (!progress || filesize <= 0)
+ return;
+ cursize = bytes + restart_point;
+
+ ratio = cursize * 100 / filesize;
+ ratio = MAX(ratio, 0);
+ ratio = MIN(ratio, 100);
+ snprintf(buf, sizeof(buf), "\r%3d%% ", ratio);
+
+ barlength = ttywidth - 30;
+ if (barlength > 0) {
+ i = barlength * ratio / 100;
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ "|%.*s%*s|", i,
+"*****************************************************************************"
+"*****************************************************************************",
+ barlength - i, "");
+ }
+
+ i = 0;
+ abbrevsize = cursize;
+ while (abbrevsize >= 100000 && i < sizeof(prefixes)) {
+ i++;
+ abbrevsize >>= 10;
+ }
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ " %5qd %c%c ", abbrevsize, prefixes[i],
+ prefixes[i] == ' ' ? ' ' : 'B');
+
+ timersub(&now, &lastupdate, &wait);
+ if (cursize > lastsize) {
+ lastupdate = now;
+ lastsize = cursize;
+ if (wait.tv_sec >= STALLTIME) { /* fudge out stalled time */
+ start.tv_sec += wait.tv_sec;
+ start.tv_usec += wait.tv_usec;
+ }
+ wait.tv_sec = 0;
+ }
+
+ timersub(&now, &start, &td);
+ elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
+
+ if (bytes <= 0 || elapsed <= 0.0) {
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ " --:-- ETA");
+ } else if (wait.tv_sec >= STALLTIME) {
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ " - stalled -");
+ } else {
+ remaining = (int)((filesize - restart_point) /
+ (bytes / elapsed) - elapsed);
+ i = remaining / 3600;
+ if (i)
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ "%2d:", i);
+ else
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ " ");
+ i = remaining % 3600;
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ "%02d:%02d ETA", i / 60, i % 60);
+ }
+ (void)write(STDOUT_FILENO, buf, strlen(buf));
+
+ if (flag == -1) {
+ (void) signal(SIGALRM, updateprogressmeter);
+ alarmtimer(1); /* set alarm timer for 1 Hz */
+ } else if (flag == 1) {
+ alarmtimer(0);
+ (void) putchar('\n');
+ }
+ fflush(stdout);
+}
+
+/*
+ * Display transfer statistics.
+ * Requires start to be initialised by progressmeter(-1),
+ * direction to be defined by xfer routines, and filesize and bytes
+ * to be updated by xfer routines
+ * If siginfo is nonzero, an ETA is displayed, and the output goes to STDERR
+ * instead of STDOUT.
+ */
+void
+ptransfer(siginfo)
+ int siginfo;
+{
+ struct timeval now, td;
+ double elapsed;
+ off_t bs;
+ int meg, remaining, hh;
+ char buf[100];
+
+ if (!verbose && !siginfo)
+ return;
+
+ (void) gettimeofday(&now, (struct timezone *)0);
+ timersub(&now, &start, &td);
+ elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
+ bs = bytes / (elapsed == 0.0 ? 1 : elapsed);
+ meg = 0;
+ if (bs > (1024 * 1024))
+ meg = 1;
+ (void)snprintf(buf, sizeof(buf),
+ "%qd byte%s %s in %.2f seconds (%.2f %sB/s)\n",
+ bytes, bytes == 1 ? "" : "s", direction, elapsed,
+ bs / (1024.0 * (meg ? 1024.0 : 1.0)), meg ? "M" : "K");
+ if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0) {
+ remaining = (int)((filesize - restart_point) /
+ (bytes / elapsed) - elapsed);
+ hh = remaining / 3600;
+ remaining %= 3600;
+ snprintf(buf + strlen(buf) - 1, sizeof(buf) - strlen(buf),
+ " ETA: %02d:%02d:%02d\n", hh, remaining / 60,
+ remaining % 60);
+ }
+ (void)write(siginfo ? STDERR_FILENO : STDOUT_FILENO, buf, strlen(buf));
+}
+
+/*
+ * List words in stringlist, vertically arranged
+ */
+void
+list_vertical(sl)
+ StringList *sl;
+{
+ int i, j, w;
+ int columns, width, lines, items;
+ char *p;
+
+ width = items = 0;
+
+ for (i = 0 ; i < sl->sl_cur ; i++) {
+ w = strlen(sl->sl_str[i]);
+ if (w > width)
+ width = w;
+ }
+ width = (width + 8) &~ 7;
+
+ columns = ttywidth / width;
+ if (columns == 0)
+ columns = 1;
+ lines = (sl->sl_cur + columns - 1) / columns;
+ for (i = 0; i < lines; i++) {
+ for (j = 0; j < columns; j++) {
+ p = sl->sl_str[j * lines + i];
+ if (p)
+ printf("%s", p);
+ if (j * lines + i + lines >= sl->sl_cur) {
+ printf("\n");
+ break;
+ }
+ w = strlen(p);
+ while (w < width) {
+ w = (w + 8) &~ 7;
+ (void) putchar('\t');
+ }
+ }
+ }
+}
+
+/*
+ * Update the global ttywidth value, using TIOCGWINSZ.
+ */
+void
+setttywidth(a)
+ int a;
+{
+ struct winsize winsize;
+
+ if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1)
+ ttywidth = winsize.ws_col;
+ else
+ ttywidth = 80;
+}
+
+/*
+ * Set the SIGALRM interval timer for wait seconds, 0 to disable.
+ */
+
+void
+alarmtimer(wait)
+ int wait;
+{
+ struct itimerval itv;
+
+ itv.it_value.tv_sec = wait;
+ itv.it_value.tv_usec = 0;
+ itv.it_interval = itv.it_value;
+ setitimer(ITIMER_REAL, &itv, NULL);
+}