Bug Summary

File:src/usr.bin/ftp/util.c
Warning:line 997, column 8
Null pointer passed as 1st argument to string length function

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name util.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/ftp/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/ftp/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/ftp/util.c
1/* $OpenBSD: util.c,v 1.95 2021/02/02 12:58:42 robert 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(*__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)) (((a) < (b)) ? (a) : (b))
93#define MAXIMUM(a, b)(((a) > (b)) ? (a) : (b)) (((a) > (b)) ? (a) : (b))
94
95static void updateprogressmeter(int);
96
97/*
98 * Connect to peer server and
99 * auto-login, if possible.
100 */
101void
102setpeer(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((void*)0) || *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]) != COMPLETE2)
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_N1;
153 (void)strlcpy(modename, "stream", sizeof modename);
154 mode = MODE_S1;
155 (void)strlcpy(structname, "file", sizeof structname);
156 stru = STRU_F1;
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_A1;
168 type = 0;
169 if (autologin)
170 (void)ftp_login(argv[1], NULL((void*)0), NULL((void*)0));
171
172 overbose = verbose;
173#ifndef SMALL
174 if (!debug)
175#endif /* !SMALL */
176 verbose = -1;
177 if (command("SYST") == COMPLETE2 && overbose) {
178 char *cp, c;
179 c = 0;
180 cp = strchr(reply_string + 4, ' ');
181 if (cp == NULL((void*)0))
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 */
215int
216ftp_login(const char *host, char *user, char *pass)
217{
218 char tmp[80], *acctname = NULL((void*)0), host_name[HOST_NAME_MAX255+1];
219 char anonpass[LOGIN_NAME_MAX32 + 1 + HOST_NAME_MAX255+1]; /* "user@hostname" */
220 int n, aflag = 0, retry = 0;
221 struct passwd *pw;
222
223#ifndef SMALL
224 if (user == NULL((void*)0) && !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((void*)0) || pass == NULL((void*)0)) && 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((void*)0)) {
243 if ((pw = getpwuid(getuid())) == NULL((void*)0))
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
267tryagain:
268 if (retry)
269 user = "ftp"; /* some servers only allow "ftp" */
270
271 while (user == NULL((void*)0)) {
272 char *myname = getlogin();
273
274 if (myname == NULL((void*)0) && (pw = getpwuid(getuid())) != NULL((void*)0))
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(&__sF[0])) != NULL((void*)0)) {
282 tmp[strcspn(tmp, "\n")] = '\0';
283 if (tmp[0] != '\0')
284 user = tmp;
285 } else
286 exit(0);
287 }
288 n = command("USER %s", user);
289 if (n == CONTINUE3) {
290 if (pass == NULL((void*)0))
291 pass = getpass("Password:");
292 n = command("PASS %s", pass);
293 }
294 if (n == CONTINUE3) {
295 aflag++;
296 if (acctname == NULL((void*)0))
297 acctname = getpass("Account:");
298 n = command("ACCT %s", acctname);
299 }
300 if ((n != COMPLETE2) ||
301 (!aflag && acctname != NULL((void*)0) && command("ACCT %s", acctname) != COMPLETE2)) {
302 warnx("Login %s failed.", user);
303 if (retry || !anonftp)
304 return (0);
305 else
306 retry = 1;
307 goto tryagain;
308 }
309 if (proxy)
310 return (1);
311 connected = -1;
312#ifndef SMALL
313 for (n = 0; n < macnum; ++n) {
314 if (!strcmp("init", macros[n].mac_name)) {
315 (void)strlcpy(line, "$init", sizeof line);
316 makeargv();
317 domacro(margc, margv(marg_sl->sl_str));
318 break;
319 }
320 }
321#endif /* SMALL */
322 return (1);
323}
324
325/*
326 * `another' gets another argument, and stores the new argc and argv.
327 * It reverts to the top level (via main.c's intr()) on EOF/error.
328 *
329 * Returns false if no new arguments have been added.
330 */
331#ifndef SMALL
332int
333another(int *pargc, char ***pargv, const char *prompt)
334{
335 int len = strlen(line), ret;
336
337 if (len >= sizeof(line) - 3) {
338 fputs("sorry, arguments too long.\n", ttyout);
339 intr();
340 }
341 fprintf(ttyout, "(%s) ", prompt);
342 line[len++] = ' ';
343 if (fgets(&line[len], (int)(sizeof(line) - len), stdin(&__sF[0])) == NULL((void*)0)) {
344 clearerr(stdin)(!__isthreaded ? ((void)(((&__sF[0]))->_flags &= ~
(0x0040|0x0020))) : (clearerr)((&__sF[0])))
;
345 intr();
346 }
347 len += strlen(&line[len]);
348 if (len > 0 && line[len - 1] == '\n')
349 line[len - 1] = '\0';
350 makeargv();
351 ret = margc > *pargc;
352 *pargc = margc;
353 *pargv = margv(marg_sl->sl_str);
354 return (ret);
355}
356#endif /* !SMALL */
357
358/*
359 * glob files given in argv[] from the remote server.
360 * if errbuf isn't NULL, store error messages there instead
361 * of writing to the screen.
362 * if type isn't NULL, use LIST instead of NLST, and store filetype.
363 * 'd' means directory, 's' means symbolic link, '-' means plain
364 * file.
365 */
366char *
367remglob2(char *argv[], int doswitch, char **errbuf, FILE **ftemp, char *type)
368{
369 char temp[PATH_MAX1024], *bufp, *cp, *lmode;
370 static char buf[PATH_MAX1024], **args;
371 int oldverbose, oldhash, fd;
372
373 if (!mflag) {
374 if (!doglob)
375 args = NULL((void*)0);
376 else {
377 if (*ftemp) {
378 (void)fclose(*ftemp);
379 *ftemp = NULL((void*)0);
380 }
381 }
382 return (NULL((void*)0));
383 }
384 if (!doglob) {
385 if (args == NULL((void*)0))
386 args = argv;
387 if ((cp = *++args) == NULL((void*)0))
388 args = NULL((void*)0);
389 return (cp);
390 }
391 if (*ftemp == NULL((void*)0)) {
392 int len;
393
394 cp = _PATH_TMP"/tmp/";
395 len = strlen(cp);
396 if (len + sizeof(TMPFILE"ftpXXXXXXXXXX") + (cp[len-1] != '/') > sizeof(temp)) {
397 warnx("unable to create temporary file: %s",
398 strerror(ENAMETOOLONG63));
399 return (NULL((void*)0));
400 }
401
402 (void)strlcpy(temp, cp, sizeof temp);
403 if (temp[len-1] != '/')
404 temp[len++] = '/';
405 (void)strlcpy(&temp[len], TMPFILE"ftpXXXXXXXXXX", sizeof temp - len);
406 if ((fd = mkstemp(temp)) == -1) {
407 warn("unable to create temporary file: %s", temp);
408 return (NULL((void*)0));
409 }
410 close(fd);
411 oldverbose = verbose;
412 verbose = (errbuf != NULL((void*)0)) ? -1 : 0;
413 oldhash = hash;
414 hash = 0;
415 if (doswitch)
416 pswitch(!proxy);
417 for (lmode = "w"; *++argv != NULL((void*)0); lmode = "a")
418 recvrequest(type ? "LIST" : "NLST", temp, *argv, lmode,
419 0, 0);
420 if ((code / 100) != COMPLETE2) {
421 if (errbuf != NULL((void*)0))
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((void*)0)) {
431 if (errbuf == NULL((void*)0))
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((void*)0));
438 }
439 }
440#ifndef SMALL
441again:
442#endif
443 if (fgets(buf, sizeof(buf), *ftemp) == NULL((void*)0)) {
444 (void)fclose(*ftemp);
445 *ftemp = NULL((void*)0);
446 return (NULL((void*)0));
447 }
448
449 buf[strcspn(buf, "\n")] = '\0';
450 bufp = buf;
451
452#ifndef SMALL
453 if (type) {
454 parse_list(&bufp, type);
455 if (!bufp ||
456 (bufp[0] == '.' && /* LIST defaults to -a on some ftp */
457 (bufp[1] == '\0' || /* servers. Ignore '.' and '..'. */
458 (bufp[1] == '.' && bufp[2] == '\0'))))
459 goto again;
460 }
461#endif /* !SMALL */
462
463 return (bufp);
464}
465
466/*
467 * wrapper for remglob2
468 */
469char *
470remglob(char *argv[], int doswitch, char **errbuf)
471{
472 static FILE *ftemp = NULL((void*)0);
473
474 return remglob2(argv, doswitch, errbuf, &ftemp, NULL((void*)0));
475}
476
477#ifndef SMALL
478int
479confirm(const char *cmd, const char *file)
480{
481 char str[BUFSIZ1024];
482
483 if (file && (confirmrest || !interactive))
484 return (1);
485top:
486 if (file)
487 fprintf(ttyout, "%s %s? ", cmd, file);
488 else
489 fprintf(ttyout, "Continue with %s? ", cmd);
490 (void)fflush(ttyout);
491 if (fgets(str, sizeof(str), stdin(&__sF[0])) == NULL((void*)0))
492 goto quit;
493 switch (tolower((unsigned char)*str)) {
494 case '?':
495 fprintf(ttyout,
496 "? help\n"
497 "a answer yes to all\n"
498 "n answer no\n"
499 "p turn off prompt mode\n"
500 "q answer no to all\n"
501 "y answer yes\n");
502 goto top;
503 case 'a':
504 confirmrest = 1;
505 fprintf(ttyout, "Prompting off for duration of %s.\n",
506 cmd);
507 break;
508 case 'n':
509 return (0);
510 case 'p':
511 interactive = 0;
512 fputs("Interactive mode: off.\n", ttyout);
513 break;
514 case 'q':
515quit:
516 mflag = 0;
517 clearerr(stdin)(!__isthreaded ? ((void)(((&__sF[0]))->_flags &= ~
(0x0040|0x0020))) : (clearerr)((&__sF[0])))
;
518 return (0);
519 case 'y':
520 return(1);
521 break;
522 default:
523 fprintf(ttyout, "?, a, n, p, q, y "
524 "are the only acceptable commands!\n");
525 goto top;
526 break;
527 }
528 return (1);
529}
530#endif /* !SMALL */
531
532/*
533 * Glob a local file name specification with
534 * the expectation of a single return value.
535 * Can't control multiple values being expanded
536 * from the expression, we return only the first.
537 */
538int
539globulize(char **cpp)
540{
541 glob_t gl;
542 int flags;
543
544 if (!doglob)
545 return (1);
546
547 flags = GLOB_BRACE0x0080|GLOB_NOCHECK0x0010|GLOB_QUOTE0x0400|GLOB_TILDE0x0800;
548 memset(&gl, 0, sizeof(gl));
549 if (glob(*cpp, flags, NULL((void*)0), &gl) ||
550 gl.gl_pathc == 0) {
551 warnx("%s: not found", *cpp);
552 globfree(&gl);
553 return (0);
554 }
555 /* XXX: caller should check if *cpp changed, and
556 * free(*cpp) if that is the case
557 */
558 *cpp = strdup(gl.gl_pathv[0]);
559 if (*cpp == NULL((void*)0))
560 err(1, NULL((void*)0));
561 globfree(&gl);
562 return (1);
563}
564
565/*
566 * determine size of remote file
567 */
568off_t
569remotesize(const char *file, int noisy)
570{
571 int overbose;
572 off_t size;
573
574 overbose = verbose;
575 size = -1;
576#ifndef SMALL
577 if (!debug)
578#endif /* !SMALL */
579 verbose = -1;
580 if (command("SIZE %s", file) == COMPLETE2) {
581 char *cp, *ep;
582
583 cp = strchr(reply_string, ' ');
584 if (cp != NULL((void*)0)) {
585 cp++;
586 size = strtoll(cp, &ep, 10);
587 if (*ep != '\0' && !isspace((unsigned char)*ep))
588 size = -1;
589 }
590 } else if (noisy
591#ifndef SMALL
592 && !debug
593#endif /* !SMALL */
594 ) {
595 fputs(reply_string, ttyout);
596 fputc('\n', ttyout);
597 }
598 verbose = overbose;
599 return (size);
600}
601
602/*
603 * determine last modification time (in GMT) of remote file
604 */
605time_t
606remotemodtime(const char *file, int noisy)
607{
608 int overbose;
609 time_t rtime;
610 int ocode;
611
612 overbose = verbose;
613 ocode = code;
614 rtime = -1;
615#ifndef SMALL
616 if (!debug)
617#endif /* !SMALL */
618 verbose = -1;
619 if (command("MDTM %s", file) == COMPLETE2) {
620 struct tm timebuf;
621 int yy, mo, day, hour, min, sec;
622 /*
623 * time-val = 14DIGIT [ "." 1*DIGIT ]
624 * YYYYMMDDHHMMSS[.sss]
625 * mdtm-response = "213" SP time-val CRLF / error-response
626 */
627 /* TODO: parse .sss as well, use timespecs. */
628 char *timestr = reply_string;
629
630 /* Repair `19%02d' bug on server side */
631 while (!isspace((unsigned char)*timestr))
632 timestr++;
633 while (isspace((unsigned char)*timestr))
634 timestr++;
635 if (strncmp(timestr, "191", 3) == 0) {
636 fprintf(ttyout,
637 "Y2K warning! Fixed incorrect time-val received from server.\n");
638 timestr[0] = ' ';
639 timestr[1] = '2';
640 timestr[2] = '0';
641 }
642 sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo,
643 &day, &hour, &min, &sec);
644 memset(&timebuf, 0, sizeof(timebuf));
645 timebuf.tm_sec = sec;
646 timebuf.tm_min = min;
647 timebuf.tm_hour = hour;
648 timebuf.tm_mday = day;
649 timebuf.tm_mon = mo - 1;
650 timebuf.tm_year = yy - 1900;
651 timebuf.tm_isdst = -1;
652 rtime = mktime(&timebuf);
653 if (rtime == -1 && (noisy
654#ifndef SMALL
655 || debug
656#endif /* !SMALL */
657 ))
658 fprintf(ttyout, "Can't convert %s to a time.\n", reply_string);
659 else
660 rtime += timebuf.tm_gmtoff; /* conv. local -> GMT */
661 } else if (noisy
662#ifndef SMALL
663 && !debug
664#endif /* !SMALL */
665 ) {
666 fputs(reply_string, ttyout);
667 fputc('\n', ttyout);
668 }
669 verbose = overbose;
670 if (rtime == -1)
671 code = ocode;
672 return (rtime);
673}
674
675/*
676 * Ensure file is in or under dir.
677 * Returns 1 if so, 0 if not (or an error occurred).
678 */
679int
680fileindir(const char *file, const char *dir)
681{
682 char parentdirbuf[PATH_MAX1024], *parentdir;
683 char realdir[PATH_MAX1024];
684 size_t dirlen;
685
686 /* determine parent directory of file */
687 (void)strlcpy(parentdirbuf, file, sizeof(parentdirbuf));
688 parentdir = dirname(parentdirbuf);
689 if (strcmp(parentdir, ".") == 0)
690 return 1; /* current directory is ok */
691
692 /* find the directory */
693 if (realpath(parentdir, realdir) == NULL((void*)0)) {
694 warn("Unable to determine real path of `%s'", parentdir);
695 return 0;
696 }
697 if (realdir[0] != '/') /* relative result is ok */
698 return 1;
699
700 dirlen = strlen(dir);
701 if (strncmp(realdir, dir, dirlen) == 0 &&
702 (realdir[dirlen] == '/' || realdir[dirlen] == '\0'))
703 return 1;
704 return 0;
705}
706
707
708/*
709 * Returns true if this is the controlling/foreground process, else false.
710 */
711int
712foregroundproc(void)
713{
714 static pid_t pgrp = -1;
715 int ctty_pgrp;
716
717 if (pgrp == -1)
718 pgrp = getpgrp();
719
720 return((ioctl(STDOUT_FILENO1, TIOCGPGRP((unsigned long)0x40000000 | ((sizeof(int) & 0x1fff) <<
16) | ((('t')) << 8) | ((119)))
, &ctty_pgrp) != -1 &&
721 ctty_pgrp == pgrp));
722}
723
724/* ARGSUSED */
725static void
726updateprogressmeter(int signo)
727{
728 int save_errno = errno(*__errno());
729
730 /* update progressmeter if foreground process or in -m mode */
731 if (foregroundproc() || progress == -1)
732 progressmeter(0, NULL((void*)0));
733 errno(*__errno()) = save_errno;
734}
735
736/*
737 * Display a transfer progress bar if progress is non-zero.
738 * SIGALRM is hijacked for use by this function.
739 * - Before the transfer, set filesize to size of file (or -1 if unknown),
740 * and call with flag = -1. This starts the once per second timer,
741 * and a call to updateprogressmeter() upon SIGALRM.
742 * - During the transfer, updateprogressmeter will call progressmeter
743 * with flag = 0
744 * - After the transfer, call with flag = 1
745 */
746static struct timespec start;
747
748char *action;
749
750void
751progressmeter(int flag, const char *filename)
752{
753 /*
754 * List of order of magnitude prefixes.
755 * The last is `P', as 2^64 = 16384 Petabytes
756 */
757 static const char prefixes[] = " KMGTP";
758
759 static struct timespec lastupdate;
760 static off_t lastsize;
761 static char *title = NULL((void*)0);
762 struct timespec now, td, wait;
763 off_t cursize, abbrevsize;
764 double elapsed;
765 int ratio, barlength, i, remaining, overhead = 30;
766 char buf[512], *filenamebuf;
767
768 if (flag == -1) {
769 clock_gettime(CLOCK_MONOTONIC3, &start);
770 lastupdate = start;
771 lastsize = restart_point;
772 }
773 clock_gettime(CLOCK_MONOTONIC3, &now);
774 if (!progress || filesize < 0)
775 return;
776 cursize = bytes + restart_point;
777
778 if (filesize)
779 ratio = cursize * 100 / filesize;
780 else
781 ratio = 100;
782 ratio = MAXIMUM(ratio, 0)(((ratio) > (0)) ? (ratio) : (0));
783 ratio = MINIMUM(ratio, 100)(((ratio) < (100)) ? (ratio) : (100));
784 if (!verbose && flag == -1) {
785 if ((filenamebuf = strdup(filename)) != NULL((void*)0) &&
786 (filename = basename(filenamebuf)) != NULL((void*)0)) {
787 free(title);
788 title = strdup(filename);
789 }
790 free(filenamebuf);
791 }
792
793 buf[0] = 0;
794 if (!verbose && action != NULL((void*)0)) {
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((void*)0)) {
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 timespecsub(&now, &lastupdate, &wait)do { (&wait)->tv_sec = (&now)->tv_sec - (&lastupdate
)->tv_sec; (&wait)->tv_nsec = (&now)->tv_nsec
- (&lastupdate)->tv_nsec; if ((&wait)->tv_nsec
< 0) { (&wait)->tv_sec--; (&wait)->tv_nsec +=
1000000000L; } } while (0)
;
855 if (cursize > lastsize) {
856 lastupdate = now;
857 lastsize = cursize;
858 if (wait.tv_sec >= STALLTIME5) { /* fudge out stalled time */
859 start.tv_sec += wait.tv_sec;
860 start.tv_nsec += wait.tv_nsec;
861 }
862 wait.tv_sec = 0;
863 }
864
865 timespecsub(&now, &start, &td)do { (&td)->tv_sec = (&now)->tv_sec - (&start
)->tv_sec; (&td)->tv_nsec = (&now)->tv_nsec -
(&start)->tv_nsec; if ((&td)->tv_nsec < 0) {
(&td)->tv_sec--; (&td)->tv_nsec += 1000000000L
; } } while (0)
;
866 elapsed = td.tv_sec + (td.tv_nsec / 1000000000.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 >= STALLTIME5) {
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)(!__isthreaded ? ((ttyout)->_file) : (fileno)(ttyout)), buf, strlen(buf));
900
901 if (flag == -1) {
902 (void)signal(SIGALRM14, updateprogressmeter);
903 alarmtimer(1); /* set alarm timer for 1 Hz */
904 } else if (flag == 1) {
905 alarmtimer(0);
906 (void)putc('\n', ttyout)(!__isthreaded ? __sputc('\n', ttyout) : (putc)('\n', ttyout)
)
;
907 free(title);
908 title = NULL((void*)0);
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 */
921void
922ptransfer(int siginfo)
923{
924 struct timespec now, td;
925 double elapsed, pace;
926 off_t bs;
927 int meg, remaining, hh;
928 char buf[100];
929
930 if (!verbose && !siginfo)
931 return;
932
933 clock_gettime(CLOCK_MONOTONIC3, &now);
934 timespecsub(&now, &start, &td)do { (&td)->tv_sec = (&now)->tv_sec - (&start
)->tv_sec; (&td)->tv_nsec = (&now)->tv_nsec -
(&start)->tv_nsec; if ((&td)->tv_nsec < 0) {
(&td)->tv_sec--; (&td)->tv_nsec += 1000000000L
; } } while (0)
;
935 elapsed = td.tv_sec + (td.tv_nsec / 1000000000.0);
936 bs = bytes / (elapsed == 0.0 ? 1 : elapsed);
937 meg = 0;
938 if (bs > (1024 * 1024))
939 meg = 1;
940
941 pace = bs / (1024.0 * (meg ? 1024.0 : 1.0));
942 (void)snprintf(buf, sizeof(buf),
943 "%lld byte%s %s in %lld.%02d seconds (%lld.%02d %sB/s)\n",
944 (long long)bytes, bytes == 1 ? "" : "s", direction,
945 (long long)elapsed, (int)(elapsed * 100.0) % 100,
946 (long long)pace, (int)(pace * 100.0) % 100,
947 meg ? "M" : "K");
948
949 if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0 &&
950 bytes + restart_point <= filesize) {
951 remaining = (int)((filesize - restart_point) /
952 (bytes / elapsed) - elapsed);
953 hh = remaining / 3600;
954 remaining %= 3600;
955
956 /* "buf+len(buf) -1" to overwrite \n */
957 snprintf(buf + strlen(buf) - 1, sizeof(buf) - strlen(buf),
958 " ETA: %02d:%02d:%02d\n", hh, remaining / 60,
959 remaining % 60);
960 }
961 (void)write(siginfo ? STDERR_FILENO2 : fileno(ttyout)(!__isthreaded ? ((ttyout)->_file) : (fileno)(ttyout)), buf, strlen(buf));
962}
963
964/*
965 * List words in stringlist, vertically arranged
966 */
967#ifndef SMALL
968void
969list_vertical(StringList *sl)
970{
971 int i, j, w;
972 int columns, width, lines;
973 char *p;
974
975 width = 0;
976
977 for (i = 0 ; i < sl->sl_cur ; i++) {
1
Assuming 'i' is >= field 'sl_cur'
2
Loop condition is false. Execution continues on line 982
978 w = strlen(sl->sl_str[i]);
979 if (w > width)
980 width = w;
981 }
982 width = (width + 8) &~ 7;
983
984 columns = ttywidth / width;
985 if (columns == 0)
3
Assuming 'columns' is not equal to 0
4
Taking false branch
986 columns = 1;
987 lines = (sl->sl_cur + columns - 1) / columns;
988 for (i = 0; i < lines; i++) {
5
Assuming 'i' is < 'lines'
6
Loop condition is true. Entering loop body
15
Assuming 'i' is < 'lines'
16
Loop condition is true. Entering loop body
989 for (j = 0; j
16.1
'j' is < 'columns'
< columns
; j++) {
7
Assuming 'j' is < 'columns'
8
Loop condition is true. Entering loop body
17
Loop condition is true. Entering loop body
990 p = sl->sl_str[j * lines + i];
18
Value assigned to 'p'
991 if (p)
9
Assuming 'p' is null
10
Taking false branch
19
Assuming 'p' is null
20
Taking false branch
992 fputs(p, ttyout);
993 if (j * lines + i + lines >= sl->sl_cur) {
11
Taking true branch
21
Assuming the condition is false
22
Taking false branch
994 putc('\n', ttyout)(!__isthreaded ? __sputc('\n', ttyout) : (putc)('\n', ttyout)
)
;
12
Assuming '__isthreaded' is not equal to 0
13
'?' condition is false
995 break;
14
Execution continues on line 988
996 }
997 w = strlen(p);
23
Null pointer passed as 1st argument to string length function
998 while (w < width) {
999 w = (w + 8) &~ 7;
1000 (void)putc('\t', ttyout)(!__isthreaded ? __sputc('\t', ttyout) : (putc)('\t', ttyout)
)
;
1001 }
1002 }
1003 }
1004}
1005#endif /* !SMALL */
1006
1007/*
1008 * Update the global ttywidth value, using TIOCGWINSZ.
1009 */
1010/* ARGSUSED */
1011void
1012setttywidth(int signo)
1013{
1014 int save_errno = errno(*__errno());
1015 struct winsize winsize;
1016
1017 if (ioctl(fileno(ttyout)(!__isthreaded ? ((ttyout)->_file) : (fileno)(ttyout)), TIOCGWINSZ((unsigned long)0x40000000 | ((sizeof(struct winsize) & 0x1fff
) << 16) | ((('t')) << 8) | ((104)))
, &winsize) != -1)
1018 ttywidth = winsize.ws_col ? winsize.ws_col : 80;
1019 else
1020 ttywidth = 80;
1021 errno(*__errno()) = save_errno;
1022}
1023
1024/*
1025 * Set the SIGALRM interval timer for wait seconds, 0 to disable.
1026 */
1027void
1028alarmtimer(int wait)
1029{
1030 int save_errno = errno(*__errno());
1031 struct itimerval itv;
1032
1033 itv.it_value.tv_sec = wait;
1034 itv.it_value.tv_usec = 0;
1035 itv.it_interval = itv.it_value;
1036 setitimer(ITIMER_REAL0, &itv, NULL((void*)0));
1037 errno(*__errno()) = save_errno;
1038}
1039
1040/*
1041 * Setup or cleanup EditLine structures
1042 */
1043#ifndef SMALL
1044void
1045controlediting(void)
1046{
1047 HistEvent hev;
1048
1049 if (editing && el == NULL((void*)0) && hist == NULL((void*)0)) {
1050 el = el_init(__progname, stdin(&__sF[0]), ttyout, stderr(&__sF[2])); /* init editline */
1051 hist = history_init(); /* init the builtin history */
1052 history(hist, &hev, H_SETSIZE1, 100); /* remember 100 events */
1053 el_set(el, EL_HIST10, history, hist); /* use history */
1054
1055 el_set(el, EL_EDITOR2, "emacs"); /* default editor is emacs */
1056 el_set(el, EL_PROMPT0, prompt); /* set the prompt function */
1057
1058 /* add local file completion, bind to TAB */
1059 el_set(el, EL_ADDFN9, "ftp-complete",
1060 "Context sensitive argument completion",
1061 complete);
1062 el_set(el, EL_BIND4, "^I", "ftp-complete", NULL((void*)0));
1063
1064 el_source(el, NULL((void*)0)); /* read ~/.editrc */
1065 el_set(el, EL_SIGNAL3, 1);
1066 } else if (!editing) {
1067 if (hist) {
1068 history_end(hist);
1069 hist = NULL((void*)0);
1070 }
1071 if (el) {
1072 el_end(el);
1073 el = NULL((void*)0);
1074 }
1075 }
1076}
1077#endif /* !SMALL */
1078
1079/*
1080 * Wait for an asynchronous connect(2) attempt to finish.
1081 */
1082int
1083connect_wait(int s)
1084{
1085 struct pollfd pfd[1];
1086 int error = 0;
1087 socklen_t len = sizeof(error);
1088
1089 pfd[0].fd = s;
1090 pfd[0].events = POLLOUT0x0004;
1091
1092 if (poll(pfd, 1, -1) == -1)
1093 return -1;
1094 if (getsockopt(s, SOL_SOCKET0xffff, SO_ERROR0x1007, &error, &len) == -1)
1095 return -1;
1096 if (error != 0) {
1097 errno(*__errno()) = error;
1098 return -1;
1099 }
1100 return 0;
1101}
1102
1103#ifndef SMALL
1104ssize_t
1105http_time(time_t t, char *tmbuf, size_t len)
1106{
1107 struct tm tm;
1108
1109 /* New HTTP/1.1 RFC 7231 prefers IMF-fixdate from RFC 5322 */
1110 if (gmtime_r(&t, &tm) == NULL((void*)0))
1111 return 0;
1112 else
1113 return (strftime(tmbuf, len, "%a, %d %h %Y %T %Z", &tm));
1114}
1115#endif /* !SMALL */