GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/ftp/util.c Lines: 5 499 1.0 %
Date: 2017-11-07 Branches: 2 325 0.6 %

Line Branch Exec Source
1
/*	$OpenBSD: util.c,v 1.85 2017/09/05 05:37:35 jca Exp $	*/
2
/*	$NetBSD: util.c,v 1.12 1997/08/18 10:20:27 lukem Exp $	*/
3
4
/*-
5
 * Copyright (c) 1997-1999 The NetBSD Foundation, Inc.
6
 * All rights reserved.
7
 *
8
 * This code is derived from software contributed to The NetBSD Foundation
9
 * by Luke Mewburn.
10
 *
11
 * This code is derived from software contributed to The NetBSD Foundation
12
 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
13
 * NASA Ames Research Center.
14
 *
15
 * Redistribution and use in source and binary forms, with or without
16
 * modification, are permitted provided that the following conditions
17
 * are met:
18
 * 1. Redistributions of source code must retain the above copyright
19
 *    notice, this list of conditions and the following disclaimer.
20
 * 2. Redistributions in binary form must reproduce the above copyright
21
 *    notice, this list of conditions and the following disclaimer in the
22
 *    documentation and/or other materials provided with the distribution.
23
 *
24
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
25
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
28
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
 * POSSIBILITY OF SUCH DAMAGE.
35
 */
36
37
/*
38
 * Copyright (c) 1985, 1989, 1993, 1994
39
 *	The Regents of the University of California.  All rights reserved.
40
 *
41
 * Redistribution and use in source and binary forms, with or without
42
 * modification, are permitted provided that the following conditions
43
 * are met:
44
 * 1. Redistributions of source code must retain the above copyright
45
 *    notice, this list of conditions and the following disclaimer.
46
 * 2. Redistributions in binary form must reproduce the above copyright
47
 *    notice, this list of conditions and the following disclaimer in the
48
 *    documentation and/or other materials provided with the distribution.
49
 * 3. Neither the name of the University nor the names of its contributors
50
 *    may be used to endorse or promote products derived from this software
51
 *    without specific prior written permission.
52
 *
53
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63
 * SUCH DAMAGE.
64
 */
65
66
/*
67
 * FTP User Program -- Misc support routines
68
 */
69
#include <sys/ioctl.h>
70
#include <sys/socket.h>
71
#include <sys/time.h>
72
#include <arpa/ftp.h>
73
74
#include <ctype.h>
75
#include <err.h>
76
#include <errno.h>
77
#include <fcntl.h>
78
#include <libgen.h>
79
#include <glob.h>
80
#include <poll.h>
81
#include <pwd.h>
82
#include <signal.h>
83
#include <stdio.h>
84
#include <stdlib.h>
85
#include <string.h>
86
#include <time.h>
87
#include <unistd.h>
88
89
#include "ftp_var.h"
90
#include "pathnames.h"
91
92
#define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
93
#define MAXIMUM(a, b)	(((a) > (b)) ? (a) : (b))
94
95
static void updateprogressmeter(int);
96
97
/*
98
 * Connect to peer server and
99
 * auto-login, if possible.
100
 */
101
void
102
setpeer(int argc, char *argv[])
103
{
104
	char *host, *port;
105
106
	if (connected) {
107
		fprintf(ttyout, "Already connected to %s, use close first.\n",
108
		    hostname);
109
		code = -1;
110
		return;
111
	}
112
#ifndef SMALL
113
	if (argc < 2)
114
		(void)another(&argc, &argv, "to");
115
	if (argc < 2 || argc > 3) {
116
		fprintf(ttyout, "usage: %s host [port]\n", argv[0]);
117
		code = -1;
118
		return;
119
	}
120
#endif /* !SMALL */
121
	if (gatemode)
122
		port = gateport;
123
	else
124
		port = ftpport;
125
	if (argc > 2)
126
		port = argv[2];
127
128
	if (gatemode) {
129
		if (gateserver == NULL || *gateserver == '\0')
130
			errx(1, "gateserver not defined (shouldn't happen)");
131
		host = hookup(gateserver, port);
132
	} else
133
		host = hookup(argv[1], port);
134
135
	if (host) {
136
		int overbose;
137
138
		if (gatemode) {
139
			if (command("PASSERVE %s", argv[1]) != COMPLETE)
140
				return;
141
			if (verbose)
142
				fprintf(ttyout,
143
				    "Connected via pass-through server %s\n",
144
				    gateserver);
145
		}
146
147
		connected = 1;
148
		/*
149
		 * Set up defaults for FTP.
150
		 */
151
		(void)strlcpy(formname, "non-print", sizeof formname);
152
		form = FORM_N;
153
		(void)strlcpy(modename, "stream", sizeof modename);
154
		mode = MODE_S;
155
		(void)strlcpy(structname, "file", sizeof structname);
156
		stru = STRU_F;
157
		(void)strlcpy(bytename, "8", sizeof bytename);
158
		bytesize = 8;
159
160
		/*
161
		 * Set type to 0 (not specified by user),
162
		 * meaning binary by default, but don't bother
163
		 * telling server.  We can use binary
164
		 * for text files unless changed by the user.
165
		 */
166
		(void)strlcpy(typename, "binary", sizeof typename);
167
		curtype = TYPE_A;
168
		type = 0;
169
		if (autologin)
170
			(void)ftp_login(argv[1], NULL, NULL);
171
172
		overbose = verbose;
173
#ifndef SMALL
174
		if (!debug)
175
#endif /* !SMALL */
176
			verbose = -1;
177
		if (command("SYST") == COMPLETE && overbose) {
178
			char *cp, c;
179
			c = 0;
180
			cp = strchr(reply_string + 4, ' ');
181
			if (cp == NULL)
182
				cp = strchr(reply_string + 4, '\r');
183
			if (cp) {
184
				if (cp[-1] == '.')
185
					cp--;
186
				c = *cp;
187
				*cp = '\0';
188
			}
189
190
			fprintf(ttyout, "Remote system type is %s.\n", reply_string + 4);
191
			if (cp)
192
				*cp = c;
193
		}
194
		if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) {
195
			if (proxy)
196
				unix_proxy = 1;
197
			else
198
				unix_server = 1;
199
			if (overbose)
200
				fprintf(ttyout, "Using %s mode to transfer files.\n",
201
				    typename);
202
		} else {
203
			if (proxy)
204
				unix_proxy = 0;
205
			else
206
				unix_server = 0;
207
		}
208
		verbose = overbose;
209
	}
210
}
211
212
/*
213
 * login to remote host, using given username & password if supplied
214
 */
215
int
216
ftp_login(const char *host, char *user, char *pass)
217
{
218
	char tmp[80], *acctname = NULL, host_name[HOST_NAME_MAX+1];
219
	char anonpass[LOGIN_NAME_MAX + 1 + HOST_NAME_MAX+1];	/* "user@hostname" */
220
	int n, aflag = 0, retry = 0;
221
	struct passwd *pw;
222
223
#ifndef SMALL
224
	if (user == NULL && !anonftp) {
225
		if (ruserpass(host, &user, &pass, &acctname) < 0) {
226
			code = -1;
227
			return (0);
228
		}
229
	}
230
#endif /* !SMALL */
231
232
	/*
233
	 * Set up arguments for an anonymous FTP session, if necessary.
234
	 */
235
	if ((user == NULL || pass == NULL) && anonftp) {
236
		memset(anonpass, 0, sizeof(anonpass));
237
		memset(host_name, 0, sizeof(host_name));
238
239
		/*
240
		 * Set up anonymous login password.
241
		 */
242
		if ((user = getlogin()) == NULL) {
243
			if ((pw = getpwuid(getuid())) == NULL)
244
				user = "anonymous";
245
			else
246
				user = pw->pw_name;
247
		}
248
		gethostname(host_name, sizeof(host_name));
249
#ifndef DONT_CHEAT_ANONPASS
250
		/*
251
		 * Every anonymous FTP server I've encountered
252
		 * will accept the string "username@", and will
253
		 * append the hostname itself.  We do this by default
254
		 * since many servers are picky about not having
255
		 * a FQDN in the anonymous password. - thorpej@netbsd.org
256
		 */
257
		snprintf(anonpass, sizeof(anonpass) - 1, "%s@",
258
		    user);
259
#else
260
		snprintf(anonpass, sizeof(anonpass) - 1, "%s@%s",
261
		    user, hp->h_name);
262
#endif
263
		pass = anonpass;
264
		user = "anonymous";	/* as per RFC 1635 */
265
	}
266
267
tryagain:
268
	if (retry)
269
		user = "ftp";		/* some servers only allow "ftp" */
270
271
	while (user == NULL) {
272
		char *myname = getlogin();
273
274
		if (myname == NULL && (pw = getpwuid(getuid())) != NULL)
275
			myname = pw->pw_name;
276
		if (myname)
277
			fprintf(ttyout, "Name (%s:%s): ", host, myname);
278
		else
279
			fprintf(ttyout, "Name (%s): ", host);
280
		user = myname;
281
		if (fgets(tmp, sizeof(tmp), stdin) != NULL) {
282
			tmp[strcspn(tmp, "\n")] = '\0';
283
			if (tmp[0] != '\0')
284
				user = tmp;
285
		}
286
		else
287
			exit(0);
288
	}
289
	n = command("USER %s", user);
290
	if (n == CONTINUE) {
291
		if (pass == NULL)
292
			pass = getpass("Password:");
293
		n = command("PASS %s", pass);
294
	}
295
	if (n == CONTINUE) {
296
		aflag++;
297
		if (acctname == NULL)
298
			acctname = getpass("Account:");
299
		n = command("ACCT %s", acctname);
300
	}
301
	if ((n != COMPLETE) ||
302
	    (!aflag && acctname != NULL && command("ACCT %s", acctname) != COMPLETE)) {
303
		warnx("Login %s failed.", user);
304
		if (retry || !anonftp)
305
			return (0);
306
		else
307
			retry = 1;
308
		goto tryagain;
309
	}
310
	if (proxy)
311
		return (1);
312
	connected = -1;
313
#ifndef SMALL
314
	for (n = 0; n < macnum; ++n) {
315
		if (!strcmp("init", macros[n].mac_name)) {
316
			(void)strlcpy(line, "$init", sizeof line);
317
			makeargv();
318
			domacro(margc, margv);
319
			break;
320
		}
321
	}
322
#endif /* SMALL */
323
	return (1);
324
}
325
326
/*
327
 * `another' gets another argument, and stores the new argc and argv.
328
 * It reverts to the top level (via main.c's intr()) on EOF/error.
329
 *
330
 * Returns false if no new arguments have been added.
331
 */
332
#ifndef SMALL
333
int
334
another(int *pargc, char ***pargv, const char *prompt)
335
{
336
	int len = strlen(line), ret;
337
338
	if (len >= sizeof(line) - 3) {
339
		fputs("sorry, arguments too long.\n", ttyout);
340
		intr();
341
	}
342
	fprintf(ttyout, "(%s) ", prompt);
343
	line[len++] = ' ';
344
	if (fgets(&line[len], (int)(sizeof(line) - len), stdin) == NULL) {
345
		clearerr(stdin);
346
		intr();
347
	}
348
	len += strlen(&line[len]);
349
	if (len > 0 && line[len - 1] == '\n')
350
		line[len - 1] = '\0';
351
	makeargv();
352
	ret = margc > *pargc;
353
	*pargc = margc;
354
	*pargv = margv;
355
	return (ret);
356
}
357
#endif /* !SMALL */
358
359
/*
360
 * glob files given in argv[] from the remote server.
361
 * if errbuf isn't NULL, store error messages there instead
362
 * of writing to the screen.
363
 * if type isn't NULL, use LIST instead of NLST, and store filetype.
364
 * 'd' means directory, 's' means symbolic link, '-' means plain
365
 * file.
366
 */
367
char *
368
remglob2(char *argv[], int doswitch, char **errbuf, FILE **ftemp, char *type)
369
{
370
	char temp[PATH_MAX], *bufp, *cp, *lmode;
371
	static char buf[PATH_MAX], **args;
372
	int oldverbose, oldhash, fd;
373
374
	if (!mflag) {
375
		if (!doglob)
376
			args = NULL;
377
		else {
378
			if (*ftemp) {
379
				(void)fclose(*ftemp);
380
				*ftemp = NULL;
381
			}
382
		}
383
		return (NULL);
384
	}
385
	if (!doglob) {
386
		if (args == NULL)
387
			args = argv;
388
		if ((cp = *++args) == NULL)
389
			args = NULL;
390
		return (cp);
391
	}
392
	if (*ftemp == NULL) {
393
		int len;
394
395
		cp = _PATH_TMP;
396
		len = strlen(cp);
397
		if (len + sizeof(TMPFILE) + (cp[len-1] != '/') > sizeof(temp)) {
398
			warnx("unable to create temporary file: %s",
399
			    strerror(ENAMETOOLONG));
400
			return (NULL);
401
		}
402
403
		(void)strlcpy(temp, cp, sizeof temp);
404
		if (temp[len-1] != '/')
405
			temp[len++] = '/';
406
		(void)strlcpy(&temp[len], TMPFILE, sizeof temp - len);
407
		if ((fd = mkstemp(temp)) < 0) {
408
			warn("unable to create temporary file: %s", temp);
409
			return (NULL);
410
		}
411
		close(fd);
412
		oldverbose = verbose;
413
		verbose = (errbuf != NULL) ? -1 : 0;
414
		oldhash = hash;
415
		hash = 0;
416
		if (doswitch)
417
			pswitch(!proxy);
418
		for (lmode = "w"; *++argv != NULL; lmode = "a")
419
			recvrequest(type ? "LIST" : "NLST", temp, *argv, lmode,
420
			    0, 0);
421
		if ((code / 100) != COMPLETE) {
422
			if (errbuf != NULL)
423
				*errbuf = reply_string;
424
		}
425
		if (doswitch)
426
			pswitch(!proxy);
427
		verbose = oldverbose;
428
		hash = oldhash;
429
		*ftemp = fopen(temp, "r");
430
		(void)unlink(temp);
431
		if (*ftemp == NULL) {
432
			if (errbuf == NULL)
433
				fputs("can't find list of remote files, oops.\n",
434
				    ttyout);
435
			else
436
				*errbuf =
437
				    "can't find list of remote files, oops.";
438
			return (NULL);
439
		}
440
	}
441
#ifndef SMALL
442
again:
443
#endif
444
	if (fgets(buf, sizeof(buf), *ftemp) == NULL) {
445
		(void)fclose(*ftemp);
446
		*ftemp = NULL;
447
		return (NULL);
448
	}
449
450
	buf[strcspn(buf, "\n")] = '\0';
451
	bufp = buf;
452
453
#ifndef SMALL
454
	if (type) {
455
		parse_list(&bufp, type);
456
		if (!bufp ||
457
		    (bufp[0] == '.' &&	/* LIST defaults to -a on some ftp */
458
		    (bufp[1] == '\0' ||	/* servers.  Ignore '.' and '..'. */
459
		    (bufp[1] == '.' && bufp[2] == '\0'))))
460
			goto again;
461
	}
462
#endif /* !SMALL */
463
464
	return (bufp);
465
}
466
467
/*
468
 * wrapper for remglob2
469
 */
470
char *
471
remglob(char *argv[], int doswitch, char **errbuf)
472
{
473
	static FILE *ftemp = NULL;
474
475
	return remglob2(argv, doswitch, errbuf, &ftemp, NULL);
476
}
477
478
#ifndef SMALL
479
int
480
confirm(const char *cmd, const char *file)
481
{
482
	char str[BUFSIZ];
483
484
	if (file && (confirmrest || !interactive))
485
		return (1);
486
top:
487
	if (file)
488
		fprintf(ttyout, "%s %s? ", cmd, file);
489
	else
490
		fprintf(ttyout, "Continue with %s? ", cmd);
491
	(void)fflush(ttyout);
492
	if (fgets(str, sizeof(str), stdin) == NULL)
493
		goto quit;
494
	switch (tolower((unsigned char)*str)) {
495
		case '?':
496
			fprintf(ttyout,
497
			    "?	help\n"
498
			    "a	answer yes to all\n"
499
			    "n	answer no\n"
500
			    "p	turn off prompt mode\n"
501
			    "q	answer no to all\n"
502
			    "y	answer yes\n");
503
			goto top;
504
		case 'a':
505
			confirmrest = 1;
506
			fprintf(ttyout, "Prompting off for duration of %s.\n",
507
			    cmd);
508
			break;
509
		case 'n':
510
			return (0);
511
		case 'p':
512
			interactive = 0;
513
			fputs("Interactive mode: off.\n", ttyout);
514
			break;
515
		case 'q':
516
quit:
517
			mflag = 0;
518
			clearerr(stdin);
519
			return (0);
520
		case 'y':
521
			return(1);
522
			break;
523
		default:
524
			fprintf(ttyout, "?, a, n, p, q, y "
525
			    "are the only acceptable commands!\n");
526
			goto top;
527
			break;
528
	}
529
	return (1);
530
}
531
#endif /* !SMALL */
532
533
/*
534
 * Glob a local file name specification with
535
 * the expectation of a single return value.
536
 * Can't control multiple values being expanded
537
 * from the expression, we return only the first.
538
 */
539
int
540
globulize(char **cpp)
541
{
542
	glob_t gl;
543
	int flags;
544
545
	if (!doglob)
546
		return (1);
547
548
	flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
549
	memset(&gl, 0, sizeof(gl));
550
	if (glob(*cpp, flags, NULL, &gl) ||
551
	    gl.gl_pathc == 0) {
552
		warnx("%s: not found", *cpp);
553
		globfree(&gl);
554
		return (0);
555
	}
556
		/* XXX: caller should check if *cpp changed, and
557
		 *	free(*cpp) if that is the case
558
		 */
559
	*cpp = strdup(gl.gl_pathv[0]);
560
	if (*cpp == NULL)
561
		err(1, NULL);
562
	globfree(&gl);
563
	return (1);
564
}
565
566
/*
567
 * determine size of remote file
568
 */
569
off_t
570
remotesize(const char *file, int noisy)
571
{
572
	int overbose;
573
	off_t size;
574
575
	overbose = verbose;
576
	size = -1;
577
#ifndef SMALL
578
	if (!debug)
579
#endif /* !SMALL */
580
		verbose = -1;
581
	if (command("SIZE %s", file) == COMPLETE) {
582
		char *cp, *ep;
583
584
		cp = strchr(reply_string, ' ');
585
		if (cp != NULL) {
586
			cp++;
587
			size = strtoll(cp, &ep, 10);
588
			if (*ep != '\0' && !isspace((unsigned char)*ep))
589
				size = -1;
590
		}
591
	} else if (noisy
592
#ifndef SMALL
593
	    && !debug
594
#endif /* !SMALL */
595
	    ) {
596
		fputs(reply_string, ttyout);
597
		fputc('\n', ttyout);
598
	}
599
	verbose = overbose;
600
	return (size);
601
}
602
603
/*
604
 * determine last modification time (in GMT) of remote file
605
 */
606
time_t
607
remotemodtime(const char *file, int noisy)
608
{
609
	int overbose;
610
	time_t rtime;
611
	int ocode;
612
613
	overbose = verbose;
614
	ocode = code;
615
	rtime = -1;
616
#ifndef SMALL
617
	if (!debug)
618
#endif /* !SMALL */
619
		verbose = -1;
620
	if (command("MDTM %s", file) == COMPLETE) {
621
		struct tm timebuf;
622
		int yy, mo, day, hour, min, sec;
623
		/*
624
		 * time-val = 14DIGIT [ "." 1*DIGIT ]
625
		 *		YYYYMMDDHHMMSS[.sss]
626
		 * mdtm-response = "213" SP time-val CRLF / error-response
627
		 */
628
		/* TODO: parse .sss as well, use timespecs. */
629
		char *timestr = reply_string;
630
631
		/* Repair `19%02d' bug on server side */
632
		while (!isspace((unsigned char)*timestr))
633
			timestr++;
634
		while (isspace((unsigned char)*timestr))
635
			timestr++;
636
		if (strncmp(timestr, "191", 3) == 0) {
637
			fprintf(ttyout,
638
	    "Y2K warning! Fixed incorrect time-val received from server.\n");
639
			timestr[0] = ' ';
640
			timestr[1] = '2';
641
			timestr[2] = '0';
642
		}
643
		sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo,
644
			&day, &hour, &min, &sec);
645
		memset(&timebuf, 0, sizeof(timebuf));
646
		timebuf.tm_sec = sec;
647
		timebuf.tm_min = min;
648
		timebuf.tm_hour = hour;
649
		timebuf.tm_mday = day;
650
		timebuf.tm_mon = mo - 1;
651
		timebuf.tm_year = yy - 1900;
652
		timebuf.tm_isdst = -1;
653
		rtime = mktime(&timebuf);
654
		if (rtime == -1 && (noisy
655
#ifndef SMALL
656
		    || debug
657
#endif /* !SMALL */
658
		    ))
659
			fprintf(ttyout, "Can't convert %s to a time.\n", reply_string);
660
		else
661
			rtime += timebuf.tm_gmtoff;	/* conv. local -> GMT */
662
	} else if (noisy
663
#ifndef SMALL
664
	    && !debug
665
#endif /* !SMALL */
666
	    ) {
667
		fputs(reply_string, ttyout);
668
		fputc('\n', ttyout);
669
	}
670
	verbose = overbose;
671
	if (rtime == -1)
672
		code = ocode;
673
	return (rtime);
674
}
675
676
/*
677
 * Ensure file is in or under dir.
678
 * Returns 1 if so, 0 if not (or an error occurred).
679
 */
680
int
681
fileindir(const char *file, const char *dir)
682
{
683
	char	parentdirbuf[PATH_MAX], *parentdir;
684
	char	realdir[PATH_MAX];
685
	size_t	dirlen;
686
687
					/* determine parent directory of file */
688
	(void)strlcpy(parentdirbuf, file, sizeof(parentdirbuf));
689
	parentdir = dirname(parentdirbuf);
690
	if (strcmp(parentdir, ".") == 0)
691
		return 1;		/* current directory is ok */
692
693
					/* find the directory */
694
	if (realpath(parentdir, realdir) == NULL) {
695
		warn("Unable to determine real path of `%s'", parentdir);
696
		return 0;
697
	}
698
	if (realdir[0] != '/')		/* relative result is ok */
699
		return 1;
700
701
	dirlen = strlen(dir);
702
	if (strncmp(realdir, dir, dirlen) == 0 &&
703
	    (realdir[dirlen] == '/' || realdir[dirlen] == '\0'))
704
		return 1;
705
	return 0;
706
}
707
708
709
/*
710
 * Returns true if this is the controlling/foreground process, else false.
711
 */
712
int
713
foregroundproc(void)
714
{
715
	static pid_t pgrp = -1;
716
	int ctty_pgrp;
717
718
	if (pgrp == -1)
719
		pgrp = getpgrp();
720
721
	return((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 &&
722
	    ctty_pgrp == pgrp));
723
}
724
725
/* ARGSUSED */
726
static void
727
updateprogressmeter(int signo)
728
{
729
	int save_errno = errno;
730
731
	/* update progressmeter if foreground process or in -m mode */
732
	if (foregroundproc() || progress == -1)
733
		progressmeter(0, NULL);
734
	errno = save_errno;
735
}
736
737
/*
738
 * Display a transfer progress bar if progress is non-zero.
739
 * SIGALRM is hijacked for use by this function.
740
 * - Before the transfer, set filesize to size of file (or -1 if unknown),
741
 *   and call with flag = -1. This starts the once per second timer,
742
 *   and a call to updateprogressmeter() upon SIGALRM.
743
 * - During the transfer, updateprogressmeter will call progressmeter
744
 *   with flag = 0
745
 * - After the transfer, call with flag = 1
746
 */
747
static struct timeval start;
748
749
char *action;
750
751
void
752
progressmeter(int flag, const char *filename)
753
{
754
	/*
755
	 * List of order of magnitude prefixes.
756
	 * The last is `P', as 2^64 = 16384 Petabytes
757
	 */
758
	static const char prefixes[] = " KMGTP";
759
760
	static struct timeval lastupdate;
761
	static off_t lastsize;
762
	static char *title = NULL;
763
	struct timeval now, td, wait;
764
	off_t cursize, abbrevsize;
765
	double elapsed;
766
	int ratio, barlength, i, remaining, overhead = 30;
767
	char buf[512];
768
769
	if (flag == -1) {
770
		(void)gettimeofday(&start, NULL);
771
		lastupdate = start;
772
		lastsize = restart_point;
773
	}
774
	(void)gettimeofday(&now, NULL);
775
	if (!progress || filesize < 0)
776
		return;
777
	cursize = bytes + restart_point;
778
779
	if (filesize)
780
		ratio = cursize * 100 / filesize;
781
	else
782
		ratio = 100;
783
	ratio = MAXIMUM(ratio, 0);
784
	ratio = MINIMUM(ratio, 100);
785
	if (!verbose && flag == -1) {
786
		filename = basename(filename);
787
		if (filename != NULL) {
788
			free(title);
789
			title = strdup(filename);
790
		}
791
	}
792
793
	buf[0] = 0;
794
	if (!verbose && action != NULL) {
795
		int l = strlen(action);
796
		char *dotdot = "";
797
798
		if (l < 7)
799
			l = 7;
800
		else if (l > 12) {
801
			l = 12;
802
			dotdot = "...";
803
			overhead += 3;
804
		}
805
		snprintf(buf, sizeof(buf), "\r%-*.*s%s ", l, l, action,
806
		    dotdot);
807
		overhead += l + 1;
808
	} else
809
		snprintf(buf, sizeof(buf), "\r");
810
811
	if (!verbose && title != NULL) {
812
		int l = strlen(title);
813
		char *dotdot = "";
814
815
		if (l < 12)
816
			l = 12;
817
		else if (l > 25) {
818
			l = 22;
819
			dotdot = "...";
820
			overhead += 3;
821
		}
822
		snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
823
		    "%-*.*s%s %3d%% ", l, l, title,
824
		    dotdot, ratio);
825
		overhead += l + 1;
826
	} else
827
		snprintf(buf, sizeof(buf), "\r%3d%% ", ratio);
828
829
	barlength = ttywidth - overhead;
830
	if (barlength > 0) {
831
		i = barlength * ratio / 100;
832
		snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
833
		    "|%.*s%*s|", i,
834
		    "*******************************************************"
835
		    "*******************************************************"
836
		    "*******************************************************"
837
		    "*******************************************************"
838
		    "*******************************************************"
839
		    "*******************************************************"
840
		    "*******************************************************",
841
		    barlength - i, "");
842
	}
843
844
	i = 0;
845
	abbrevsize = cursize;
846
	while (abbrevsize >= 100000 && i < sizeof(prefixes)-1) {
847
		i++;
848
		abbrevsize >>= 10;
849
	}
850
	snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
851
	    " %5lld %c%c ", (long long)abbrevsize, prefixes[i],
852
	    prefixes[i] == ' ' ? ' ' : 'B');
853
854
	timersub(&now, &lastupdate, &wait);
855
	if (cursize > lastsize) {
856
		lastupdate = now;
857
		lastsize = cursize;
858
		if (wait.tv_sec >= STALLTIME) {	/* fudge out stalled time */
859
			start.tv_sec += wait.tv_sec;
860
			start.tv_usec += wait.tv_usec;
861
		}
862
		wait.tv_sec = 0;
863
	}
864
865
	timersub(&now, &start, &td);
866
	elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
867
868
	if (flag == 1) {
869
		i = (int)elapsed / 3600;
870
		if (i)
871
			snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
872
			    "%2d:", i);
873
		else
874
			snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
875
			    "   ");
876
		i = (int)elapsed % 3600;
877
		snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
878
		    "%02d:%02d    ", i / 60, i % 60);
879
	} else if (bytes <= 0 || elapsed <= 0.0 || cursize > filesize) {
880
		snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
881
		    "   --:-- ETA");
882
	} else if (wait.tv_sec >= STALLTIME) {
883
		snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
884
		    " - stalled -");
885
	} else {
886
		remaining = (int)((filesize - restart_point) /
887
				  (bytes / elapsed) - elapsed);
888
		i = remaining / 3600;
889
		if (i)
890
			snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
891
			    "%2d:", i);
892
		else
893
			snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
894
			    "   ");
895
		i = remaining % 3600;
896
		snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
897
		    "%02d:%02d ETA", i / 60, i % 60);
898
	}
899
	(void)write(fileno(ttyout), buf, strlen(buf));
900
901
	if (flag == -1) {
902
		(void)signal(SIGALRM, updateprogressmeter);
903
		alarmtimer(1);		/* set alarm timer for 1 Hz */
904
	} else if (flag == 1) {
905
		alarmtimer(0);
906
		(void)putc('\n', ttyout);
907
		free(title);
908
		title = NULL;
909
	}
910
	fflush(ttyout);
911
}
912
913
/*
914
 * Display transfer statistics.
915
 * Requires start to be initialised by progressmeter(-1),
916
 * direction to be defined by xfer routines, and filesize and bytes
917
 * to be updated by xfer routines
918
 * If siginfo is nonzero, an ETA is displayed, and the output goes to STDERR
919
 * instead of TTYOUT.
920
 */
921
void
922
ptransfer(int siginfo)
923
{
924
	struct timeval now, td;
925
	double elapsed;
926
	off_t bs;
927
	int meg, remaining, hh;
928
	char buf[100];
929
930
	if (!verbose && !siginfo)
931
		return;
932
933
	(void)gettimeofday(&now, NULL);
934
	timersub(&now, &start, &td);
935
	elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
936
	bs = bytes / (elapsed == 0.0 ? 1 : elapsed);
937
	meg = 0;
938
	if (bs > (1024 * 1024))
939
		meg = 1;
940
941
	/* XXX floating point printf in signal handler */
942
	(void)snprintf(buf, sizeof(buf),
943
	    "%lld byte%s %s in %.2f seconds (%.2f %sB/s)\n",
944
	    (long long)bytes, bytes == 1 ? "" : "s", direction, elapsed,
945
	    bs / (1024.0 * (meg ? 1024.0 : 1.0)), meg ? "M" : "K");
946
947
	if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0 &&
948
	    bytes + restart_point <= filesize) {
949
		remaining = (int)((filesize - restart_point) /
950
		    (bytes / elapsed) - elapsed);
951
		hh = remaining / 3600;
952
		remaining %= 3600;
953
954
		/* "buf+len(buf) -1" to overwrite \n */
955
		snprintf(buf + strlen(buf) - 1, sizeof(buf) - strlen(buf),
956
		    "  ETA: %02d:%02d:%02d\n", hh, remaining / 60,
957
		    remaining % 60);
958
	}
959
	(void)write(siginfo ? STDERR_FILENO : fileno(ttyout), buf, strlen(buf));
960
}
961
962
/*
963
 * List words in stringlist, vertically arranged
964
 */
965
#ifndef SMALL
966
void
967
list_vertical(StringList *sl)
968
{
969
	int i, j, w;
970
	int columns, width, lines;
971
	char *p;
972
973
	width = 0;
974
975
	for (i = 0 ; i < sl->sl_cur ; i++) {
976
		w = strlen(sl->sl_str[i]);
977
		if (w > width)
978
			width = w;
979
	}
980
	width = (width + 8) &~ 7;
981
982
	columns = ttywidth / width;
983
	if (columns == 0)
984
		columns = 1;
985
	lines = (sl->sl_cur + columns - 1) / columns;
986
	for (i = 0; i < lines; i++) {
987
		for (j = 0; j < columns; j++) {
988
			p = sl->sl_str[j * lines + i];
989
			if (p)
990
				fputs(p, ttyout);
991
			if (j * lines + i + lines >= sl->sl_cur) {
992
				putc('\n', ttyout);
993
				break;
994
			}
995
			w = strlen(p);
996
			while (w < width) {
997
				w = (w + 8) &~ 7;
998
				(void)putc('\t', ttyout);
999
			}
1000
		}
1001
	}
1002
}
1003
#endif /* !SMALL */
1004
1005
/*
1006
 * Update the global ttywidth value, using TIOCGWINSZ.
1007
 */
1008
/* ARGSUSED */
1009
void
1010
setttywidth(int signo)
1011
{
1012
30
	int save_errno = errno;
1013
15
	struct winsize winsize;
1014
1015

45
	if (ioctl(fileno(ttyout), TIOCGWINSZ, &winsize) != -1)
1016
		ttywidth = winsize.ws_col ? winsize.ws_col : 80;
1017
	else
1018
		ttywidth = 80;
1019
15
	errno = save_errno;
1020
15
}
1021
1022
/*
1023
 * Set the SIGALRM interval timer for wait seconds, 0 to disable.
1024
 */
1025
void
1026
alarmtimer(int wait)
1027
{
1028
	int save_errno = errno;
1029
	struct itimerval itv;
1030
1031
	itv.it_value.tv_sec = wait;
1032
	itv.it_value.tv_usec = 0;
1033
	itv.it_interval = itv.it_value;
1034
	setitimer(ITIMER_REAL, &itv, NULL);
1035
	errno = save_errno;
1036
}
1037
1038
/*
1039
 * Setup or cleanup EditLine structures
1040
 */
1041
#ifndef SMALL
1042
void
1043
controlediting(void)
1044
{
1045
	HistEvent hev;
1046
1047
	if (editing && el == NULL && hist == NULL) {
1048
		el = el_init(__progname, stdin, ttyout, stderr); /* init editline */
1049
		hist = history_init();		/* init the builtin history */
1050
		history(hist, &hev, H_SETSIZE, 100);	/* remember 100 events */
1051
		el_set(el, EL_HIST, history, hist);	/* use history */
1052
1053
		el_set(el, EL_EDITOR, "emacs");	/* default editor is emacs */
1054
		el_set(el, EL_PROMPT, prompt);	/* set the prompt function */
1055
1056
		/* add local file completion, bind to TAB */
1057
		el_set(el, EL_ADDFN, "ftp-complete",
1058
		    "Context sensitive argument completion",
1059
		    complete);
1060
		el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1061
1062
		el_source(el, NULL);	/* read ~/.editrc */
1063
		el_set(el, EL_SIGNAL, 1);
1064
	} else if (!editing) {
1065
		if (hist) {
1066
			history_end(hist);
1067
			hist = NULL;
1068
		}
1069
		if (el) {
1070
			el_end(el);
1071
			el = NULL;
1072
		}
1073
	}
1074
}
1075
#endif /* !SMALL */
1076
1077
/*
1078
 * Wait for an asynchronous connect(2) attempt to finish.
1079
 */
1080
int
1081
connect_wait(int s)
1082
{
1083
	struct pollfd pfd[1];
1084
	int error = 0;
1085
	socklen_t len = sizeof(error);
1086
1087
	pfd[0].fd = s;
1088
	pfd[0].events = POLLOUT;
1089
1090
	if (poll(pfd, 1, -1) == -1)
1091
		return -1;
1092
	if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
1093
		return -1;
1094
	if (error != 0) {
1095
		errno = error;
1096
		return -1;
1097
	}
1098
	return 0;
1099
}