util.c revision 1.78 1 /* $NetBSD: util.c,v 1.78 1999/10/13 02:47:54 lukem Exp $ */
2
3 /*-
4 * Copyright (c) 1997-1999 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Luke Mewburn.
9 *
10 * This code is derived from software contributed to The NetBSD Foundation
11 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
12 * NASA Ames Research Center.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 * 3. All advertising materials mentioning features or use of this software
23 * must display the following acknowledgement:
24 * This product includes software developed by the NetBSD
25 * Foundation, Inc. and its contributors.
26 * 4. Neither the name of The NetBSD Foundation nor the names of its
27 * contributors may be used to endorse or promote products derived
28 * from this software without specific prior written permission.
29 *
30 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
31 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
32 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
34 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
35 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
36 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
37 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
38 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
39 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
40 * POSSIBILITY OF SUCH DAMAGE.
41 */
42
43 /*
44 * Copyright (c) 1985, 1989, 1993, 1994
45 * The Regents of the University of California. All rights reserved.
46 *
47 * Redistribution and use in source and binary forms, with or without
48 * modification, are permitted provided that the following conditions
49 * are met:
50 * 1. Redistributions of source code must retain the above copyright
51 * notice, this list of conditions and the following disclaimer.
52 * 2. Redistributions in binary form must reproduce the above copyright
53 * notice, this list of conditions and the following disclaimer in the
54 * documentation and/or other materials provided with the distribution.
55 * 3. All advertising materials mentioning features or use of this software
56 * must display the following acknowledgement:
57 * This product includes software developed by the University of
58 * California, Berkeley and its contributors.
59 * 4. Neither the name of the University nor the names of its contributors
60 * may be used to endorse or promote products derived from this software
61 * without specific prior written permission.
62 *
63 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
64 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
65 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
66 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
67 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
68 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
69 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
70 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
71 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
72 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
73 * SUCH DAMAGE.
74 */
75
76 #include <sys/cdefs.h>
77 #ifndef lint
78 __RCSID("$NetBSD: util.c,v 1.78 1999/10/13 02:47:54 lukem Exp $");
79 #endif /* not lint */
80
81 /*
82 * FTP User Program -- Misc support routines
83 */
84 #include <sys/types.h>
85 #include <sys/socket.h>
86 #include <sys/ioctl.h>
87 #include <sys/time.h>
88 #include <netinet/in.h>
89 #include <arpa/ftp.h>
90
91 #include <ctype.h>
92 #include <err.h>
93 #include <errno.h>
94 #include <fcntl.h>
95 #include <glob.h>
96 #include <signal.h>
97 #include <limits.h>
98 #include <pwd.h>
99 #include <stdio.h>
100 #include <stdlib.h>
101 #include <string.h>
102 #include <termios.h>
103 #include <time.h>
104 #include <tzfile.h>
105 #include <unistd.h>
106
107 #include "ftp_var.h"
108
109 /*
110 * Connect to peer server and
111 * auto-login, if possible.
112 */
113 void
114 setpeer(argc, argv)
115 int argc;
116 char *argv[];
117 {
118 char *host;
119 char *port;
120
121 if (connected) {
122 fprintf(ttyout, "Already connected to %s, use close first.\n",
123 hostname);
124 code = -1;
125 return;
126 }
127 if (argc < 2)
128 (void)another(&argc, &argv, "to");
129 if (argc < 2 || argc > 3) {
130 fprintf(ttyout, "usage: %s host-name [port]\n", argv[0]);
131 code = -1;
132 return;
133 }
134 if (gatemode)
135 port = gateport;
136 else
137 port = ftpport;
138 if (argc > 2)
139 port = strdup(argv[2]);
140
141 if (gatemode) {
142 if (gateserver == NULL || *gateserver == '\0')
143 errx(1, "gateserver not defined (shouldn't happen)");
144 host = hookup(gateserver, port);
145 } else
146 host = hookup(argv[1], port);
147
148 if (host) {
149 int overbose;
150
151 if (gatemode && verbose) {
152 fprintf(ttyout,
153 "Connecting via pass-through server %s\n",
154 gateserver);
155 }
156
157 connected = 1;
158 /*
159 * Set up defaults for FTP.
160 */
161 (void)strlcpy(typename, "ascii", sizeof(typename));
162 type = TYPE_A;
163 curtype = TYPE_A;
164 (void)strlcpy(formname, "non-print", sizeof(formname));
165 form = FORM_N;
166 (void)strlcpy(modename, "stream", sizeof(modename));
167 mode = MODE_S;
168 (void)strlcpy(structname, "file", sizeof(structname));
169 stru = STRU_F;
170 (void)strlcpy(bytename, "8", sizeof(bytename));
171 bytesize = 8;
172 if (autologin)
173 (void)ftp_login(argv[1], NULL, NULL);
174
175 overbose = verbose;
176 if (debug == 0)
177 verbose = -1;
178 if (command("SYST") == COMPLETE && overbose) {
179 char *cp, c;
180 c = 0;
181 cp = strchr(reply_string + 4, ' ');
182 if (cp == NULL)
183 cp = strchr(reply_string + 4, '\r');
184 if (cp) {
185 if (cp[-1] == '.')
186 cp--;
187 c = *cp;
188 *cp = '\0';
189 }
190
191 fprintf(ttyout, "Remote system type is %s.\n",
192 reply_string + 4);
193 if (cp)
194 *cp = c;
195 }
196 if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) {
197 if (proxy)
198 unix_proxy = 1;
199 else
200 unix_server = 1;
201 /*
202 * Set type to 0 (not specified by user),
203 * meaning binary by default, but don't bother
204 * telling server. We can use binary
205 * for text files unless changed by the user.
206 */
207 type = 0;
208 (void)strlcpy(typename, "binary", sizeof(typename));
209 if (overbose)
210 fprintf(ttyout,
211 "Using %s mode to transfer files.\n",
212 typename);
213 } else {
214 if (proxy)
215 unix_proxy = 0;
216 else
217 unix_server = 0;
218 if (overbose &&
219 !strncmp(reply_string, "215 TOPS20", 10))
220 fputs(
221 "Remember to set tenex mode when transferring binary files from this machine.\n",
222 ttyout);
223 }
224 verbose = overbose;
225 }
226 }
227
228 /*
229 * login to remote host, using given username & password if supplied
230 */
231 int
232 ftp_login(host, user, pass)
233 const char *host;
234 const char *user, *pass;
235 {
236 char tmp[80];
237 const char *acct;
238 struct passwd *pw;
239 int n, aflag, rval, freeuser, freepass, freeacct, len;
240
241 acct = NULL;
242 aflag = rval = freeuser = freepass = freeacct = 0;
243
244 /*
245 * Set up arguments for an anonymous FTP session, if necessary.
246 */
247 if (anonftp) {
248 /*
249 * Set up anonymous login password.
250 */
251 if ((pass = getenv("FTPANONPASS")) == NULL) {
252 char *anonpass;
253
254 if ((pass = getlogin()) == NULL) {
255 if ((pw = getpwuid(getuid())) == NULL)
256 pass = "anonymous";
257 else
258 pass = pw->pw_name;
259 }
260 /*
261 * Every anonymous FTP server I've encountered
262 * will accept the string "username@", and will
263 * append the hostname itself. We do this by default
264 * since many servers are picky about not having
265 * a FQDN in the anonymous password.
266 * - thorpej (at) netbsd.org
267 */
268 len = strlen(pass) + 2;
269 anonpass = xmalloc(len);
270 (void)strlcpy(anonpass, pass, len);
271 (void)strlcat(anonpass, "@", len);
272 pass = anonpass;
273 freepass = 1;
274 }
275 user = "anonymous"; /* as per RFC 1635 */
276 }
277
278 if (user == NULL)
279 freeuser = 1;
280 if (pass == NULL)
281 freepass = 1;
282 freeacct = 1;
283 if (ruserpass(host, &user, &pass, &acct) < 0) {
284 code = -1;
285 goto cleanup_ftp_login;
286 }
287
288 while (user == NULL) {
289 const char *myname = getlogin();
290
291 if (myname == NULL && (pw = getpwuid(getuid())) != NULL)
292 myname = pw->pw_name;
293 if (myname)
294 fprintf(ttyout, "Name (%s:%s): ", host, myname);
295 else
296 fprintf(ttyout, "Name (%s): ", host);
297 *tmp = '\0';
298 if (fgets(tmp, sizeof(tmp) - 1, stdin) == NULL) {
299 fprintf(ttyout, "\nEOF received; login aborted.\n");
300 clearerr(stdin);
301 code = -1;
302 goto cleanup_ftp_login;
303 }
304 tmp[strlen(tmp) - 1] = '\0';
305 freeuser = 0;
306 if (*tmp == '\0')
307 user = myname;
308 else
309 user = tmp;
310 }
311
312 if (gatemode) {
313 char *nuser;
314 int len;
315
316 len = strlen(user) + 1 + strlen(host) + 1;
317 nuser = xmalloc(len);
318 (void)strlcpy(nuser, user, len);
319 (void)strlcat(nuser, "@", len);
320 (void)strlcat(nuser, host, len);
321 freeuser = 1;
322 user = nuser;
323 }
324
325 n = command("USER %s", user);
326 if (n == CONTINUE) {
327 if (pass == NULL) {
328 freepass = 0;
329 pass = getpass("Password:");
330 }
331 n = command("PASS %s", pass);
332 }
333 if (n == CONTINUE) {
334 aflag++;
335 if (acct == NULL) {
336 freeacct = 0;
337 acct = getpass("Account:");
338 }
339 if (acct[0] == '\0') {
340 warnx("Login failed.");
341 goto cleanup_ftp_login;
342 }
343 n = command("ACCT %s", acct);
344 }
345 if ((n != COMPLETE) ||
346 (!aflag && acct != NULL && command("ACCT %s", acct) != COMPLETE)) {
347 warnx("Login failed.");
348 goto cleanup_ftp_login;
349 }
350 rval = 1;
351 if (proxy)
352 goto cleanup_ftp_login;
353
354 connected = -1;
355 for (n = 0; n < macnum; ++n) {
356 if (!strcmp("init", macros[n].mac_name)) {
357 (void)strlcpy(line, "$init", sizeof(line));
358 makeargv();
359 domacro(margc, margv);
360 break;
361 }
362 }
363 cleanup_ftp_login:
364 if (user != NULL && freeuser)
365 free((char *)user);
366 if (pass != NULL && freepass)
367 free((char *)pass);
368 if (acct != NULL && freeacct)
369 free((char *)acct);
370 return (rval);
371 }
372
373 /*
374 * `another' gets another argument, and stores the new argc and argv.
375 * It reverts to the top level (via main.c's intr()) on EOF/error.
376 *
377 * Returns false if no new arguments have been added.
378 */
379 int
380 another(pargc, pargv, prompt)
381 int *pargc;
382 char ***pargv;
383 const char *prompt;
384 {
385 int len = strlen(line), ret;
386
387 if (len >= sizeof(line) - 3) {
388 fputs("sorry, arguments too long.\n", ttyout);
389 intr(0);
390 }
391 fprintf(ttyout, "(%s) ", prompt);
392 line[len++] = ' ';
393 if (fgets(&line[len], sizeof(line) - len, stdin) == NULL) {
394 clearerr(stdin);
395 intr(0);
396 }
397 len += strlen(&line[len]);
398 if (len > 0 && line[len - 1] == '\n')
399 line[len - 1] = '\0';
400 makeargv();
401 ret = margc > *pargc;
402 *pargc = margc;
403 *pargv = margv;
404 return (ret);
405 }
406
407 /*
408 * glob files given in argv[] from the remote server.
409 * if errbuf isn't NULL, store error messages there instead
410 * of writing to the screen.
411 */
412 char *
413 remglob(argv, doswitch, errbuf)
414 char *argv[];
415 int doswitch;
416 char **errbuf;
417 {
418 char temp[MAXPATHLEN];
419 static char buf[MAXPATHLEN];
420 static FILE *ftemp = NULL;
421 static char **args;
422 int oldverbose, oldhash, fd, len;
423 char *cp, *mode;
424
425 if (!mflag) {
426 if (!doglob)
427 args = NULL;
428 else {
429 if (ftemp) {
430 (void)fclose(ftemp);
431 ftemp = NULL;
432 }
433 }
434 return (NULL);
435 }
436 if (!doglob) {
437 if (args == NULL)
438 args = argv;
439 if ((cp = *++args) == NULL)
440 args = NULL;
441 return (cp);
442 }
443 if (ftemp == NULL) {
444 len = strlcpy(temp, tmpdir, sizeof(temp));
445 if (temp[len - 1] != '/')
446 (void)strlcat(temp, "/", sizeof(temp));
447 (void)strlcat(temp, TMPFILE, sizeof(temp));
448 if ((fd = mkstemp(temp)) < 0) {
449 warn("unable to create temporary file %s", temp);
450 return (NULL);
451 }
452 close(fd);
453 oldverbose = verbose;
454 verbose = (errbuf != NULL) ? -1 : 0;
455 oldhash = hash;
456 hash = 0;
457 if (doswitch)
458 pswitch(!proxy);
459 for (mode = "w"; *++argv != NULL; mode = "a")
460 recvrequest("NLST", temp, *argv, mode, 0, 0);
461 if ((code / 100) != COMPLETE) {
462 if (errbuf != NULL)
463 *errbuf = reply_string;
464 }
465 if (doswitch)
466 pswitch(!proxy);
467 verbose = oldverbose;
468 hash = oldhash;
469 ftemp = fopen(temp, "r");
470 (void)unlink(temp);
471 if (ftemp == NULL) {
472 if (errbuf == NULL)
473 fputs(
474 "can't find list of remote files, oops.\n",
475 ttyout);
476 else
477 *errbuf =
478 "can't find list of remote files, oops.";
479 return (NULL);
480 }
481 }
482 if (fgets(buf, sizeof(buf), ftemp) == NULL) {
483 (void)fclose(ftemp);
484 ftemp = NULL;
485 return (NULL);
486 }
487 if ((cp = strchr(buf, '\n')) != NULL)
488 *cp = '\0';
489 return (buf);
490 }
491
492 /*
493 * Glob a local file name specification with the expectation of a single
494 * return value. Can't control multiple values being expanded from the
495 * expression, we return only the first.
496 * Returns NULL on error, or a pointer to a buffer containing the filename
497 * that's the caller's responsiblity to free(3) when finished with.
498 */
499 char *
500 globulize(pattern)
501 const char *pattern;
502 {
503 glob_t gl;
504 int flags;
505 char *p;
506
507 if (!doglob)
508 return (xstrdup(pattern));
509
510 flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
511 memset(&gl, 0, sizeof(gl));
512 if (glob(pattern, flags, NULL, &gl) || gl.gl_pathc == 0) {
513 warnx("%s: not found", pattern);
514 globfree(&gl);
515 return (NULL);
516 }
517 p = xstrdup(gl.gl_pathv[0]);
518 globfree(&gl);
519 return (p);
520 }
521
522 /*
523 * determine size of remote file
524 */
525 off_t
526 remotesize(file, noisy)
527 const char *file;
528 int noisy;
529 {
530 int overbose;
531 off_t size;
532
533 overbose = verbose;
534 size = -1;
535 if (debug == 0)
536 verbose = -1;
537 if (command("SIZE %s", file) == COMPLETE) {
538 char *cp, *ep;
539
540 cp = strchr(reply_string, ' ');
541 if (cp != NULL) {
542 cp++;
543 #ifndef NO_QUAD
544 size = strtoq(cp, &ep, 10);
545 #else
546 size = strtol(cp, &ep, 10);
547 #endif
548 if (*ep != '\0' && !isspace((unsigned char)*ep))
549 size = -1;
550 }
551 } else if (noisy && debug == 0) {
552 fputs(reply_string, ttyout);
553 putc('\n', ttyout);
554 }
555 verbose = overbose;
556 return (size);
557 }
558
559 /*
560 * determine last modification time (in GMT) of remote file
561 */
562 time_t
563 remotemodtime(file, noisy)
564 const char *file;
565 int noisy;
566 {
567 int overbose;
568 time_t rtime;
569 int ocode;
570
571 overbose = verbose;
572 ocode = code;
573 rtime = -1;
574 if (debug == 0)
575 verbose = -1;
576 if (command("MDTM %s", file) == COMPLETE) {
577 struct tm timebuf;
578 int yy, mo, day, hour, min, sec;
579 sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo,
580 &day, &hour, &min, &sec);
581 memset(&timebuf, 0, sizeof(timebuf));
582 timebuf.tm_sec = sec;
583 timebuf.tm_min = min;
584 timebuf.tm_hour = hour;
585 timebuf.tm_mday = day;
586 timebuf.tm_mon = mo - 1;
587 timebuf.tm_year = yy - TM_YEAR_BASE;
588 timebuf.tm_isdst = -1;
589 rtime = timegm(&timebuf);
590 if (rtime == -1 && (noisy || debug != 0))
591 fprintf(ttyout, "Can't convert %s to a time.\n",
592 reply_string);
593 } else if (noisy && debug == 0) {
594 fputs(reply_string, ttyout);
595 putc('\n', ttyout);
596 }
597 verbose = overbose;
598 if (rtime == -1)
599 code = ocode;
600 return (rtime);
601 }
602
603 #ifndef NO_PROGRESS
604
605 /*
606 * return non-zero if we're the current foreground process
607 */
608 int
609 foregroundproc()
610 {
611 static pid_t pgrp = -1;
612
613 if (pgrp == -1)
614 pgrp = getpgrp();
615
616 return (tcgetpgrp(fileno(ttyout)) == pgrp);
617 }
618
619
620 static void updateprogressmeter __P((int));
621
622 /*
623 * SIGALRM handler to update the progress meter
624 */
625 static void
626 updateprogressmeter(dummy)
627 int dummy;
628 {
629 int oerrno = errno;
630
631 progressmeter(0);
632 errno = oerrno;
633 }
634 #endif /* NO_PROGRESS */
635
636
637 /*
638 * List of order of magnitude prefixes.
639 * The last is `P', as 2^64 = 16384 Petabytes
640 */
641 static const char prefixes[] = " KMGTP";
642
643 /*
644 * Display a transfer progress bar if progress is non-zero.
645 * SIGALRM is hijacked for use by this function.
646 * - Before the transfer, set filesize to size of file (or -1 if unknown),
647 * and call with flag = -1. This starts the once per second timer,
648 * and a call to updateprogressmeter() upon SIGALRM.
649 * - During the transfer, updateprogressmeter will call progressmeter
650 * with flag = 0
651 * - After the transfer, call with flag = 1
652 */
653 static struct timeval start;
654 static struct timeval lastupdate;
655
656 #define BUFLEFT (sizeof(buf) - len)
657
658 void
659 progressmeter(flag)
660 int flag;
661 {
662 static off_t lastsize;
663 #ifndef NO_PROGRESS
664 struct timeval now, td, wait;
665 off_t cursize, abbrevsize, bytespersec;
666 double elapsed;
667 int ratio, barlength, i, len, remaining;
668
669 /*
670 * Work variables for progress bar.
671 *
672 * XXX: if the format of the progress bar changes
673 * (especially the number of characters in the
674 * `static' portion of it), be sure to update
675 * these appropriately.
676 */
677 char buf[256]; /* workspace for progress bar */
678 #define BAROVERHEAD 43 /* non `*' portion of progress bar */
679 /*
680 * stars should contain at least
681 * sizeof(buf) - BAROVERHEAD entries
682 */
683 const char stars[] =
684 "*****************************************************************************"
685 "*****************************************************************************"
686 "*****************************************************************************";
687
688 #endif
689
690 if (flag == -1) {
691 (void)gettimeofday(&start, NULL);
692 lastupdate = start;
693 lastsize = restart_point;
694 }
695 #ifndef NO_PROGRESS
696 len = 0;
697 if (!progress || filesize <= 0)
698 return;
699
700 (void)gettimeofday(&now, NULL);
701 cursize = bytes + restart_point;
702 timersub(&now, &lastupdate, &wait);
703 if (cursize > lastsize) {
704 lastupdate = now;
705 lastsize = cursize;
706 wait.tv_sec = 0;
707 }
708
709 /*
710 * print progress bar only if we are foreground process.
711 */
712 if (! foregroundproc())
713 return;
714
715 ratio = (int)((double)cursize * 100.0 / (double)filesize);
716 ratio = MAX(ratio, 0);
717 ratio = MIN(ratio, 100);
718 len += snprintf(buf + len, BUFLEFT, "\r%3d%% ", ratio);
719
720 /*
721 * calculate the length of the `*' bar, ensuring that
722 * the number of stars won't exceed the buffer size
723 */
724 barlength = MIN(sizeof(buf) - 1, ttywidth) - BAROVERHEAD;
725 if (barlength > 0) {
726 i = barlength * ratio / 100;
727 len += snprintf(buf + len, BUFLEFT,
728 "|%.*s%*s|", i, stars, barlength - i, "");
729 }
730
731 abbrevsize = cursize;
732 for (i = 0; abbrevsize >= 100000 && i < sizeof(prefixes); i++)
733 abbrevsize >>= 10;
734 len += snprintf(buf + len, BUFLEFT,
735 #ifndef NO_QUAD
736 " %5lld %c%c ", (long long)abbrevsize,
737 #else
738 " %5ld %c%c ", (long)abbrevsize,
739 #endif
740 prefixes[i],
741 i == 0 ? ' ' : 'B');
742
743 timersub(&now, &start, &td);
744 elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
745
746 bytespersec = 0;
747 if (bytes > 0) {
748 bytespersec = bytes;
749 if (elapsed > 0.0)
750 bytespersec /= elapsed;
751 }
752 for (i = 1; bytespersec >= 1024000 && i < sizeof(prefixes); i++)
753 bytespersec >>= 10;
754 len += snprintf(buf + len, BUFLEFT,
755 #ifndef NO_QUAD
756 " %3lld.%02d %cB/s ", (long long)bytespersec / 1024,
757 #else
758 " %3ld.%02d %cB/s ", (long)bytespersec / 1024,
759 #endif
760 (int)((bytespersec % 1024) * 100 / 1024),
761 prefixes[i]);
762
763 if (bytes <= 0 || elapsed <= 0.0 || cursize > filesize) {
764 len += snprintf(buf + len, BUFLEFT, " --:-- ETA");
765 } else if (wait.tv_sec >= STALLTIME) {
766 len += snprintf(buf + len, BUFLEFT, " - stalled -");
767 } else {
768 remaining = (int)
769 ((filesize - restart_point) / (bytes / elapsed) - elapsed);
770 if (remaining >= 100 * SECSPERHOUR)
771 len += snprintf(buf + len, BUFLEFT, " --:-- ETA");
772 else {
773 i = remaining / SECSPERHOUR;
774 if (i)
775 len += snprintf(buf + len, BUFLEFT, "%2d:", i);
776 else
777 len += snprintf(buf + len, BUFLEFT, " ");
778 i = remaining % SECSPERHOUR;
779 len += snprintf(buf + len, BUFLEFT,
780 "%02d:%02d ETA", i / 60, i % 60);
781 }
782 }
783 if (flag == 1)
784 len += snprintf(buf + len, BUFLEFT, "\n");
785 (void)write(fileno(ttyout), buf, len);
786
787 if (flag == -1) {
788 (void)xsignal_restart(SIGALRM, updateprogressmeter, 1);
789 alarmtimer(1); /* set alarm timer for 1 Hz */
790 } else if (flag == 1) {
791 (void)xsignal(SIGALRM, SIG_DFL);
792 alarmtimer(0);
793 }
794 #endif /* !NO_PROGRESS */
795 }
796
797 /*
798 * Display transfer statistics.
799 * Requires start to be initialised by progressmeter(-1),
800 * direction to be defined by xfer routines, and filesize and bytes
801 * to be updated by xfer routines
802 * If siginfo is nonzero, an ETA is displayed, and the output goes to stderr
803 * instead of ttyout.
804 */
805 void
806 ptransfer(siginfo)
807 int siginfo;
808 {
809 struct timeval now, td, wait;
810 double elapsed;
811 off_t bytespersec;
812 int remaining, hh, i, len;
813
814 char buf[256]; /* Work variable for transfer status. */
815
816 if (!verbose && !progress && !siginfo)
817 return;
818
819 (void)gettimeofday(&now, NULL);
820 timersub(&now, &start, &td);
821 elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
822 bytespersec = 0;
823 if (bytes > 0) {
824 bytespersec = bytes;
825 if (elapsed > 0.0)
826 bytespersec /= elapsed;
827 }
828 len = 0;
829 len += snprintf(buf + len, BUFLEFT,
830 #ifndef NO_QUAD
831 "%lld byte%s %s in ", (long long)bytes,
832 #else
833 "%ld byte%s %s in ", (long)bytes,
834 #endif
835 bytes == 1 ? "" : "s", direction);
836 remaining = (int)elapsed;
837 if (remaining > SECSPERDAY) {
838 int days;
839
840 days = remaining / SECSPERDAY;
841 remaining %= SECSPERDAY;
842 len += snprintf(buf + len, BUFLEFT,
843 "%d day%s ", days, days == 1 ? "" : "s");
844 }
845 hh = remaining / SECSPERHOUR;
846 remaining %= SECSPERHOUR;
847 if (hh)
848 len += snprintf(buf + len, BUFLEFT, "%2d:", hh);
849 len += snprintf(buf + len, BUFLEFT,
850 "%02d:%02d ", remaining / 60, remaining % 60);
851
852 for (i = 1; bytespersec >= 1024000 && i < sizeof(prefixes); i++)
853 bytespersec >>= 10;
854 len += snprintf(buf + len, BUFLEFT,
855 #ifndef NO_QUAD
856 "(%lld.%02d %cB/s)", (long long)bytespersec / 1024,
857 #else
858 "(%ld.%02d %cB/s)", (long)bytespersec / 1024,
859 #endif
860 (int)((bytespersec % 1024) * 100 / 1024),
861 prefixes[i]);
862
863 if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0
864 && bytes + restart_point <= filesize) {
865 remaining = (int)((filesize - restart_point) /
866 (bytes / elapsed) - elapsed);
867 hh = remaining / SECSPERHOUR;
868 remaining %= SECSPERHOUR;
869 len += snprintf(buf + len, BUFLEFT, " ETA: ");
870 if (hh)
871 len += snprintf(buf + len, BUFLEFT, "%2d:", hh);
872 len += snprintf(buf + len, BUFLEFT, "%02d:%02d",
873 remaining / 60, remaining % 60);
874 timersub(&now, &lastupdate, &wait);
875 if (wait.tv_sec >= STALLTIME)
876 len += snprintf(buf + len, BUFLEFT, " (stalled)");
877 }
878 len += snprintf(buf + len, BUFLEFT, "\n");
879 (void)write(siginfo ? STDERR_FILENO : fileno(ttyout), buf, len);
880 }
881
882 /*
883 * SIG{INFO,QUIT} handler to print transfer stats if a transfer is in progress
884 */
885 void
886 psummary(notused)
887 int notused;
888 {
889 int oerrno = errno;
890
891 if (bytes > 0) {
892 if (fromatty)
893 write(fileno(ttyout), "\n", 1);
894 ptransfer(1);
895 }
896 errno = oerrno;
897 }
898
899 /*
900 * List words in stringlist, vertically arranged
901 */
902 void
903 list_vertical(sl)
904 StringList *sl;
905 {
906 int i, j, w;
907 int columns, width, lines, items;
908 char *p;
909
910 width = items = 0;
911
912 for (i = 0 ; i < sl->sl_cur ; i++) {
913 w = strlen(sl->sl_str[i]);
914 if (w > width)
915 width = w;
916 }
917 width = (width + 8) &~ 7;
918
919 columns = ttywidth / width;
920 if (columns == 0)
921 columns = 1;
922 lines = (sl->sl_cur + columns - 1) / columns;
923 for (i = 0; i < lines; i++) {
924 for (j = 0; j < columns; j++) {
925 p = sl->sl_str[j * lines + i];
926 if (p)
927 fputs(p, ttyout);
928 if (j * lines + i + lines >= sl->sl_cur) {
929 putc('\n', ttyout);
930 break;
931 }
932 w = strlen(p);
933 while (w < width) {
934 w = (w + 8) &~ 7;
935 (void)putc('\t', ttyout);
936 }
937 }
938 }
939 }
940
941 /*
942 * Update the global ttywidth value, using TIOCGWINSZ.
943 */
944 void
945 setttywidth(a)
946 int a;
947 {
948 struct winsize winsize;
949 int oerrno = errno;
950
951 if (ioctl(fileno(ttyout), TIOCGWINSZ, &winsize) != -1 &&
952 winsize.ws_col != 0)
953 ttywidth = winsize.ws_col;
954 else
955 ttywidth = 80;
956 errno = oerrno;
957 }
958
959 /*
960 * Change the rate limit up (SIGUSR1) or down (SIGUSR2)
961 */
962 void
963 crankrate(sig)
964 int sig;
965 {
966
967 switch (sig) {
968 case SIGUSR1:
969 if (rate_get)
970 rate_get += rate_get_incr;
971 if (rate_put)
972 rate_put += rate_put_incr;
973 break;
974 case SIGUSR2:
975 if (rate_get && rate_get > rate_get_incr)
976 rate_get -= rate_get_incr;
977 if (rate_put && rate_put > rate_put_incr)
978 rate_put -= rate_put_incr;
979 break;
980 default:
981 err(1, "crankrate invoked with unknown signal: %d", sig);
982 }
983 }
984
985
986 /*
987 * Set the SIGALRM interval timer for wait seconds, 0 to disable.
988 */
989 void
990 alarmtimer(wait)
991 int wait;
992 {
993 struct itimerval itv;
994
995 itv.it_value.tv_sec = wait;
996 itv.it_value.tv_usec = 0;
997 itv.it_interval = itv.it_value;
998 setitimer(ITIMER_REAL, &itv, NULL);
999 }
1000
1001 /*
1002 * Setup or cleanup EditLine structures
1003 */
1004 #ifndef NO_EDITCOMPLETE
1005 void
1006 controlediting()
1007 {
1008 if (editing && el == NULL && hist == NULL) {
1009 HistEvent ev;
1010 int editmode;
1011
1012 el = el_init(__progname, stdin, ttyout, stderr);
1013 /* init editline */
1014 hist = history_init(); /* init the builtin history */
1015 history(hist, &ev, H_SETSIZE, 100);/* remember 100 events */
1016 el_set(el, EL_HIST, history, hist); /* use history */
1017
1018 el_set(el, EL_EDITOR, "emacs"); /* default editor is emacs */
1019 el_set(el, EL_PROMPT, prompt); /* set the prompt function */
1020
1021 /* add local file completion, bind to TAB */
1022 el_set(el, EL_ADDFN, "ftp-complete",
1023 "Context sensitive argument completion",
1024 complete);
1025 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1026 el_source(el, NULL); /* read ~/.editrc */
1027 if ((el_get(el, EL_EDITMODE, &editmode) != -1) && editmode == 0)
1028 editing = 0; /* the user doesn't want editing,
1029 * so disable, and let statement
1030 * below cleanup */
1031 else
1032 el_set(el, EL_SIGNAL, 1);
1033 }
1034 if (!editing) {
1035 if (hist) {
1036 history_end(hist);
1037 hist = NULL;
1038 }
1039 if (el) {
1040 el_end(el);
1041 el = NULL;
1042 }
1043 }
1044 }
1045 #endif /* !NO_EDITCOMPLETE */
1046
1047 /*
1048 * Convert the string `arg' to an int, which may have an optional SI suffix
1049 * (`b', `k', `m', `g'). Returns the number for success, -1 otherwise.
1050 */
1051 int
1052 strsuftoi(arg)
1053 const char *arg;
1054 {
1055 char *cp;
1056 long val;
1057
1058 if (!isdigit((unsigned char)arg[0]))
1059 return (-1);
1060
1061 val = strtol(arg, &cp, 10);
1062 if (cp != NULL) {
1063 if (cp[0] != '\0' && cp[1] != '\0')
1064 return (-1);
1065 switch (tolower((unsigned char)cp[0])) {
1066 case '\0':
1067 case 'b':
1068 break;
1069 case 'k':
1070 val <<= 10;
1071 break;
1072 case 'm':
1073 val <<= 20;
1074 break;
1075 case 'g':
1076 val <<= 30;
1077 break;
1078 default:
1079 return (-1);
1080 }
1081 }
1082 if (val < 0 || val > INT_MAX)
1083 return (-1);
1084
1085 return (val);
1086 }
1087
1088 /*
1089 * Set up socket buffer sizes before a connection is made.
1090 */
1091 void
1092 setupsockbufsize(sock)
1093 int sock;
1094 {
1095
1096 if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void *) &sndbuf_size,
1097 sizeof(rcvbuf_size)) < 0)
1098 warn("unable to set sndbuf size %d", sndbuf_size);
1099
1100 if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void *) &rcvbuf_size,
1101 sizeof(rcvbuf_size)) < 0)
1102 warn("unable to set rcvbuf size %d", rcvbuf_size);
1103 }
1104
1105 /*
1106 * Copy characters from src into dst, \ quoting characters that require it
1107 */
1108 void
1109 ftpvis(dst, dstlen, src, srclen)
1110 char *dst;
1111 size_t dstlen;
1112 const char *src;
1113 size_t srclen;
1114 {
1115 int di, si;
1116
1117 for (di = si = 0;
1118 src[si] != '\0' && di < dstlen && si < srclen;
1119 di++, si++) {
1120 switch (src[si]) {
1121 case '\\':
1122 case ' ':
1123 case '\t':
1124 case '\r':
1125 case '\n':
1126 case '"':
1127 dst[di++] = '\\';
1128 if (di >= dstlen)
1129 break;
1130 /* FALLTHROUGH */
1131 default:
1132 dst[di] = src[si];
1133 }
1134 }
1135 dst[di] = '\0';
1136 }
1137
1138 /*
1139 * Determine if given string is an IPv6 address or not.
1140 * Return 1 for yes, 0 for no
1141 */
1142 int
1143 isipv6addr(addr)
1144 const char *addr;
1145 {
1146 int rv = 0;
1147 #ifdef INET6
1148 struct sockaddr_in6 su_sin6;
1149
1150 rv = inet_pton(AF_INET6, addr, &su_sin6.sin6_addr);
1151 if (debug)
1152 fprintf(ttyout, "isipv6addr: got %d for %s\n", rv, addr);
1153 #endif
1154 return rv;
1155 }
1156
1157
1158 /*
1159 * Internal version of connect(2); sets socket buffer sizes first.
1160 */
1161 int
1162 xconnect(sock, name, namelen)
1163 int sock;
1164 const struct sockaddr *name;
1165 int namelen;
1166 {
1167
1168 setupsockbufsize(sock);
1169 return (connect(sock, name, namelen));
1170 }
1171
1172 /*
1173 * Internal version of listen(2); sets socket buffer sizes first.
1174 */
1175 int
1176 xlisten(sock, backlog)
1177 int sock, backlog;
1178 {
1179
1180 setupsockbufsize(sock);
1181 return (listen(sock, backlog));
1182 }
1183
1184 /*
1185 * malloc() with inbuilt error checking
1186 */
1187 void *
1188 xmalloc(size)
1189 size_t size;
1190 {
1191 void *p;
1192
1193 p = malloc(size);
1194 if (p == NULL)
1195 err(1, "Unable to allocate %ld bytes of memory", (long)size);
1196 return (p);
1197 }
1198
1199 /*
1200 * strdup() with inbuilt error checking
1201 */
1202 char *
1203 xstrdup(str)
1204 const char *str;
1205 {
1206 char *s;
1207
1208 if (str == NULL)
1209 errx(1, "xstrdup() called with NULL argument");
1210 s = strdup(str);
1211 if (s == NULL)
1212 err(1, "Unable to allocate memory for string copy");
1213 return (s);
1214 }
1215
1216 /*
1217 * Install a POSIX signal handler, allowing the invoker to set whether
1218 * the signal should be restartable or not
1219 */
1220 sig_t
1221 xsignal_restart(sig, func, restartable)
1222 int sig;
1223 void (*func) __P((int));
1224 int restartable;
1225 {
1226 struct sigaction act, oact;
1227 act.sa_handler = func;
1228
1229 sigemptyset(&act.sa_mask);
1230 #if defined(SA_RESTART) /* 4.4BSD, Posix(?), SVR4 */
1231 act.sa_flags = restartable ? SA_RESTART : 0;
1232 #elif defined(SA_INTERRUPT) /* SunOS 4.x */
1233 act.sa_flags = restartable ? 0 : SA_INTERRUPT;
1234 #else
1235 #error "system must have SA_RESTART or SA_INTERRUPT"
1236 #endif
1237 if (sigaction(sig, &act, &oact) < 0)
1238 return (SIG_ERR);
1239 return (oact.sa_handler);
1240 }
1241
1242 /*
1243 * Install a signal handler with the `restartable' flag set dependent upon
1244 * which signal is being set. (This is a wrapper to xsignal_restart())
1245 */
1246 sig_t
1247 xsignal(sig, func)
1248 int sig;
1249 void (*func) __P((int));
1250 {
1251 int restartable;
1252
1253 /*
1254 * Some signals print output or change the state of the process.
1255 * There should be restartable, so that reads and writes are
1256 * not affected. Some signals should cause program flow to change;
1257 * these signals should not be restartable, so that the system call
1258 * will return with EINTR, and the program will go do something
1259 * different. If the signal handler calls longjmp() or siglongjmp(),
1260 * it doesn't matter if it's restartable.
1261 */
1262
1263 switch(sig) {
1264 #ifdef SIGINFO
1265 case SIGINFO:
1266 #endif
1267 case SIGQUIT:
1268 case SIGUSR1:
1269 case SIGUSR2:
1270 case SIGWINCH:
1271 restartable = 1;
1272 break;
1273
1274 case SIGALRM:
1275 case SIGINT:
1276 case SIGPIPE:
1277 restartable = 0;
1278 break;
1279
1280 default:
1281 /*
1282 * This is unpleasant, but I don't know what would be better.
1283 * Right now, this "can't happen"
1284 */
1285 errx(1, "xsignal_restart called with signal %d\n",
1286 sig);
1287 }
1288
1289 return(xsignal_restart(sig, func, restartable));
1290 }
1291