Annotation of src/usr.bin/ftp/util.c, Revision 1.11
1.11 ! millert 1: /* $OpenBSD: util.c,v 1.10 1997/06/10 19:39:54 millert Exp $ */
! 2: /* $NetBSD: util.c,v 1.11 1997/07/21 14:03:49 lukem Exp $ */
1.1 millert 3:
4: /*
5: * Copyright (c) 1985, 1989, 1993, 1994
6: * The Regents of the University of California. All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
16: * 3. All advertising materials mentioning features or use of this software
17: * must display the following acknowledgement:
18: * This product includes software developed by the University of
19: * California, Berkeley and its contributors.
20: * 4. Neither the name of the University nor the names of its contributors
21: * may be used to endorse or promote products derived from this software
22: * without specific prior written permission.
23: *
24: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34: * SUCH DAMAGE.
35: */
36:
37: #ifndef lint
1.11 ! millert 38: static char rcsid[] = "$OpenBSD: util.c,v 1.10 1997/06/10 19:39:54 millert Exp $";
1.1 millert 39: #endif /* not lint */
40:
41: /*
42: * FTP User Program -- Misc support routines
43: */
44: #include <sys/ioctl.h>
45: #include <sys/time.h>
46: #include <arpa/ftp.h>
47:
48: #include <ctype.h>
49: #include <err.h>
1.3 millert 50: #include <errno.h>
1.1 millert 51: #include <fcntl.h>
1.11 ! millert 52: #include <limits.h>
1.1 millert 53: #include <glob.h>
1.6 millert 54: #include <pwd.h>
1.3 millert 55: #include <signal.h>
1.1 millert 56: #include <stdio.h>
1.2 millert 57: #include <stdlib.h>
1.1 millert 58: #include <string.h>
59: #include <time.h>
60: #include <unistd.h>
61:
62: #include "ftp_var.h"
63: #include "pathnames.h"
64:
65: /*
66: * Connect to peer server and
67: * auto-login, if possible.
68: */
69: void
70: setpeer(argc, argv)
71: int argc;
72: char *argv[];
73: {
74: char *host;
1.11 ! millert 75: in_port_t port;
1.1 millert 76:
77: if (connected) {
1.7 deraadt 78: fprintf(ttyout, "Already connected to %s, use close first.\n",
1.2 millert 79: hostname);
1.1 millert 80: code = -1;
81: return;
82: }
83: if (argc < 2)
1.2 millert 84: (void)another(&argc, &argv, "to");
1.1 millert 85: if (argc < 2 || argc > 3) {
1.7 deraadt 86: fprintf(ttyout, "usage: %s host-name [port]\n", argv[0]);
1.1 millert 87: code = -1;
88: return;
89: }
90: port = ftpport;
91: if (argc > 2) {
1.11 ! millert 92: char *ep;
! 93: long nport;
! 94:
! 95: nport = strtol(argv[2], &ep, 10);
! 96: if (nport < 1 || nport > 0xffff || *ep != '\0') {
! 97: fprintf(ttyout, "%s: bad port number '%s'.\n",
! 98: argv[1], argv[2]);
! 99: fprintf(ttyout, "usage: %s host-name [port]\n",
! 100: argv[0]);
1.1 millert 101: code = -1;
102: return;
103: }
1.11 ! millert 104: port = htons((in_port_t)nport);
1.1 millert 105: }
106: host = hookup(argv[1], port);
107: if (host) {
108: int overbose;
109:
110: connected = 1;
111: /*
112: * Set up defaults for FTP.
113: */
1.2 millert 114: (void)strcpy(typename, "ascii"), type = TYPE_A;
1.1 millert 115: curtype = TYPE_A;
1.2 millert 116: (void)strcpy(formname, "non-print"), form = FORM_N;
117: (void)strcpy(modename, "stream"), mode = MODE_S;
118: (void)strcpy(structname, "file"), stru = STRU_F;
119: (void)strcpy(bytename, "8"), bytesize = 8;
1.1 millert 120: if (autologin)
1.6 millert 121: (void)login(argv[1], NULL, NULL);
1.1 millert 122:
1.2 millert 123: #if (defined(unix) || defined(BSD)) && NBBY == 8
124: /*
125: * this ifdef is to keep someone form "porting" this to an incompatible
126: * system and not checking this out. This way they have to think about it.
127: */
1.1 millert 128: overbose = verbose;
129: if (debug == 0)
130: verbose = -1;
131: if (command("SYST") == COMPLETE && overbose) {
132: char *cp, c;
133: c = 0;
1.4 millert 134: cp = strchr(reply_string + 4, ' ');
1.1 millert 135: if (cp == NULL)
1.4 millert 136: cp = strchr(reply_string + 4, '\r');
1.1 millert 137: if (cp) {
138: if (cp[-1] == '.')
139: cp--;
140: c = *cp;
141: *cp = '\0';
142: }
143:
1.7 deraadt 144: fprintf(ttyout, "Remote system type is %s.\n", reply_string + 4);
1.1 millert 145: if (cp)
146: *cp = c;
147: }
148: if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) {
149: if (proxy)
150: unix_proxy = 1;
151: else
152: unix_server = 1;
153: /*
154: * Set type to 0 (not specified by user),
155: * meaning binary by default, but don't bother
156: * telling server. We can use binary
157: * for text files unless changed by the user.
158: */
159: type = 0;
1.2 millert 160: (void)strcpy(typename, "binary");
1.1 millert 161: if (overbose)
1.7 deraadt 162: fprintf(ttyout, "Using %s mode to transfer files.\n",
163: typename);
1.1 millert 164: } else {
165: if (proxy)
166: unix_proxy = 0;
167: else
168: unix_server = 0;
169: if (overbose &&
170: !strncmp(reply_string, "215 TOPS20", 10))
1.7 deraadt 171: fputs(
172: "Remember to set tenex mode when transferring binary files from this machine.\n",
173: ttyout);
1.1 millert 174: }
175: verbose = overbose;
1.2 millert 176: #endif /* unix || BSD */
1.1 millert 177: }
1.6 millert 178: }
179:
180: /*
181: * login to remote host, using given username & password if supplied
182: */
183: int
184: login(host, user, pass)
185: const char *host;
186: char *user, *pass;
187: {
188: char tmp[80];
189: char *acct;
190: char anonpass[MAXLOGNAME + 1 + MAXHOSTNAMELEN]; /* "user@hostname" */
191: char hostname[MAXHOSTNAMELEN];
1.11 ! millert 192: int n, aflag = 0, retry = 0;
1.6 millert 193:
194: acct = NULL;
195: if (user == NULL) {
196: if (ruserpass(host, &user, &pass, &acct) < 0) {
197: code = -1;
198: return (0);
199: }
200: }
201:
202: /*
203: * Set up arguments for an anonymous FTP session, if necessary.
204: */
205: if ((user == NULL || pass == NULL) && anonftp) {
206: memset(anonpass, 0, sizeof(anonpass));
207: memset(hostname, 0, sizeof(hostname));
208:
209: /*
210: * Set up anonymous login password.
211: */
212: user = getlogin();
213: gethostname(hostname, MAXHOSTNAMELEN);
214: #ifndef DONT_CHEAT_ANONPASS
215: /*
216: * Every anonymous FTP server I've encountered
217: * will accept the string "username@", and will
218: * append the hostname itself. We do this by default
219: * since many servers are picky about not having
220: * a FQDN in the anonymous password. - thorpej@netbsd.org
221: */
222: snprintf(anonpass, sizeof(anonpass) - 1, "%s@",
223: user);
224: #else
225: snprintf(anonpass, sizeof(anonpass) - 1, "%s@%s",
226: user, hp->h_name);
227: #endif
228: pass = anonpass;
1.11 ! millert 229: user = "anonymous"; /* as per RFC 1635 */
1.8 jkatz 230: }
231:
232: tryagain:
1.9 millert 233: if (retry)
1.11 ! millert 234: user = "ftp"; /* some servers only allow "ftp" */
1.8 jkatz 235:
1.6 millert 236: while (user == NULL) {
237: char *myname = getlogin();
238:
239: if (myname == NULL) {
240: struct passwd *pp = getpwuid(getuid());
241:
242: if (pp != NULL)
243: myname = pp->pw_name;
244: }
245: if (myname)
1.7 deraadt 246: fprintf(ttyout, "Name (%s:%s): ", host, myname);
1.6 millert 247: else
1.7 deraadt 248: fprintf(ttyout, "Name (%s): ", host);
1.6 millert 249: (void)fgets(tmp, sizeof(tmp) - 1, stdin);
250: tmp[strlen(tmp) - 1] = '\0';
251: if (*tmp == '\0')
252: user = myname;
253: else
254: user = tmp;
255: }
256: n = command("USER %s", user);
257: if (n == CONTINUE) {
258: if (pass == NULL)
259: pass = getpass("Password:");
260: n = command("PASS %s", pass);
261: }
262: if (n == CONTINUE) {
263: aflag++;
264: if (acct == NULL)
265: acct = getpass("Account:");
266: n = command("ACCT %s", acct);
267: }
268: if ((n != COMPLETE) ||
269: (!aflag && acct != NULL && command("ACCT %s", acct) != COMPLETE)) {
270: warnx("Login failed.");
1.9 millert 271: if (retry || !anonftp)
1.8 jkatz 272: return (0);
1.9 millert 273: else
274: retry = 1;
1.8 jkatz 275: goto tryagain;
1.6 millert 276: }
277: if (proxy)
278: return (1);
279: connected = -1;
280: for (n = 0; n < macnum; ++n) {
281: if (!strcmp("init", macros[n].mac_name)) {
282: (void)strcpy(line, "$init");
283: makeargv();
284: domacro(margc, margv);
285: break;
286: }
287: }
288: return (1);
1.1 millert 289: }
290:
291: /*
1.4 millert 292: * `another' gets another argument, and stores the new argc and argv.
1.1 millert 293: * It reverts to the top level (via main.c's intr()) on EOF/error.
294: *
295: * Returns false if no new arguments have been added.
296: */
297: int
298: another(pargc, pargv, prompt)
299: int *pargc;
300: char ***pargv;
301: const char *prompt;
302: {
303: int len = strlen(line), ret;
304:
305: if (len >= sizeof(line) - 3) {
1.7 deraadt 306: fputs("sorry, arguments too long.\n", ttyout);
1.1 millert 307: intr();
308: }
1.7 deraadt 309: fprintf(ttyout, "(%s) ", prompt);
1.1 millert 310: line[len++] = ' ';
1.11 ! millert 311: if (fgets(&line[len], (int)(sizeof(line) - len), stdin) == NULL)
1.1 millert 312: intr();
313: len += strlen(&line[len]);
314: if (len > 0 && line[len - 1] == '\n')
315: line[len - 1] = '\0';
316: makeargv();
317: ret = margc > *pargc;
318: *pargc = margc;
319: *pargv = margv;
320: return (ret);
321: }
322:
1.4 millert 323: /*
324: * glob files given in argv[] from the remote server.
325: * if errbuf isn't NULL, store error messages there instead
326: * of writing to the screen.
327: */
1.1 millert 328: char *
1.4 millert 329: remglob(argv, doswitch, errbuf)
1.1 millert 330: char *argv[];
331: int doswitch;
1.4 millert 332: char **errbuf;
1.1 millert 333: {
334: char temp[MAXPATHLEN];
335: static char buf[MAXPATHLEN];
336: static FILE *ftemp = NULL;
337: static char **args;
338: int oldverbose, oldhash, fd;
339: char *cp, *mode;
340:
341: if (!mflag) {
1.4 millert 342: if (!doglob)
1.1 millert 343: args = NULL;
344: else {
345: if (ftemp) {
1.2 millert 346: (void)fclose(ftemp);
1.1 millert 347: ftemp = NULL;
348: }
349: }
350: return (NULL);
351: }
352: if (!doglob) {
353: if (args == NULL)
354: args = argv;
355: if ((cp = *++args) == NULL)
356: args = NULL;
357: return (cp);
358: }
359: if (ftemp == NULL) {
1.3 millert 360: int len;
361:
362: if ((cp = getenv("TMPDIR")) == NULL)
363: cp = _PATH_TMP;
364: len = strlen(cp);
365: if (len + sizeof(TMPFILE) + (cp[len-1] != '/') > sizeof(temp)) {
366: warnx("unable to create temporary file: %s",
367: strerror(ENAMETOOLONG));
368: return (NULL);
369: }
370:
371: (void)strcpy(temp, cp);
372: if (temp[len-1] != '/')
373: temp[len++] = '/';
374: (void)strcpy(&temp[len], TMPFILE);
375: if ((fd = mkstemp(temp)) < 0) {
1.1 millert 376: warn("unable to create temporary file %s", temp);
377: return (NULL);
378: }
379: close(fd);
1.4 millert 380: oldverbose = verbose;
381: verbose = (errbuf != NULL) ? -1 : 0;
382: oldhash = hash;
383: hash = 0;
384: if (doswitch)
1.1 millert 385: pswitch(!proxy);
386: for (mode = "w"; *++argv != NULL; mode = "a")
1.4 millert 387: recvrequest("NLST", temp, *argv, mode, 0);
388: if ((code / 100) != COMPLETE) {
389: if (errbuf != NULL)
390: *errbuf = reply_string;
391: }
392: if (doswitch)
393: pswitch(!proxy);
394: verbose = oldverbose;
395: hash = oldhash;
1.1 millert 396: ftemp = fopen(temp, "r");
1.2 millert 397: (void)unlink(temp);
1.1 millert 398: if (ftemp == NULL) {
1.4 millert 399: if (errbuf == NULL)
1.7 deraadt 400: fputs("can't find list of remote files, oops.\n",
401: ttyout);
1.4 millert 402: else
403: *errbuf =
404: "can't find list of remote files, oops.";
1.1 millert 405: return (NULL);
406: }
407: }
1.2 millert 408: if (fgets(buf, sizeof(buf), ftemp) == NULL) {
1.4 millert 409: (void)fclose(ftemp);
410: ftemp = NULL;
1.1 millert 411: return (NULL);
412: }
413: if ((cp = strchr(buf, '\n')) != NULL)
414: *cp = '\0';
415: return (buf);
416: }
417:
418: int
419: confirm(cmd, file)
420: const char *cmd, *file;
421: {
422: char line[BUFSIZ];
423:
424: if (!interactive || confirmrest)
425: return (1);
1.7 deraadt 426: fprintf(ttyout, "%s %s? ", cmd, file);
427: (void)fflush(ttyout);
1.1 millert 428: if (fgets(line, sizeof(line), stdin) == NULL)
429: return (0);
430: switch (tolower(*line)) {
431: case 'n':
432: return (0);
433: case 'p':
434: interactive = 0;
1.7 deraadt 435: fputs("Interactive mode: off.\n", ttyout);
1.1 millert 436: break;
437: case 'a':
438: confirmrest = 1;
1.7 deraadt 439: fprintf(ttyout, "Prompting off for duration of %s.\n", cmd);
1.1 millert 440: break;
441: }
442: return (1);
443: }
444:
445: /*
446: * Glob a local file name specification with
447: * the expectation of a single return value.
448: * Can't control multiple values being expanded
449: * from the expression, we return only the first.
450: */
451: int
452: globulize(cpp)
453: char **cpp;
454: {
455: glob_t gl;
456: int flags;
457:
458: if (!doglob)
459: return (1);
460:
461: flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
462: memset(&gl, 0, sizeof(gl));
463: if (glob(*cpp, flags, NULL, &gl) ||
464: gl.gl_pathc == 0) {
465: warnx("%s: not found", *cpp);
466: globfree(&gl);
467: return (0);
468: }
469: *cpp = strdup(gl.gl_pathv[0]); /* XXX - wasted memory */
470: globfree(&gl);
471: return (1);
472: }
473:
474: /*
475: * determine size of remote file
476: */
477: off_t
478: remotesize(file, noisy)
479: const char *file;
480: int noisy;
481: {
482: int overbose;
483: off_t size;
484:
485: overbose = verbose;
486: size = -1;
487: if (debug == 0)
488: verbose = -1;
1.11 ! millert 489: if (command("SIZE %s", file) == COMPLETE) {
! 490: char *cp, *ep;
! 491:
! 492: cp = strchr(reply_string, ' ');
! 493: if (cp != NULL) {
! 494: cp++;
! 495: size = strtoq(cp, &ep, 10);
! 496: if (*ep != '\0' && !isspace(*ep))
! 497: size = -1;
! 498: }
! 499: } else if (noisy && debug == 0) {
1.7 deraadt 500: fputs(reply_string, ttyout);
1.11 ! millert 501: fputc('\n', ttyout);
1.7 deraadt 502: }
1.1 millert 503: verbose = overbose;
504: return (size);
505: }
506:
507: /*
508: * determine last modification time (in GMT) of remote file
509: */
510: time_t
511: remotemodtime(file, noisy)
512: const char *file;
513: int noisy;
514: {
515: int overbose;
516: time_t rtime;
517:
518: overbose = verbose;
519: rtime = -1;
520: if (debug == 0)
521: verbose = -1;
522: if (command("MDTM %s", file) == COMPLETE) {
523: struct tm timebuf;
524: int yy, mo, day, hour, min, sec;
525: sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo,
526: &day, &hour, &min, &sec);
527: memset(&timebuf, 0, sizeof(timebuf));
528: timebuf.tm_sec = sec;
529: timebuf.tm_min = min;
530: timebuf.tm_hour = hour;
531: timebuf.tm_mday = day;
532: timebuf.tm_mon = mo - 1;
533: timebuf.tm_year = yy - 1900;
534: timebuf.tm_isdst = -1;
535: rtime = mktime(&timebuf);
536: if (rtime == -1 && (noisy || debug != 0))
1.7 deraadt 537: fprintf(ttyout, "Can't convert %s to a time.\n", reply_string);
1.1 millert 538: else
539: rtime += timebuf.tm_gmtoff; /* conv. local -> GMT */
1.7 deraadt 540: } else if (noisy && debug == 0) {
541: fputs(reply_string, ttyout);
1.11 ! millert 542: fputc('\n', ttyout);
1.7 deraadt 543: }
1.1 millert 544: verbose = overbose;
545: return (rtime);
546: }
547:
1.10 millert 548: /*
549: * Returns true if this is the controlling/foreground process, else false.
550: */
551: int
552: foregroundproc()
553: {
554: static pid_t pgrp = -1;
555: int ctty_pgrp;
556:
557: if (pgrp == -1)
558: pgrp = getpgrp();
559:
560: return((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 &&
561: ctty_pgrp == pgrp));
562: }
563:
1.11 ! millert 564: void updateprogressmeter __P((int));
! 565:
1.1 millert 566: void
1.11 ! millert 567: updateprogressmeter(dummy)
! 568: int dummy;
1.1 millert 569: {
570:
1.10 millert 571: if (foregroundproc())
572: progressmeter(0);
1.1 millert 573: }
574:
575: /*
576: * Display a transfer progress bar if progress is non-zero.
577: * SIGALRM is hijacked for use by this function.
578: * - Before the transfer, set filesize to size of file (or -1 if unknown),
579: * and call with flag = -1. This starts the once per second timer,
580: * and a call to updateprogressmeter() upon SIGALRM.
581: * - During the transfer, updateprogressmeter will call progressmeter
582: * with flag = 0
583: * - After the transfer, call with flag = 1
584: */
585: static struct timeval start;
586:
587: void
588: progressmeter(flag)
589: int flag;
590: {
591: /*
592: * List of order of magnitude prefixes.
593: * The last is `P', as 2^64 = 16384 Petabytes
594: */
595: static const char prefixes[] = " KMGTP";
596:
597: static struct timeval lastupdate;
598: static off_t lastsize;
599: struct timeval now, td, wait;
600: off_t cursize, abbrevsize;
601: double elapsed;
602: int ratio, barlength, i, remaining;
603: char buf[256];
604:
605: if (flag == -1) {
1.2 millert 606: (void)gettimeofday(&start, (struct timezone *)0);
1.1 millert 607: lastupdate = start;
608: lastsize = restart_point;
609: }
1.2 millert 610: (void)gettimeofday(&now, (struct timezone *)0);
1.1 millert 611: if (!progress || filesize <= 0)
612: return;
613: cursize = bytes + restart_point;
614:
615: ratio = cursize * 100 / filesize;
616: ratio = MAX(ratio, 0);
617: ratio = MIN(ratio, 100);
618: snprintf(buf, sizeof(buf), "\r%3d%% ", ratio);
619:
620: barlength = ttywidth - 30;
621: if (barlength > 0) {
622: i = barlength * ratio / 100;
623: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
624: "|%.*s%*s|", i,
625: "*****************************************************************************"
626: "*****************************************************************************",
627: barlength - i, "");
628: }
629:
630: i = 0;
631: abbrevsize = cursize;
632: while (abbrevsize >= 100000 && i < sizeof(prefixes)) {
633: i++;
634: abbrevsize >>= 10;
635: }
636: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
1.11 ! millert 637: " %5qd %c%c ", (quad_t)abbrevsize, prefixes[i],
1.1 millert 638: prefixes[i] == ' ' ? ' ' : 'B');
639:
640: timersub(&now, &lastupdate, &wait);
641: if (cursize > lastsize) {
642: lastupdate = now;
643: lastsize = cursize;
644: if (wait.tv_sec >= STALLTIME) { /* fudge out stalled time */
645: start.tv_sec += wait.tv_sec;
646: start.tv_usec += wait.tv_usec;
647: }
648: wait.tv_sec = 0;
649: }
650:
651: timersub(&now, &start, &td);
652: elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
653:
1.11 ! millert 654: if (bytes <= 0 || elapsed <= 0.0 || cursize > filesize) {
1.1 millert 655: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
656: " --:-- ETA");
657: } else if (wait.tv_sec >= STALLTIME) {
658: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
659: " - stalled -");
660: } else {
661: remaining = (int)((filesize - restart_point) /
662: (bytes / elapsed) - elapsed);
663: i = remaining / 3600;
664: if (i)
665: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
666: "%2d:", i);
667: else
668: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
669: " ");
670: i = remaining % 3600;
671: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
672: "%02d:%02d ETA", i / 60, i % 60);
673: }
1.7 deraadt 674: (void)write(fileno(ttyout), buf, strlen(buf));
1.1 millert 675:
676: if (flag == -1) {
1.2 millert 677: (void)signal(SIGALRM, updateprogressmeter);
1.1 millert 678: alarmtimer(1); /* set alarm timer for 1 Hz */
679: } else if (flag == 1) {
680: alarmtimer(0);
1.7 deraadt 681: (void)putc('\n', ttyout);
1.1 millert 682: }
1.7 deraadt 683: fflush(ttyout);
1.1 millert 684: }
685:
686: /*
687: * Display transfer statistics.
688: * Requires start to be initialised by progressmeter(-1),
689: * direction to be defined by xfer routines, and filesize and bytes
690: * to be updated by xfer routines
691: * If siginfo is nonzero, an ETA is displayed, and the output goes to STDERR
1.7 deraadt 692: * instead of TTYOUT.
1.1 millert 693: */
694: void
695: ptransfer(siginfo)
696: int siginfo;
697: {
698: struct timeval now, td;
699: double elapsed;
700: off_t bs;
701: int meg, remaining, hh;
702: char buf[100];
703:
704: if (!verbose && !siginfo)
705: return;
706:
1.2 millert 707: (void)gettimeofday(&now, (struct timezone *)0);
1.1 millert 708: timersub(&now, &start, &td);
709: elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
710: bs = bytes / (elapsed == 0.0 ? 1 : elapsed);
711: meg = 0;
712: if (bs > (1024 * 1024))
713: meg = 1;
714: (void)snprintf(buf, sizeof(buf),
715: "%qd byte%s %s in %.2f seconds (%.2f %sB/s)\n",
1.11 ! millert 716: (quad_t)bytes, bytes == 1 ? "" : "s", direction, elapsed,
1.1 millert 717: bs / (1024.0 * (meg ? 1024.0 : 1.0)), meg ? "M" : "K");
1.11 ! millert 718: if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0
! 719: && bytes + restart_point <= filesize) {
1.1 millert 720: remaining = (int)((filesize - restart_point) /
721: (bytes / elapsed) - elapsed);
722: hh = remaining / 3600;
723: remaining %= 3600;
1.11 ! millert 724: /* "buf+len(buf) -1" to overwrite \n */
1.1 millert 725: snprintf(buf + strlen(buf) - 1, sizeof(buf) - strlen(buf),
726: " ETA: %02d:%02d:%02d\n", hh, remaining / 60,
727: remaining % 60);
728: }
1.7 deraadt 729: (void)write(siginfo ? STDERR_FILENO : fileno(ttyout), buf, strlen(buf));
1.1 millert 730: }
731:
732: /*
733: * List words in stringlist, vertically arranged
734: */
735: void
736: list_vertical(sl)
737: StringList *sl;
738: {
739: int i, j, w;
740: int columns, width, lines, items;
741: char *p;
742:
743: width = items = 0;
744:
745: for (i = 0 ; i < sl->sl_cur ; i++) {
746: w = strlen(sl->sl_str[i]);
747: if (w > width)
748: width = w;
749: }
750: width = (width + 8) &~ 7;
751:
752: columns = ttywidth / width;
753: if (columns == 0)
754: columns = 1;
755: lines = (sl->sl_cur + columns - 1) / columns;
756: for (i = 0; i < lines; i++) {
757: for (j = 0; j < columns; j++) {
758: p = sl->sl_str[j * lines + i];
759: if (p)
1.7 deraadt 760: fputs(p, ttyout);
1.1 millert 761: if (j * lines + i + lines >= sl->sl_cur) {
1.7 deraadt 762: putc('\n', ttyout);
1.1 millert 763: break;
764: }
765: w = strlen(p);
766: while (w < width) {
767: w = (w + 8) &~ 7;
1.7 deraadt 768: (void)putc('\t', ttyout);
1.1 millert 769: }
770: }
771: }
772: }
773:
774: /*
775: * Update the global ttywidth value, using TIOCGWINSZ.
776: */
777: void
778: setttywidth(a)
779: int a;
780: {
781: struct winsize winsize;
782:
1.7 deraadt 783: if (ioctl(fileno(ttyout), TIOCGWINSZ, &winsize) != -1)
1.1 millert 784: ttywidth = winsize.ws_col;
785: else
786: ttywidth = 80;
787: }
788:
789: /*
790: * Set the SIGALRM interval timer for wait seconds, 0 to disable.
791: */
792: void
793: alarmtimer(wait)
794: int wait;
795: {
796: struct itimerval itv;
797:
798: itv.it_value.tv_sec = wait;
799: itv.it_value.tv_usec = 0;
800: itv.it_interval = itv.it_value;
801: setitimer(ITIMER_REAL, &itv, NULL);
802: }
1.5 millert 803:
804: /*
805: * Setup or cleanup EditLine structures
806: */
807: #ifndef SMALL
808: void
809: controlediting()
810: {
811: if (editing && el == NULL && hist == NULL) {
1.7 deraadt 812: el = el_init(__progname, stdin, ttyout); /* init editline */
1.5 millert 813: hist = history_init(); /* init the builtin history */
814: history(hist, H_EVENT, 100); /* remember 100 events */
815: el_set(el, EL_HIST, history, hist); /* use history */
816:
817: el_set(el, EL_EDITOR, "emacs"); /* default editor is emacs */
818: el_set(el, EL_PROMPT, prompt); /* set the prompt function */
819:
820: /* add local file completion, bind to TAB */
821: el_set(el, EL_ADDFN, "ftp-complete",
822: "Context sensitive argument completion",
823: complete);
824: el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
825:
826: el_source(el, NULL); /* read ~/.editrc */
827: el_set(el, EL_SIGNAL, 1);
828: } else if (!editing) {
829: if (hist) {
830: history_end(hist);
831: hist = NULL;
832: }
833: if (el) {
834: el_end(el);
835: el = NULL;
836: }
837: }
838: }
839: #endif /* !SMALL */