File: | src/usr.bin/ftp/util.c |
Warning: | line 997, column 8 Null pointer passed as 1st argument to string length function |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | ||||
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((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 | */ | |||
215 | int | |||
216 | ftp_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 | ||||
267 | tryagain: | |||
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 | |||
332 | int | |||
333 | another(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 | */ | |||
366 | char * | |||
367 | remglob2(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 | |||
441 | again: | |||
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 | */ | |||
469 | char * | |||
470 | remglob(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 | |||
478 | int | |||
479 | confirm(const char *cmd, const char *file) | |||
480 | { | |||
481 | char str[BUFSIZ1024]; | |||
482 | ||||
483 | if (file && (confirmrest || !interactive)) | |||
484 | return (1); | |||
485 | top: | |||
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': | |||
515 | quit: | |||
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 | */ | |||
538 | int | |||
539 | globulize(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 | */ | |||
568 | off_t | |||
569 | remotesize(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 | */ | |||
605 | time_t | |||
606 | remotemodtime(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 | */ | |||
679 | int | |||
680 | fileindir(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 | */ | |||
711 | int | |||
712 | foregroundproc(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 */ | |||
725 | static void | |||
726 | updateprogressmeter(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 | */ | |||
746 | static struct timespec start; | |||
747 | ||||
748 | char *action; | |||
749 | ||||
750 | void | |||
751 | progressmeter(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 | */ | |||
921 | void | |||
922 | ptransfer(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 | |||
968 | void | |||
969 | list_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++) { | |||
| ||||
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) | |||
986 | columns = 1; | |||
987 | lines = (sl->sl_cur + columns - 1) / columns; | |||
988 | for (i = 0; i < lines; i++) { | |||
989 | for (j = 0; j
| |||
990 | p = sl->sl_str[j * lines + i]; | |||
991 | if (p) | |||
992 | fputs(p, ttyout); | |||
993 | if (j * lines + i + lines >= sl->sl_cur) { | |||
994 | putc('\n', ttyout)(!__isthreaded ? __sputc('\n', ttyout) : (putc)('\n', ttyout) ); | |||
995 | break; | |||
996 | } | |||
997 | w = strlen(p); | |||
| ||||
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 */ | |||
1011 | void | |||
1012 | setttywidth(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 | */ | |||
1027 | void | |||
1028 | alarmtimer(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 | |||
1044 | void | |||
1045 | controlediting(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 | */ | |||
1082 | int | |||
1083 | connect_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 | |||
1104 | ssize_t | |||
1105 | http_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 */ |