GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/ftp/util.c Lines: 0 472 0.0 %
Date: 2016-12-06 Branches: 0 381 0.0 %

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