Annotation of src/usr.bin/ftp/util.c, Revision 1.1
1.1 ! millert 1: /* $NetBSD: util.c,v 1.4 1997/02/01 11:26:34 lukem Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 1985, 1989, 1993, 1994
! 5: * The Regents of the University of California. All rights reserved.
! 6: *
! 7: * Redistribution and use in source and binary forms, with or without
! 8: * modification, are permitted provided that the following conditions
! 9: * are met:
! 10: * 1. Redistributions of source code must retain the above copyright
! 11: * notice, this list of conditions and the following disclaimer.
! 12: * 2. Redistributions in binary form must reproduce the above copyright
! 13: * notice, this list of conditions and the following disclaimer in the
! 14: * documentation and/or other materials provided with the distribution.
! 15: * 3. All advertising materials mentioning features or use of this software
! 16: * must display the following acknowledgement:
! 17: * This product includes software developed by the University of
! 18: * California, Berkeley and its contributors.
! 19: * 4. Neither the name of the University nor the names of its contributors
! 20: * may be used to endorse or promote products derived from this software
! 21: * without specific prior written permission.
! 22: *
! 23: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 24: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 25: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 26: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 27: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 28: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 29: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 31: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 32: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 33: * SUCH DAMAGE.
! 34: */
! 35:
! 36: #ifndef lint
! 37: static char rcsid[] = "$NetBSD: util.c,v 1.4 1997/02/01 11:26:34 lukem Exp $";
! 38: #endif /* not lint */
! 39:
! 40: /*
! 41: * FTP User Program -- Misc support routines
! 42: */
! 43: #include <sys/ioctl.h>
! 44: #include <sys/time.h>
! 45: #include <arpa/ftp.h>
! 46:
! 47: #include <ctype.h>
! 48: #include <err.h>
! 49: #include <fcntl.h>
! 50: #include <glob.h>
! 51: #include <stdio.h>
! 52: #include <string.h>
! 53: #include <time.h>
! 54: #include <unistd.h>
! 55:
! 56: #include "ftp_var.h"
! 57: #include "pathnames.h"
! 58:
! 59: /*
! 60: * Connect to peer server and
! 61: * auto-login, if possible.
! 62: */
! 63: void
! 64: setpeer(argc, argv)
! 65: int argc;
! 66: char *argv[];
! 67: {
! 68: char *host;
! 69: short port;
! 70:
! 71: if (connected) {
! 72: printf("Already connected to %s, use close first.\n",
! 73: hostname);
! 74: code = -1;
! 75: return;
! 76: }
! 77: if (argc < 2)
! 78: (void) another(&argc, &argv, "to");
! 79: if (argc < 2 || argc > 3) {
! 80: printf("usage: %s host-name [port]\n", argv[0]);
! 81: code = -1;
! 82: return;
! 83: }
! 84: port = ftpport;
! 85: if (argc > 2) {
! 86: port = atoi(argv[2]);
! 87: if (port <= 0) {
! 88: printf("%s: bad port number-- %s\n", argv[1], argv[2]);
! 89: printf ("usage: %s host-name [port]\n", argv[0]);
! 90: code = -1;
! 91: return;
! 92: }
! 93: port = htons(port);
! 94: }
! 95: host = hookup(argv[1], port);
! 96: if (host) {
! 97: int overbose;
! 98:
! 99: connected = 1;
! 100: /*
! 101: * Set up defaults for FTP.
! 102: */
! 103: (void) strcpy(typename, "ascii"), type = TYPE_A;
! 104: curtype = TYPE_A;
! 105: (void) strcpy(formname, "non-print"), form = FORM_N;
! 106: (void) strcpy(modename, "stream"), mode = MODE_S;
! 107: (void) strcpy(structname, "file"), stru = STRU_F;
! 108: (void) strcpy(bytename, "8"), bytesize = 8;
! 109: if (autologin)
! 110: (void) login(argv[1]);
! 111:
! 112: overbose = verbose;
! 113: if (debug == 0)
! 114: verbose = -1;
! 115: if (command("SYST") == COMPLETE && overbose) {
! 116: char *cp, c;
! 117: c = 0;
! 118: cp = strchr(reply_string+4, ' ');
! 119: if (cp == NULL)
! 120: cp = strchr(reply_string+4, '\r');
! 121: if (cp) {
! 122: if (cp[-1] == '.')
! 123: cp--;
! 124: c = *cp;
! 125: *cp = '\0';
! 126: }
! 127:
! 128: printf("Remote system type is %s.\n",
! 129: reply_string+4);
! 130: if (cp)
! 131: *cp = c;
! 132: }
! 133: if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) {
! 134: if (proxy)
! 135: unix_proxy = 1;
! 136: else
! 137: unix_server = 1;
! 138: /*
! 139: * Set type to 0 (not specified by user),
! 140: * meaning binary by default, but don't bother
! 141: * telling server. We can use binary
! 142: * for text files unless changed by the user.
! 143: */
! 144: type = 0;
! 145: (void) strcpy(typename, "binary");
! 146: if (overbose)
! 147: printf("Using %s mode to transfer files.\n",
! 148: typename);
! 149: } else {
! 150: if (proxy)
! 151: unix_proxy = 0;
! 152: else
! 153: unix_server = 0;
! 154: if (overbose &&
! 155: !strncmp(reply_string, "215 TOPS20", 10))
! 156: printf("Remember to set tenex mode when "
! 157: "transferring binary files from this "
! 158: "machine.\n");
! 159: }
! 160: verbose = overbose;
! 161: }
! 162: }
! 163:
! 164: /*
! 165: * `Another' gets another argument, and stores the new argc and argv.
! 166: * It reverts to the top level (via main.c's intr()) on EOF/error.
! 167: *
! 168: * Returns false if no new arguments have been added.
! 169: */
! 170: int
! 171: another(pargc, pargv, prompt)
! 172: int *pargc;
! 173: char ***pargv;
! 174: const char *prompt;
! 175: {
! 176: int len = strlen(line), ret;
! 177:
! 178: if (len >= sizeof(line) - 3) {
! 179: printf("sorry, arguments too long\n");
! 180: intr();
! 181: }
! 182: printf("(%s) ", prompt);
! 183: line[len++] = ' ';
! 184: if (fgets(&line[len], sizeof(line) - len, stdin) == NULL)
! 185: intr();
! 186: len += strlen(&line[len]);
! 187: if (len > 0 && line[len - 1] == '\n')
! 188: line[len - 1] = '\0';
! 189: makeargv();
! 190: ret = margc > *pargc;
! 191: *pargc = margc;
! 192: *pargv = margv;
! 193: return (ret);
! 194: }
! 195:
! 196: char *
! 197: remglob(argv, doswitch)
! 198: char *argv[];
! 199: int doswitch;
! 200: {
! 201: char temp[MAXPATHLEN];
! 202: static char buf[MAXPATHLEN];
! 203: static FILE *ftemp = NULL;
! 204: static char **args;
! 205: int oldverbose, oldhash, fd;
! 206: char *cp, *mode;
! 207:
! 208: if (!mflag) {
! 209: if (!doglob) {
! 210: args = NULL;
! 211: }
! 212: else {
! 213: if (ftemp) {
! 214: (void) fclose(ftemp);
! 215: ftemp = NULL;
! 216: }
! 217: }
! 218: return (NULL);
! 219: }
! 220: if (!doglob) {
! 221: if (args == NULL)
! 222: args = argv;
! 223: if ((cp = *++args) == NULL)
! 224: args = NULL;
! 225: return (cp);
! 226: }
! 227: if (ftemp == NULL) {
! 228: (void) snprintf(temp, sizeof(temp), "%s%s", _PATH_TMP, TMPFILE)
! 229: ;
! 230: fd = mkstemp(temp);
! 231: if (fd < 0) {
! 232: warn("unable to create temporary file %s", temp);
! 233: return (NULL);
! 234: }
! 235: close(fd);
! 236: oldverbose = verbose, verbose = 0;
! 237: oldhash = hash, hash = 0;
! 238: if (doswitch) {
! 239: pswitch(!proxy);
! 240: }
! 241: for (mode = "w"; *++argv != NULL; mode = "a")
! 242: recvrequest ("NLST", temp, *argv, mode, 0);
! 243: if (doswitch) {
! 244: pswitch(!proxy);
! 245: }
! 246: verbose = oldverbose; hash = oldhash;
! 247: ftemp = fopen(temp, "r");
! 248: (void) unlink(temp);
! 249: if (ftemp == NULL) {
! 250: printf("can't find list of remote files, oops\n");
! 251: return (NULL);
! 252: }
! 253: }
! 254: if (fgets(buf, sizeof (buf), ftemp) == NULL) {
! 255: (void) fclose(ftemp), ftemp = NULL;
! 256: return (NULL);
! 257: }
! 258: if ((cp = strchr(buf, '\n')) != NULL)
! 259: *cp = '\0';
! 260: return (buf);
! 261: }
! 262:
! 263: int
! 264: confirm(cmd, file)
! 265: const char *cmd, *file;
! 266: {
! 267: char line[BUFSIZ];
! 268:
! 269: if (!interactive || confirmrest)
! 270: return (1);
! 271: printf("%s %s? ", cmd, file);
! 272: (void) fflush(stdout);
! 273: if (fgets(line, sizeof(line), stdin) == NULL)
! 274: return (0);
! 275: switch (tolower(*line)) {
! 276: case 'n':
! 277: return (0);
! 278: case 'p':
! 279: interactive = 0;
! 280: printf("Interactive mode: off\n");
! 281: break;
! 282: case 'a':
! 283: confirmrest = 1;
! 284: printf("Prompting off for duration of %s\n", cmd);
! 285: break;
! 286: }
! 287: return (1);
! 288: }
! 289:
! 290: /*
! 291: * Glob a local file name specification with
! 292: * the expectation of a single return value.
! 293: * Can't control multiple values being expanded
! 294: * from the expression, we return only the first.
! 295: */
! 296: int
! 297: globulize(cpp)
! 298: char **cpp;
! 299: {
! 300: glob_t gl;
! 301: int flags;
! 302:
! 303: if (!doglob)
! 304: return (1);
! 305:
! 306: flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
! 307: memset(&gl, 0, sizeof(gl));
! 308: if (glob(*cpp, flags, NULL, &gl) ||
! 309: gl.gl_pathc == 0) {
! 310: warnx("%s: not found", *cpp);
! 311: globfree(&gl);
! 312: return (0);
! 313: }
! 314: *cpp = strdup(gl.gl_pathv[0]); /* XXX - wasted memory */
! 315: globfree(&gl);
! 316: return (1);
! 317: }
! 318:
! 319: /*
! 320: * determine size of remote file
! 321: */
! 322: off_t
! 323: remotesize(file, noisy)
! 324: const char *file;
! 325: int noisy;
! 326: {
! 327: int overbose;
! 328: off_t size;
! 329:
! 330: overbose = verbose;
! 331: size = -1;
! 332: if (debug == 0)
! 333: verbose = -1;
! 334: if (command("SIZE %s", file) == COMPLETE)
! 335: sscanf(reply_string, "%*s %qd", &size);
! 336: else if (noisy && debug == 0)
! 337: printf("%s\n", reply_string);
! 338: verbose = overbose;
! 339: return (size);
! 340: }
! 341:
! 342: /*
! 343: * determine last modification time (in GMT) of remote file
! 344: */
! 345: time_t
! 346: remotemodtime(file, noisy)
! 347: const char *file;
! 348: int noisy;
! 349: {
! 350: int overbose;
! 351: time_t rtime;
! 352:
! 353: overbose = verbose;
! 354: rtime = -1;
! 355: if (debug == 0)
! 356: verbose = -1;
! 357: if (command("MDTM %s", file) == COMPLETE) {
! 358: struct tm timebuf;
! 359: int yy, mo, day, hour, min, sec;
! 360: sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo,
! 361: &day, &hour, &min, &sec);
! 362: memset(&timebuf, 0, sizeof(timebuf));
! 363: timebuf.tm_sec = sec;
! 364: timebuf.tm_min = min;
! 365: timebuf.tm_hour = hour;
! 366: timebuf.tm_mday = day;
! 367: timebuf.tm_mon = mo - 1;
! 368: timebuf.tm_year = yy - 1900;
! 369: timebuf.tm_isdst = -1;
! 370: rtime = mktime(&timebuf);
! 371: if (rtime == -1 && (noisy || debug != 0))
! 372: printf("Can't convert %s to a time\n", reply_string);
! 373: else
! 374: rtime += timebuf.tm_gmtoff; /* conv. local -> GMT */
! 375: } else if (noisy && debug == 0)
! 376: printf("%s\n", reply_string);
! 377: verbose = overbose;
! 378: return (rtime);
! 379: }
! 380:
! 381: void
! 382: updateprogressmeter()
! 383: {
! 384:
! 385: progressmeter(0);
! 386: }
! 387:
! 388: /*
! 389: * Display a transfer progress bar if progress is non-zero.
! 390: * SIGALRM is hijacked for use by this function.
! 391: * - Before the transfer, set filesize to size of file (or -1 if unknown),
! 392: * and call with flag = -1. This starts the once per second timer,
! 393: * and a call to updateprogressmeter() upon SIGALRM.
! 394: * - During the transfer, updateprogressmeter will call progressmeter
! 395: * with flag = 0
! 396: * - After the transfer, call with flag = 1
! 397: */
! 398: static struct timeval start;
! 399:
! 400: void
! 401: progressmeter(flag)
! 402: int flag;
! 403: {
! 404: /*
! 405: * List of order of magnitude prefixes.
! 406: * The last is `P', as 2^64 = 16384 Petabytes
! 407: */
! 408: static const char prefixes[] = " KMGTP";
! 409:
! 410: static struct timeval lastupdate;
! 411: static off_t lastsize;
! 412: struct timeval now, td, wait;
! 413: off_t cursize, abbrevsize;
! 414: double elapsed;
! 415: int ratio, barlength, i, remaining;
! 416: char buf[256];
! 417:
! 418: if (flag == -1) {
! 419: (void) gettimeofday(&start, (struct timezone *)0);
! 420: lastupdate = start;
! 421: lastsize = restart_point;
! 422: }
! 423: (void) gettimeofday(&now, (struct timezone *)0);
! 424: if (!progress || filesize <= 0)
! 425: return;
! 426: cursize = bytes + restart_point;
! 427:
! 428: ratio = cursize * 100 / filesize;
! 429: ratio = MAX(ratio, 0);
! 430: ratio = MIN(ratio, 100);
! 431: snprintf(buf, sizeof(buf), "\r%3d%% ", ratio);
! 432:
! 433: barlength = ttywidth - 30;
! 434: if (barlength > 0) {
! 435: i = barlength * ratio / 100;
! 436: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
! 437: "|%.*s%*s|", i,
! 438: "*****************************************************************************"
! 439: "*****************************************************************************",
! 440: barlength - i, "");
! 441: }
! 442:
! 443: i = 0;
! 444: abbrevsize = cursize;
! 445: while (abbrevsize >= 100000 && i < sizeof(prefixes)) {
! 446: i++;
! 447: abbrevsize >>= 10;
! 448: }
! 449: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
! 450: " %5qd %c%c ", abbrevsize, prefixes[i],
! 451: prefixes[i] == ' ' ? ' ' : 'B');
! 452:
! 453: timersub(&now, &lastupdate, &wait);
! 454: if (cursize > lastsize) {
! 455: lastupdate = now;
! 456: lastsize = cursize;
! 457: if (wait.tv_sec >= STALLTIME) { /* fudge out stalled time */
! 458: start.tv_sec += wait.tv_sec;
! 459: start.tv_usec += wait.tv_usec;
! 460: }
! 461: wait.tv_sec = 0;
! 462: }
! 463:
! 464: timersub(&now, &start, &td);
! 465: elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
! 466:
! 467: if (bytes <= 0 || elapsed <= 0.0) {
! 468: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
! 469: " --:-- ETA");
! 470: } else if (wait.tv_sec >= STALLTIME) {
! 471: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
! 472: " - stalled -");
! 473: } else {
! 474: remaining = (int)((filesize - restart_point) /
! 475: (bytes / elapsed) - elapsed);
! 476: i = remaining / 3600;
! 477: if (i)
! 478: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
! 479: "%2d:", i);
! 480: else
! 481: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
! 482: " ");
! 483: i = remaining % 3600;
! 484: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
! 485: "%02d:%02d ETA", i / 60, i % 60);
! 486: }
! 487: (void)write(STDOUT_FILENO, buf, strlen(buf));
! 488:
! 489: if (flag == -1) {
! 490: (void) signal(SIGALRM, updateprogressmeter);
! 491: alarmtimer(1); /* set alarm timer for 1 Hz */
! 492: } else if (flag == 1) {
! 493: alarmtimer(0);
! 494: (void) putchar('\n');
! 495: }
! 496: fflush(stdout);
! 497: }
! 498:
! 499: /*
! 500: * Display transfer statistics.
! 501: * Requires start to be initialised by progressmeter(-1),
! 502: * direction to be defined by xfer routines, and filesize and bytes
! 503: * to be updated by xfer routines
! 504: * If siginfo is nonzero, an ETA is displayed, and the output goes to STDERR
! 505: * instead of STDOUT.
! 506: */
! 507: void
! 508: ptransfer(siginfo)
! 509: int siginfo;
! 510: {
! 511: struct timeval now, td;
! 512: double elapsed;
! 513: off_t bs;
! 514: int meg, remaining, hh;
! 515: char buf[100];
! 516:
! 517: if (!verbose && !siginfo)
! 518: return;
! 519:
! 520: (void) gettimeofday(&now, (struct timezone *)0);
! 521: timersub(&now, &start, &td);
! 522: elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
! 523: bs = bytes / (elapsed == 0.0 ? 1 : elapsed);
! 524: meg = 0;
! 525: if (bs > (1024 * 1024))
! 526: meg = 1;
! 527: (void)snprintf(buf, sizeof(buf),
! 528: "%qd byte%s %s in %.2f seconds (%.2f %sB/s)\n",
! 529: bytes, bytes == 1 ? "" : "s", direction, elapsed,
! 530: bs / (1024.0 * (meg ? 1024.0 : 1.0)), meg ? "M" : "K");
! 531: if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0) {
! 532: remaining = (int)((filesize - restart_point) /
! 533: (bytes / elapsed) - elapsed);
! 534: hh = remaining / 3600;
! 535: remaining %= 3600;
! 536: snprintf(buf + strlen(buf) - 1, sizeof(buf) - strlen(buf),
! 537: " ETA: %02d:%02d:%02d\n", hh, remaining / 60,
! 538: remaining % 60);
! 539: }
! 540: (void)write(siginfo ? STDERR_FILENO : STDOUT_FILENO, buf, strlen(buf));
! 541: }
! 542:
! 543: /*
! 544: * List words in stringlist, vertically arranged
! 545: */
! 546: void
! 547: list_vertical(sl)
! 548: StringList *sl;
! 549: {
! 550: int i, j, w;
! 551: int columns, width, lines, items;
! 552: char *p;
! 553:
! 554: width = items = 0;
! 555:
! 556: for (i = 0 ; i < sl->sl_cur ; i++) {
! 557: w = strlen(sl->sl_str[i]);
! 558: if (w > width)
! 559: width = w;
! 560: }
! 561: width = (width + 8) &~ 7;
! 562:
! 563: columns = ttywidth / width;
! 564: if (columns == 0)
! 565: columns = 1;
! 566: lines = (sl->sl_cur + columns - 1) / columns;
! 567: for (i = 0; i < lines; i++) {
! 568: for (j = 0; j < columns; j++) {
! 569: p = sl->sl_str[j * lines + i];
! 570: if (p)
! 571: printf("%s", p);
! 572: if (j * lines + i + lines >= sl->sl_cur) {
! 573: printf("\n");
! 574: break;
! 575: }
! 576: w = strlen(p);
! 577: while (w < width) {
! 578: w = (w + 8) &~ 7;
! 579: (void) putchar('\t');
! 580: }
! 581: }
! 582: }
! 583: }
! 584:
! 585: /*
! 586: * Update the global ttywidth value, using TIOCGWINSZ.
! 587: */
! 588: void
! 589: setttywidth(a)
! 590: int a;
! 591: {
! 592: struct winsize winsize;
! 593:
! 594: if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1)
! 595: ttywidth = winsize.ws_col;
! 596: else
! 597: ttywidth = 80;
! 598: }
! 599:
! 600: /*
! 601: * Set the SIGALRM interval timer for wait seconds, 0 to disable.
! 602: */
! 603:
! 604: void
! 605: alarmtimer(wait)
! 606: int wait;
! 607: {
! 608: struct itimerval itv;
! 609:
! 610: itv.it_value.tv_sec = wait;
! 611: itv.it_value.tv_usec = 0;
! 612: itv.it_interval = itv.it_value;
! 613: setitimer(ITIMER_REAL, &itv, NULL);
! 614: }