1 /* File Transfer Protocol support.
2 Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001
3 Free Software Foundation, Inc.
5 This file is part of GNU Wget.
7 GNU Wget is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GNU Wget is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with Wget; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 In addition, as a special exception, the Free Software Foundation
22 gives permission to link the code of its release of Wget with the
23 OpenSSL project's "OpenSSL" library (or with modified versions of it
24 that use the same license as the "OpenSSL" library), and distribute
25 the linked executables. You must obey the GNU General Public License
26 in all respects for all of the code used other than "OpenSSL". If you
27 modify this file, you may extend this exception to your version of the
28 file, but you are not obligated to do so. If you do not wish to do
29 so, delete this exception statement from your version. */
43 #include <sys/types.h>
56 #include "convert.h" /* for downloaded_file */
57 #include "recur.h" /* for INFINITE_RECURSION */
63 extern LARGE_INT total_downloaded_bytes;
65 /* File where the "ls -al" listing will be saved. */
66 #define LIST_FILENAME ".listing"
68 extern char ftp_last_respline[];
72 int st; /* connection status */
73 int cmd; /* command code */
74 struct rbuf rbuf; /* control connection buffer */
75 double dltime; /* time of the download in msecs */
76 enum stype rs; /* remote system reported by ftp server */
77 char *id; /* initial directory */
78 char *target; /* target file name */
79 struct url *proxy; /* FTWK-style proxy */
83 /* Look for regexp "( *[0-9]+ *byte" (literal parenthesis) anywhere in
84 the string S, and return the number converted to long, if found, 0
87 ftp_expected_bytes (const char *s)
93 while (*s && *s != '(')
97 for (++s; *s && ISSPACE (*s); s++);
105 res = (*s - '0') + 10 * res;
108 while (*s && ISDIGIT (*s));
111 while (*s && ISSPACE (*s))
115 if (TOLOWER (*s) != 'b')
117 if (strncasecmp (s, "byte", 4))
127 * This function sets up a passive data connection with the FTP server.
128 * It is merely a wrapper around ftp_epsv, ftp_lpsv and ftp_pasv.
131 ftp_do_pasv (struct rbuf *rbuf, ip_address *addr, int *port)
135 /* We need to determine the address family and need to call
136 getpeername, so while we're at it, store the address to ADDR.
137 ftp_pasv and ftp_lpsv can simply override it. */
138 if (!socket_ip_address (RBUF_FD (rbuf), addr, ENDPOINT_PEER))
141 /* If our control connection is over IPv6, then we first try EPSV and then
142 * LPSV if the former is not supported. If the control connection is over
143 * IPv4, we simply issue the good old PASV request. */
147 if (!opt.server_response)
148 logputs (LOG_VERBOSE, "==> PASV ... ");
149 err = ftp_pasv (rbuf, addr, port);
152 if (!opt.server_response)
153 logputs (LOG_VERBOSE, "==> EPSV ... ");
154 err = ftp_epsv (rbuf, addr, port);
156 /* If EPSV is not supported try LPSV */
157 if (err == FTPNOPASV)
159 if (!opt.server_response)
160 logputs (LOG_VERBOSE, "==> LPSV ... ");
161 err = ftp_lpsv (rbuf, addr, port);
172 * This function sets up an active data connection with the FTP server.
173 * It is merely a wrapper around ftp_eprt, ftp_lprt and ftp_port.
176 ftp_do_port (struct rbuf *rbuf, int *local_sock)
181 assert (rbuf != NULL);
182 assert (rbuf_initialized_p (rbuf));
184 if (!socket_ip_address (RBUF_FD (rbuf), &cip, ENDPOINT_PEER))
187 /* If our control connection is over IPv6, then we first try EPRT and then
188 * LPRT if the former is not supported. If the control connection is over
189 * IPv4, we simply issue the good old PORT request. */
193 if (!opt.server_response)
194 logputs (LOG_VERBOSE, "==> PORT ... ");
195 err = ftp_port (rbuf, local_sock);
198 if (!opt.server_response)
199 logputs (LOG_VERBOSE, "==> EPRT ... ");
200 err = ftp_eprt (rbuf, local_sock);
202 /* If EPRT is not supported try LPRT */
203 if (err == FTPPORTERR)
205 if (!opt.server_response)
206 logputs (LOG_VERBOSE, "==> LPRT ... ");
207 err = ftp_lprt (rbuf, local_sock);
218 ftp_do_pasv (struct rbuf *rbuf, ip_address *addr, int *port)
220 if (!opt.server_response)
221 logputs (LOG_VERBOSE, "==> PASV ... ");
222 return ftp_pasv (rbuf, addr, port);
226 ftp_do_port (struct rbuf *rbuf, int *local_sock)
228 if (!opt.server_response)
229 logputs (LOG_VERBOSE, "==> PORT ... ");
230 return ftp_port (rbuf, local_sock);
234 /* Retrieves a file with denoted parameters through opening an FTP
235 connection to the server. It always closes the data connection,
236 and closes the control connection in case of error. */
238 getftp (struct url *u, long *len, long restval, ccon *con)
240 int csock, dtsock, local_sock, res;
243 char *user, *passwd, *respline;
246 int pasv_mode_open = 0;
247 long expected_bytes = 0L;
249 assert (con != NULL);
250 assert (con->target != NULL);
252 /* Debug-check of the sanity of the request by making sure that LIST
253 and RETR are never both requested (since we can handle only one
255 assert (!((cmd & DO_LIST) && (cmd & DO_RETR)));
256 /* Make sure that at least *something* is requested. */
257 assert ((cmd & (DO_LIST | DO_CWD | DO_RETR | DO_LOGIN)) != 0);
261 search_netrc (u->host, (const char **)&user, (const char **)&passwd, 1);
262 user = user ? user : opt.ftp_acc;
263 passwd = passwd ? passwd : opt.ftp_pass;
264 assert (user && passwd);
270 if (!(cmd & DO_LOGIN))
271 csock = RBUF_FD (&con->rbuf);
272 else /* cmd & DO_LOGIN */
275 char *host = con->proxy ? con->proxy->host : u->host;
276 int port = con->proxy ? con->proxy->port : u->port;
277 char *logname = user;
281 /* If proxy is in use, log in as username@target-site. */
282 logname = xmalloc (strlen (user) + 1 + strlen (u->host) + 1);
283 sprintf (logname, "%s@%s", user, u->host);
286 /* Login to the server: */
288 /* First: Establish the control connection. */
290 csock = connect_to_host (host, port);
294 return CONNECT_ERROR (errno);
296 if (cmd & LEAVE_PENDING)
297 rbuf_initialize (&con->rbuf, csock);
299 rbuf_uninitialize (&con->rbuf);
301 /* Since this is a new connection, we may safely discard
302 anything left in the buffer. */
303 rbuf_discard (&con->rbuf);
305 /* Second: Login with proper USER/PASS sequence. */
306 logprintf (LOG_VERBOSE, _("Logging in as %s ... "), user);
307 if (opt.server_response)
308 logputs (LOG_ALWAYS, "\n");
309 err = ftp_login (&con->rbuf, logname, passwd);
314 /* FTPRERR, FTPSRVERR, WRITEFAILED, FTPLOGREFUSED, FTPLOGINC */
318 logputs (LOG_VERBOSE, "\n");
319 logputs (LOG_NOTQUIET, _("\
320 Error in server response, closing control connection.\n"));
322 rbuf_uninitialize (&con->rbuf);
326 logputs (LOG_VERBOSE, "\n");
327 logputs (LOG_NOTQUIET, _("Error in server greeting.\n"));
329 rbuf_uninitialize (&con->rbuf);
333 logputs (LOG_VERBOSE, "\n");
334 logputs (LOG_NOTQUIET,
335 _("Write failed, closing control connection.\n"));
337 rbuf_uninitialize (&con->rbuf);
341 logputs (LOG_VERBOSE, "\n");
342 logputs (LOG_NOTQUIET, _("The server refuses login.\n"));
344 rbuf_uninitialize (&con->rbuf);
345 return FTPLOGREFUSED;
348 logputs (LOG_VERBOSE, "\n");
349 logputs (LOG_NOTQUIET, _("Login incorrect.\n"));
351 rbuf_uninitialize (&con->rbuf);
355 if (!opt.server_response)
356 logputs (LOG_VERBOSE, _("Logged in!\n"));
363 /* Third: Get the system type */
364 if (!opt.server_response)
365 logprintf (LOG_VERBOSE, "==> SYST ... ");
366 err = ftp_syst (&con->rbuf, &con->rs);
371 logputs (LOG_VERBOSE, "\n");
372 logputs (LOG_NOTQUIET, _("\
373 Error in server response, closing control connection.\n"));
375 rbuf_uninitialize (&con->rbuf);
379 logputs (LOG_VERBOSE, "\n");
380 logputs (LOG_NOTQUIET,
381 _("Server error, can't determine system type.\n"));
384 /* Everything is OK. */
390 if (!opt.server_response && err != FTPSRVERR)
391 logputs (LOG_VERBOSE, _("done. "));
393 /* Fourth: Find the initial ftp directory */
395 if (!opt.server_response)
396 logprintf (LOG_VERBOSE, "==> PWD ... ");
397 err = ftp_pwd(&con->rbuf, &con->id);
402 logputs (LOG_VERBOSE, "\n");
403 logputs (LOG_NOTQUIET, _("\
404 Error in server response, closing control connection.\n"));
406 rbuf_uninitialize (&con->rbuf);
410 /* PWD unsupported -- assume "/". */
411 xfree_null (con->id);
412 con->id = xstrdup ("/");
415 /* Everything is OK. */
421 /* VMS will report something like "PUB$DEVICE:[INITIAL.FOLDER]".
422 Convert it to "/INITIAL/FOLDER" */
423 if (con->rs == ST_VMS)
425 char *path = strchr (con->id, '[');
426 char *pathend = path ? strchr (path + 1, ']') : NULL;
427 if (!path || !pathend)
428 DEBUGP (("Initial VMS directory not in the form [...]!\n"));
431 char *idir = con->id;
432 DEBUGP (("Preprocessing the initial VMS directory\n"));
433 DEBUGP ((" old = '%s'\n", con->id));
434 /* We do the conversion in-place by copying the stuff
435 between [ and ] to the beginning, and changing dots
436 to slashes at the same time. */
438 for (++path; path < pathend; path++, idir++)
439 *idir = *path == '.' ? '/' : *path;
441 DEBUGP ((" new = '%s'\n\n", con->id));
444 if (!opt.server_response)
445 logputs (LOG_VERBOSE, _("done.\n"));
447 /* Fifth: Set the FTP type. */
448 type_char = ftp_process_type (u->params);
449 if (!opt.server_response)
450 logprintf (LOG_VERBOSE, "==> TYPE %c ... ", type_char);
451 err = ftp_type (&con->rbuf, type_char);
452 /* FTPRERR, WRITEFAILED, FTPUNKNOWNTYPE */
456 logputs (LOG_VERBOSE, "\n");
457 logputs (LOG_NOTQUIET, _("\
458 Error in server response, closing control connection.\n"));
460 rbuf_uninitialize (&con->rbuf);
464 logputs (LOG_VERBOSE, "\n");
465 logputs (LOG_NOTQUIET,
466 _("Write failed, closing control connection.\n"));
468 rbuf_uninitialize (&con->rbuf);
472 logputs (LOG_VERBOSE, "\n");
473 logprintf (LOG_NOTQUIET,
474 _("Unknown type `%c', closing control connection.\n"),
477 rbuf_uninitialize (&con->rbuf);
480 /* Everything is OK. */
486 if (!opt.server_response)
487 logputs (LOG_VERBOSE, _("done. "));
493 logputs (LOG_VERBOSE, _("==> CWD not needed.\n"));
496 char *target = u->dir;
498 DEBUGP (("changing working directory\n"));
500 /* Change working directory. To change to a non-absolute
501 Unix directory, we need to prepend initial directory
502 (con->id) to it. Absolute directories "just work".
504 A relative directory is one that does not begin with '/'
505 and, on non-Unix OS'es, one that doesn't begin with
508 This is not done for OS400, which doesn't use
509 "/"-delimited directories, nor does it support directory
510 hierarchies. "CWD foo" followed by "CWD bar" leaves us
511 in "bar", not in "foo/bar", as would be customary
515 && !(con->rs != ST_UNIX
516 && ISALPHA (target[0])
518 && con->rs != ST_OS400)
520 int idlen = strlen (con->id);
523 /* Strip trailing slash(es) from con->id. */
524 while (idlen > 0 && con->id[idlen - 1] == '/')
526 p = ntarget = (char *)alloca (idlen + 1 + strlen (u->dir) + 1);
527 memcpy (p, con->id, idlen);
532 DEBUGP (("Prepended initial PWD to relative path:\n"));
533 DEBUGP ((" pwd: '%s'\n old: '%s'\n new: '%s'\n",
534 con->id, target, ntarget));
538 /* If the FTP host runs VMS, we will have to convert the absolute
539 directory path in UNIX notation to absolute directory path in
540 VMS notation as VMS FTP servers do not like UNIX notation of
541 absolute paths. "VMS notation" is [dir.subdir.subsubdir]. */
543 if (con->rs == ST_VMS)
546 char *ntarget = (char *)alloca (strlen (target) + 2);
547 /* We use a converted initial dir, so directories in
548 TARGET will be separated with slashes, something like
549 "/INITIAL/FOLDER/DIR/SUBDIR". Convert that to
550 "[INITIAL.FOLDER.DIR.SUBDIR]". */
551 strcpy (ntarget, target);
552 assert (*ntarget == '/');
554 for (tmpp = ntarget + 1; *tmpp; tmpp++)
559 DEBUGP (("Changed file name to VMS syntax:\n"));
560 DEBUGP ((" Unix: '%s'\n VMS: '%s'\n", target, ntarget));
564 if (!opt.server_response)
565 logprintf (LOG_VERBOSE, "==> CWD %s ... ", target);
566 err = ftp_cwd (&con->rbuf, target);
567 /* FTPRERR, WRITEFAILED, FTPNSFOD */
571 logputs (LOG_VERBOSE, "\n");
572 logputs (LOG_NOTQUIET, _("\
573 Error in server response, closing control connection.\n"));
575 rbuf_uninitialize (&con->rbuf);
579 logputs (LOG_VERBOSE, "\n");
580 logputs (LOG_NOTQUIET,
581 _("Write failed, closing control connection.\n"));
583 rbuf_uninitialize (&con->rbuf);
587 logputs (LOG_VERBOSE, "\n");
588 logprintf (LOG_NOTQUIET, _("No such directory `%s'.\n\n"),
591 rbuf_uninitialize (&con->rbuf);
601 if (!opt.server_response)
602 logputs (LOG_VERBOSE, _("done.\n"));
605 else /* do not CWD */
606 logputs (LOG_VERBOSE, _("==> CWD not required.\n"));
608 if ((cmd & DO_RETR) && restval && *len == 0)
612 if (!opt.server_response)
613 logprintf (LOG_VERBOSE, "==> SIZE %s ... ", u->file);
616 err = ftp_size(&con->rbuf, u->file, len);
622 logputs (LOG_VERBOSE, "\n");
623 logputs (LOG_NOTQUIET, _("\
624 Error in server response, closing control connection.\n"));
626 rbuf_uninitialize (&con->rbuf);
630 /* Everything is OK. */
636 if (!opt.server_response)
637 logputs (LOG_VERBOSE, _("done.\n"));
640 /* If anything is to be retrieved, PORT (or PASV) must be sent. */
641 if (cmd & (DO_LIST | DO_RETR))
643 if (opt.ftp_pasv > 0)
645 ip_address passive_addr;
647 err = ftp_do_pasv (&con->rbuf, &passive_addr, &passive_port);
648 /* FTPRERR, WRITEFAILED, FTPNOPASV, FTPINVPASV */
652 logputs (LOG_VERBOSE, "\n");
653 logputs (LOG_NOTQUIET, _("\
654 Error in server response, closing control connection.\n"));
656 rbuf_uninitialize (&con->rbuf);
660 logputs (LOG_VERBOSE, "\n");
661 logputs (LOG_NOTQUIET,
662 _("Write failed, closing control connection.\n"));
664 rbuf_uninitialize (&con->rbuf);
668 logputs (LOG_VERBOSE, "\n");
669 logputs (LOG_NOTQUIET, _("Cannot initiate PASV transfer.\n"));
672 logputs (LOG_VERBOSE, "\n");
673 logputs (LOG_NOTQUIET, _("Cannot parse PASV response.\n"));
684 DEBUGP (("trying to connect to %s port %d\n",
685 pretty_print_address (&passive_addr),
687 dtsock = connect_to_ip (&passive_addr, passive_port, NULL);
690 int save_errno = errno;
692 rbuf_uninitialize (&con->rbuf);
693 logprintf (LOG_VERBOSE, _("couldn't connect to %s port %hu: %s\n"),
694 pretty_print_address (&passive_addr), passive_port,
695 strerror (save_errno));
696 return CONNECT_ERROR (save_errno);
699 pasv_mode_open = 1; /* Flag to avoid accept port */
700 if (!opt.server_response)
701 logputs (LOG_VERBOSE, _("done. "));
705 if (!pasv_mode_open) /* Try to use a port command if PASV failed */
707 err = ftp_do_port (&con->rbuf, &local_sock);
708 /* FTPRERR, WRITEFAILED, bindport (FTPSYSERR), HOSTERR,
713 logputs (LOG_VERBOSE, "\n");
714 logputs (LOG_NOTQUIET, _("\
715 Error in server response, closing control connection.\n"));
719 rbuf_uninitialize (&con->rbuf);
723 logputs (LOG_VERBOSE, "\n");
724 logputs (LOG_NOTQUIET,
725 _("Write failed, closing control connection.\n"));
729 rbuf_uninitialize (&con->rbuf);
733 logputs (LOG_VERBOSE, "\n");
734 logprintf (LOG_NOTQUIET, "socket: %s\n", strerror (errno));
738 rbuf_uninitialize (&con->rbuf);
742 logputs (LOG_VERBOSE, "\n");
743 logprintf (LOG_NOTQUIET, _("Bind error (%s).\n"),
749 logputs (LOG_VERBOSE, "\n");
750 logputs (LOG_NOTQUIET, _("Invalid PORT.\n"));
754 rbuf_uninitialize (&con->rbuf);
764 if (!opt.server_response)
765 logputs (LOG_VERBOSE, _("done. "));
767 } /* cmd & (DO_LIST | DO_RETR) */
769 /* Restart if needed. */
770 if (restval && (cmd & DO_RETR))
772 if (!opt.server_response)
773 logprintf (LOG_VERBOSE, "==> REST %ld ... ", restval);
774 err = ftp_rest (&con->rbuf, restval);
776 /* FTPRERR, WRITEFAILED, FTPRESTFAIL */
780 logputs (LOG_VERBOSE, "\n");
781 logputs (LOG_NOTQUIET, _("\
782 Error in server response, closing control connection.\n"));
786 rbuf_uninitialize (&con->rbuf);
790 logputs (LOG_VERBOSE, "\n");
791 logputs (LOG_NOTQUIET,
792 _("Write failed, closing control connection.\n"));
796 rbuf_uninitialize (&con->rbuf);
800 /* If `-c' is specified and the file already existed when
801 Wget was started, it would be a bad idea for us to start
802 downloading it from scratch, effectively truncating it. */
803 if (opt.always_rest && (cmd & NO_TRUNCATE))
805 logprintf (LOG_NOTQUIET,
806 _("\nREST failed; will not truncate `%s'.\n"),
811 rbuf_uninitialize (&con->rbuf);
812 return CONTNOTSUPPORTED;
814 logputs (LOG_VERBOSE, _("\nREST failed, starting from scratch.\n"));
824 if (err != FTPRESTFAIL && !opt.server_response)
825 logputs (LOG_VERBOSE, _("done. "));
826 } /* restval && cmd & DO_RETR */
830 /* If we're in spider mode, don't really retrieve anything. The
831 fact that we got to this point should be proof enough that
832 the file exists, vaguely akin to HTTP's concept of a "HEAD"
839 rbuf_uninitialize (&con->rbuf);
845 if (!opt.server_response)
848 logputs (LOG_VERBOSE, "\n");
849 logprintf (LOG_VERBOSE, "==> RETR %s ... ", u->file);
853 err = ftp_retr (&con->rbuf, u->file);
854 /* FTPRERR, WRITEFAILED, FTPNSFOD */
858 logputs (LOG_VERBOSE, "\n");
859 logputs (LOG_NOTQUIET, _("\
860 Error in server response, closing control connection.\n"));
864 rbuf_uninitialize (&con->rbuf);
868 logputs (LOG_VERBOSE, "\n");
869 logputs (LOG_NOTQUIET,
870 _("Write failed, closing control connection.\n"));
874 rbuf_uninitialize (&con->rbuf);
878 logputs (LOG_VERBOSE, "\n");
879 logprintf (LOG_NOTQUIET, _("No such file `%s'.\n\n"), u->file);
892 if (!opt.server_response)
893 logputs (LOG_VERBOSE, _("done.\n"));
894 expected_bytes = ftp_expected_bytes (ftp_last_respline);
899 if (!opt.server_response)
900 logputs (LOG_VERBOSE, "==> LIST ... ");
901 /* As Maciej W. Rozycki (macro@ds2.pg.gda.pl) says, `LIST'
902 without arguments is better than `LIST .'; confirmed by
904 err = ftp_list (&con->rbuf, NULL);
905 /* FTPRERR, WRITEFAILED */
909 logputs (LOG_VERBOSE, "\n");
910 logputs (LOG_NOTQUIET, _("\
911 Error in server response, closing control connection.\n"));
915 rbuf_uninitialize (&con->rbuf);
919 logputs (LOG_VERBOSE, "\n");
920 logputs (LOG_NOTQUIET,
921 _("Write failed, closing control connection.\n"));
925 rbuf_uninitialize (&con->rbuf);
929 logputs (LOG_VERBOSE, "\n");
930 logprintf (LOG_NOTQUIET, _("No such file or directory `%s'.\n\n"),
943 if (!opt.server_response)
944 logputs (LOG_VERBOSE, _("done.\n"));
945 expected_bytes = ftp_expected_bytes (ftp_last_respline);
946 } /* cmd & DO_LIST */
948 if (!(cmd & (DO_LIST | DO_RETR)) || (opt.spider && !(cmd & DO_LIST)))
951 /* Some FTP servers return the total length of file after REST
952 command, others just return the remaining size. */
953 if (*len && restval && expected_bytes
954 && (expected_bytes == *len - restval))
956 DEBUGP (("Lying FTP server found, adjusting.\n"));
957 expected_bytes = *len;
960 /* If no transmission was required, then everything is OK. */
961 if (!pasv_mode_open) /* we are not using pasive mode so we need
964 /* Wait for the server to connect to the address we're waiting
966 dtsock = accept_connection (local_sock);
969 logprintf (LOG_NOTQUIET, "accept: %s\n", strerror (errno));
974 /* Open the file -- if opt.dfp is set, use it instead. */
975 if (!opt.dfp || con->cmd & DO_LIST)
977 mkalldirs (con->target);
979 rotate_backups (con->target);
980 /* #### Is this correct? */
981 chmod (con->target, 0600);
983 fp = fopen (con->target, restval ? "ab" : "wb");
986 logprintf (LOG_NOTQUIET, "%s: %s\n", con->target, strerror (errno));
988 rbuf_uninitialize (&con->rbuf);
996 extern int global_download_count;
999 /* Rewind the output document if the download starts over and if
1000 this is the first download. See gethttp() for a longer
1002 if (!restval && global_download_count == 0 && opt.dfp != stdout)
1004 /* This will silently fail for streams that don't correspond
1005 to regular files, but that's OK. */
1007 /* ftruncate is needed because opt.dfp is opened in append
1008 mode if opt.always_rest is set. */
1009 ftruncate (fileno (fp), 0);
1016 logprintf (LOG_VERBOSE, _("Length: %s"), legible (*len));
1018 logprintf (LOG_VERBOSE, _(" [%s to go]"), legible (*len - restval));
1019 logputs (LOG_VERBOSE, "\n");
1020 expected_bytes = *len; /* for get_contents/show_progress */
1022 else if (expected_bytes)
1024 logprintf (LOG_VERBOSE, _("Length: %s"), legible (expected_bytes));
1026 logprintf (LOG_VERBOSE, _(" [%s to go]"),
1027 legible (expected_bytes - restval));
1028 logputs (LOG_VERBOSE, _(" (unauthoritative)\n"));
1031 /* Get the contents of the document. */
1032 res = get_contents (dtsock, fp, len, restval, expected_bytes, &con->rbuf,
1034 tms = time_str (NULL);
1035 tmrate = retr_rate (*len - restval, con->dltime, 0);
1036 /* Close data connection socket. */
1038 xclose (local_sock);
1039 /* Close the local file. */
1041 /* Close or flush the file. We have to be careful to check for
1042 error here. Checking the result of fwrite() is not enough --
1043 errors could go unnoticed! */
1045 if (!opt.dfp || con->cmd & DO_LIST)
1046 flush_res = fclose (fp);
1048 flush_res = fflush (fp);
1049 if (flush_res == EOF)
1053 /* If get_contents couldn't write to fp, bail out. */
1056 logprintf (LOG_NOTQUIET, _("%s: %s, closing control connection.\n"),
1057 con->target, strerror (errno));
1059 rbuf_uninitialize (&con->rbuf);
1064 logprintf (LOG_NOTQUIET, _("%s (%s) - Data connection: %s; "),
1065 tms, tmrate, strerror (errno));
1066 if (opt.server_response)
1067 logputs (LOG_ALWAYS, "\n");
1070 /* Get the server to tell us if everything is retrieved. */
1071 err = ftp_response (&con->rbuf, &respline);
1072 /* ...and empty the buffer. */
1073 rbuf_discard (&con->rbuf);
1077 /* The control connection is decidedly closed. Print the time
1078 only if it hasn't already been printed. */
1080 logprintf (LOG_NOTQUIET, "%s (%s) - ", tms, tmrate);
1081 logputs (LOG_NOTQUIET, _("Control connection closed.\n"));
1082 /* If there is an error on the control connection, close it, but
1083 return FTPRETRINT, since there is a possibility that the
1084 whole file was retrieved nevertheless (but that is for
1085 ftp_loop_internal to decide). */
1087 rbuf_uninitialize (&con->rbuf);
1089 } /* err != FTPOK */
1090 /* If retrieval failed for any reason, return FTPRETRINT, but do not
1091 close socket, since the control connection is still alive. If
1092 there is something wrong with the control connection, it will
1093 become apparent later. */
1094 if (*respline != '2')
1098 logprintf (LOG_NOTQUIET, "%s (%s) - ", tms, tmrate);
1099 logputs (LOG_NOTQUIET, _("Data transfer aborted.\n"));
1106 /* What now? The data connection was erroneous, whereas the
1107 response says everything is OK. We shall play it safe. */
1111 if (!(cmd & LEAVE_PENDING))
1113 /* I should probably send 'QUIT' and check for a reply, but this
1114 is faster. #### Is it OK, though? */
1116 rbuf_uninitialize (&con->rbuf);
1118 /* If it was a listing, and opt.server_response is true,
1120 if (opt.server_response && (con->cmd & DO_LIST))
1122 mkalldirs (con->target);
1123 fp = fopen (con->target, "r");
1125 logprintf (LOG_ALWAYS, "%s: %s\n", con->target, strerror (errno));
1129 /* The lines are being read with read_whole_line because of
1130 no-buffering on opt.lfile. */
1131 while ((line = read_whole_line (fp)))
1133 logprintf (LOG_ALWAYS, "%s\n", line);
1138 } /* con->cmd & DO_LIST && server_response */
1140 return RETRFINISHED;
1143 /* A one-file FTP loop. This is the part where FTP retrieval is
1144 retried, and retried, and retried, and...
1146 This loop either gets commands from con, or (if ON_YOUR_OWN is
1147 set), makes them up to retrieve the file given by the URL. */
1149 ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
1154 char *tmrate = NULL;
1159 con->target = url_file_name (u);
1161 if (opt.noclobber && file_exists_p (con->target))
1163 logprintf (LOG_VERBOSE,
1164 _("File `%s' already there, not retrieving.\n"), con->target);
1165 /* If the file is there, we suppose it's retrieved OK. */
1169 /* Remove it if it's a link. */
1170 remove_link (con->target);
1171 if (!opt.output_document)
1174 locf = opt.output_document;
1178 if (con->st & ON_YOUR_OWN)
1179 con->st = ON_YOUR_OWN;
1181 orig_lp = con->cmd & LEAVE_PENDING ? 1 : 0;
1186 /* Increment the pass counter. */
1188 sleep_between_retrievals (count);
1189 if (con->st & ON_YOUR_OWN)
1192 con->cmd |= (DO_RETR | LEAVE_PENDING);
1193 if (rbuf_initialized_p (&con->rbuf))
1194 con->cmd &= ~ (DO_LOGIN | DO_CWD);
1196 con->cmd |= (DO_LOGIN | DO_CWD);
1198 else /* not on your own */
1200 if (rbuf_initialized_p (&con->rbuf))
1201 con->cmd &= ~DO_LOGIN;
1203 con->cmd |= DO_LOGIN;
1204 if (con->st & DONE_CWD)
1205 con->cmd &= ~DO_CWD;
1210 /* Assume no restarting. */
1212 if ((count > 1 || opt.always_rest)
1213 && !(con->cmd & DO_LIST)
1214 && file_exists_p (locf))
1215 if (stat (locf, &st) == 0 && S_ISREG (st.st_mode))
1216 restval = st.st_size;
1218 /* In `-c' is used, check whether the file we're writing to
1219 exists and is of non-zero length. If so, we'll refuse to
1220 truncate it if the server doesn't support continued
1222 if (opt.always_rest && restval > 0)
1223 con->cmd |= NO_TRUNCATE;
1225 /* Get the current time string. */
1226 tms = time_str (NULL);
1227 /* Print fetch message, if opt.verbose. */
1230 char *hurl = url_string (u, 1);
1234 sprintf (tmp, _("(try:%2d)"), count);
1235 logprintf (LOG_VERBOSE, "--%s-- %s\n %s => `%s'\n",
1236 tms, hurl, tmp, locf);
1238 ws_changetitle (hurl, 1);
1242 /* Send getftp the proper length, if fileinfo was provided. */
1247 err = getftp (u, &len, restval, con);
1249 if (!rbuf_initialized_p (&con->rbuf))
1250 con->st &= ~DONE_CWD;
1252 con->st |= DONE_CWD;
1256 case HOSTERR: case CONIMPOSSIBLE: case FWRITEERR: case FOPENERR:
1257 case FTPNSFOD: case FTPLOGINC: case FTPNOPASV: case CONTNOTSUPPORTED:
1258 /* Fatal errors, give up. */
1261 case CONSOCKERR: case CONERROR: case FTPSRVERR: case FTPRERR:
1262 case WRITEFAILED: case FTPUNKNOWNTYPE: case FTPSYSERR:
1263 case FTPPORTERR: case FTPLOGREFUSED: case FTPINVPASV:
1264 printwhat (count, opt.ntry);
1265 /* non-fatal errors */
1269 /* If the control connection was closed, the retrieval
1270 will be considered OK if f->size == len. */
1271 if (!f || len != f->size)
1273 printwhat (count, opt.ntry);
1285 tms = time_str (NULL);
1287 tmrate = retr_rate (len - restval, con->dltime, 0);
1289 /* If we get out of the switch above without continue'ing, we've
1290 successfully downloaded a file. Remember this fact. */
1291 downloaded_file (FILE_DOWNLOADED_NORMALLY, locf);
1293 if (con->st & ON_YOUR_OWN)
1295 xclose (RBUF_FD (&con->rbuf));
1296 rbuf_uninitialize (&con->rbuf);
1299 logprintf (LOG_VERBOSE, _("%s (%s) - `%s' saved [%ld]\n\n"),
1300 tms, tmrate, locf, len);
1301 if (!opt.verbose && !opt.quiet)
1303 /* Need to hide the password from the URL. The `if' is here
1304 so that we don't do the needless allocation every
1306 char *hurl = url_string (u, 1);
1307 logprintf (LOG_NONVERBOSE, "%s URL: %s [%ld] -> \"%s\" [%d]\n",
1308 tms, hurl, len, locf, count);
1312 if ((con->cmd & DO_LIST))
1313 /* This is a directory listing file. */
1315 if (!opt.remove_listing)
1316 /* --dont-remove-listing was specified, so do count this towards the
1317 number of bytes and files downloaded. */
1319 total_downloaded_bytes += len;
1323 /* Deletion of listing files is not controlled by --delete-after, but
1324 by the more specific option --dont-remove-listing, and the code
1325 to do this deletion is in another function. */
1327 else if (!opt.spider)
1328 /* This is not a directory listing file. */
1330 /* Unlike directory listing files, don't pretend normal files weren't
1331 downloaded if they're going to be deleted. People seeding proxies,
1332 for instance, may want to know how many bytes and files they've
1333 downloaded through it. */
1334 total_downloaded_bytes += len;
1337 if (opt.delete_after)
1339 DEBUGP (("Removing file due to --delete-after in"
1340 " ftp_loop_internal():\n"));
1341 logprintf (LOG_VERBOSE, _("Removing %s.\n"), locf);
1343 logprintf (LOG_NOTQUIET, "unlink: %s\n", strerror (errno));
1347 /* Restore the original leave-pendingness. */
1349 con->cmd |= LEAVE_PENDING;
1351 con->cmd &= ~LEAVE_PENDING;
1353 } while (!opt.ntry || (count < opt.ntry));
1355 if (rbuf_initialized_p (&con->rbuf) && (con->st & ON_YOUR_OWN))
1357 xclose (RBUF_FD (&con->rbuf));
1358 rbuf_uninitialize (&con->rbuf);
1363 /* Return the directory listing in a reusable format. The directory
1364 is specifed in u->dir. */
1366 ftp_get_listing (struct url *u, ccon *con, struct fileinfo **f)
1369 char *uf; /* url file name */
1370 char *lf; /* list file name */
1371 char *old_target = con->target;
1373 con->st &= ~ON_YOUR_OWN;
1374 con->cmd |= (DO_LIST | LEAVE_PENDING);
1375 con->cmd &= ~DO_RETR;
1377 /* Find the listing file name. We do it by taking the file name of
1378 the URL and replacing the last component with the listing file
1380 uf = url_file_name (u);
1381 lf = file_merge (uf, LIST_FILENAME);
1383 DEBUGP ((_("Using `%s' as listing tmp file.\n"), lf));
1386 err = ftp_loop_internal (u, NULL, con);
1387 con->target = old_target;
1390 *f = ftp_parse_ls (lf, con->rs);
1393 if (opt.remove_listing)
1396 logprintf (LOG_NOTQUIET, "unlink: %s\n", strerror (errno));
1398 logprintf (LOG_VERBOSE, _("Removed `%s'.\n"), lf);
1401 con->cmd &= ~DO_LIST;
1405 static uerr_t ftp_retrieve_dirs PARAMS ((struct url *, struct fileinfo *,
1407 static uerr_t ftp_retrieve_glob PARAMS ((struct url *, ccon *, int));
1408 static struct fileinfo *delelement PARAMS ((struct fileinfo *,
1409 struct fileinfo **));
1410 static void freefileinfo PARAMS ((struct fileinfo *f));
1412 /* Retrieve a list of files given in struct fileinfo linked list. If
1413 a file is a symbolic link, do not retrieve it, but rather try to
1414 set up a similar link on the local disk, if the symlinks are
1417 If opt.recursive is set, after all files have been retrieved,
1418 ftp_retrieve_dirs will be called to retrieve the directories. */
1420 ftp_retrieve_list (struct url *u, struct fileinfo *f, ccon *con)
1422 static int depth = 0;
1424 struct fileinfo *orig;
1429 /* Increase the depth. */
1431 if (opt.reclevel != INFINITE_RECURSION && depth > opt.reclevel)
1433 DEBUGP ((_("Recursion depth %d exceeded max. depth %d.\n"),
1434 depth, opt.reclevel));
1442 con->st &= ~ON_YOUR_OWN;
1443 if (!(con->st & DONE_CWD))
1446 con->cmd &= ~DO_CWD;
1447 con->cmd |= (DO_RETR | LEAVE_PENDING);
1449 if (!rbuf_initialized_p (&con->rbuf))
1450 con->cmd |= DO_LOGIN;
1452 con->cmd &= ~DO_LOGIN;
1454 err = RETROK; /* in case it's not used */
1458 char *old_target, *ofile;
1460 if (opt.quota && total_downloaded_bytes > opt.quota)
1465 old_target = con->target;
1467 ofile = xstrdup (u->file);
1468 url_set_file (u, f->name);
1470 con->target = url_file_name (u);
1474 if (opt.timestamping && f->type == FT_PLAINFILE)
1477 /* If conversion of HTML files retrieved via FTP is ever implemented,
1478 we'll need to stat() <file>.orig here when -K has been specified.
1479 I'm not implementing it now since files on an FTP server are much
1480 more likely than files on an HTTP server to legitimately have a
1482 if (!stat (con->target, &st))
1486 /* Else, get it from the file. */
1487 local_size = st.st_size;
1490 /* Modification time granularity is 2 seconds for Windows, so
1491 increase local time by 1 second for later comparison. */
1494 /* Compare file sizes only for servers that tell us correct
1495 values. Assumme sizes being equal for servers that lie
1497 cor_val = (con->rs == ST_UNIX || con->rs == ST_WINNT);
1498 eq_size = cor_val ? (local_size == f->size) : 1 ;
1499 if (f->tstamp <= tml && eq_size)
1501 /* Remote file is older, file sizes can be compared and
1503 logprintf (LOG_VERBOSE, _("\
1504 Remote file no newer than local file `%s' -- not retrieving.\n"), con->target);
1509 /* Remote file is newer or sizes cannot be matched */
1510 logprintf (LOG_VERBOSE, _("\
1511 Remote file is newer than local file `%s' -- retrieving.\n\n"),
1516 /* Sizes do not match */
1517 logprintf (LOG_VERBOSE, _("\
1518 The sizes do not match (local %ld) -- retrieving.\n\n"), local_size);
1521 } /* opt.timestamping && f->type == FT_PLAINFILE */
1525 /* If opt.retr_symlinks is defined, we treat symlinks as
1526 if they were normal files. There is currently no way
1527 to distinguish whether they might be directories, and
1529 if (!opt.retr_symlinks)
1533 logputs (LOG_NOTQUIET,
1534 _("Invalid name of the symlink, skipping.\n"));
1538 /* Check whether we already have the correct
1540 int rc = lstat (con->target, &st);
1543 size_t len = strlen (f->linkto) + 1;
1544 if (S_ISLNK (st.st_mode))
1546 char *link_target = (char *)alloca (len);
1547 size_t n = readlink (con->target, link_target, len);
1549 && (memcmp (link_target, f->linkto, n) == 0))
1551 logprintf (LOG_VERBOSE, _("\
1552 Already have correct symlink %s -> %s\n\n"),
1553 con->target, f->linkto);
1559 logprintf (LOG_VERBOSE, _("Creating symlink %s -> %s\n"),
1560 con->target, f->linkto);
1561 /* Unlink before creating symlink! */
1562 unlink (con->target);
1563 if (symlink (f->linkto, con->target) == -1)
1564 logprintf (LOG_NOTQUIET, "symlink: %s\n",
1566 logputs (LOG_VERBOSE, "\n");
1567 } /* have f->linkto */
1568 #else /* not HAVE_SYMLINK */
1569 logprintf (LOG_NOTQUIET,
1570 _("Symlinks not supported, skipping symlink `%s'.\n"),
1572 #endif /* not HAVE_SYMLINK */
1574 else /* opt.retr_symlinks */
1577 err = ftp_loop_internal (u, f, con);
1578 } /* opt.retr_symlinks */
1582 logprintf (LOG_NOTQUIET, _("Skipping directory `%s'.\n"),
1586 /* Call the retrieve loop. */
1588 err = ftp_loop_internal (u, f, con);
1591 logprintf (LOG_NOTQUIET, _("%s: unknown/unsupported file type.\n"),
1596 /* Set the time-stamp information to the local file. Symlinks
1597 are not to be stamped because it sets the stamp on the
1599 if (!(f->type == FT_SYMLINK && !opt.retr_symlinks)
1602 && file_exists_p (con->target))
1604 /* #### This code repeats in http.c and ftp.c. Move it to a
1606 const char *fl = NULL;
1607 if (opt.output_document)
1609 if (opt.od_known_regular)
1610 fl = opt.output_document;
1615 touch (fl, f->tstamp);
1617 else if (f->tstamp == -1)
1618 logprintf (LOG_NOTQUIET, _("%s: corrupt time-stamp.\n"), con->target);
1620 if (f->perms && f->type == FT_PLAINFILE && dlthis)
1622 if (opt.preserve_perm)
1623 chmod (con->target, f->perms);
1626 DEBUGP (("Unrecognized permissions for %s.\n", con->target));
1628 xfree (con->target);
1629 con->target = old_target;
1631 url_set_file (u, ofile);
1634 /* Break on fatals. */
1635 if (err == QUOTEXC || err == HOSTERR || err == FWRITEERR)
1637 con->cmd &= ~ (DO_CWD | DO_LOGIN);
1641 /* We do not want to call ftp_retrieve_dirs here */
1642 if (opt.recursive &&
1643 !(opt.reclevel != INFINITE_RECURSION && depth >= opt.reclevel))
1644 err = ftp_retrieve_dirs (u, orig, con);
1645 else if (opt.recursive)
1646 DEBUGP ((_("Will not retrieve dirs since depth is %d (max %d).\n"),
1647 depth, opt.reclevel));
1652 /* Retrieve the directories given in a file list. This function works
1653 by simply going through the linked list and calling
1654 ftp_retrieve_glob on each directory entry. The function knows
1655 about excluded directories. */
1657 ftp_retrieve_dirs (struct url *u, struct fileinfo *f, ccon *con)
1659 char *container = NULL;
1660 int container_size = 0;
1662 for (; f; f = f->next)
1665 char *odir, *newdir;
1667 if (opt.quota && total_downloaded_bytes > opt.quota)
1669 if (f->type != FT_DIRECTORY)
1672 /* Allocate u->dir off stack, but reallocate only if a larger
1673 string is needed. It's a pity there's no "realloca" for an
1674 item on the bottom of the stack. */
1675 size = strlen (u->dir) + 1 + strlen (f->name) + 1;
1676 if (size > container_size)
1677 container = (char *)alloca (size);
1682 || (*odir == '/' && *(odir + 1) == '\0'))
1683 /* If ODIR is empty or just "/", simply append f->name to
1684 ODIR. (In the former case, to preserve u->dir being
1685 relative; in the latter case, to avoid double slash.) */
1686 sprintf (newdir, "%s%s", odir, f->name);
1688 /* Else, use a separator. */
1689 sprintf (newdir, "%s/%s", odir, f->name);
1691 DEBUGP (("Composing new CWD relative to the initial directory.\n"));
1692 DEBUGP ((" odir = '%s'\n f->name = '%s'\n newdir = '%s'\n\n",
1693 odir, f->name, newdir));
1694 if (!accdir (newdir, ALLABS))
1696 logprintf (LOG_VERBOSE, _("\
1697 Not descending to `%s' as it is excluded/not-included.\n"), newdir);
1701 con->st &= ~DONE_CWD;
1703 odir = xstrdup (u->dir); /* because url_set_dir will free
1705 url_set_dir (u, newdir);
1706 ftp_retrieve_glob (u, con, GETALL);
1707 url_set_dir (u, odir);
1710 /* Set the time-stamp? */
1713 if (opt.quota && total_downloaded_bytes > opt.quota)
1719 /* Return non-zero if S has a leading '/' or contains '../' */
1721 has_insecure_name_p (const char *s)
1726 if (strstr(s, "../") != 0)
1732 /* A near-top-level function to retrieve the files in a directory.
1733 The function calls ftp_get_listing, to get a linked list of files.
1734 Then it weeds out the file names that do not match the pattern.
1735 ftp_retrieve_list is called with this updated list as an argument.
1737 If the argument ACTION is GETONE, just download the file (but first
1738 get the listing, so that the time-stamp is heeded); if it's GLOBALL,
1739 use globbing; if it's GETALL, download the whole directory. */
1741 ftp_retrieve_glob (struct url *u, ccon *con, int action)
1743 struct fileinfo *f, *start;
1746 con->cmd |= LEAVE_PENDING;
1748 res = ftp_get_listing (u, con, &start);
1751 /* First: weed out that do not conform the global rules given in
1752 opt.accepts and opt.rejects. */
1753 if (opt.accepts || opt.rejects)
1758 if (f->type != FT_DIRECTORY && !acceptable (f->name))
1760 logprintf (LOG_VERBOSE, _("Rejecting `%s'.\n"), f->name);
1761 f = delelement (f, &start);
1767 /* Remove all files with possible harmful names */
1771 if (has_insecure_name_p (f->name))
1773 logprintf (LOG_VERBOSE, _("Rejecting `%s'.\n"), f->name);
1774 f = delelement (f, &start);
1779 /* Now weed out the files that do not match our globbing pattern.
1780 If we are dealing with a globbing pattern, that is. */
1781 if (*u->file && (action == GLOBALL || action == GETONE))
1788 matchres = fnmatch (u->file, f->name, 0);
1791 logprintf (LOG_NOTQUIET, "%s: %s\n", con->target,
1795 if (matchres == FNM_NOMATCH)
1796 f = delelement (f, &start); /* delete the element from the list */
1798 f = f->next; /* leave the element in the list */
1802 freefileinfo (start);
1803 return RETRBADPATTERN;
1809 /* Just get everything. */
1810 ftp_retrieve_list (u, start, con);
1814 if (action == GLOBALL)
1817 /* #### This message SUCKS. We should see what was the
1818 reason that nothing was retrieved. */
1819 logprintf (LOG_VERBOSE, _("No matches on pattern `%s'.\n"), u->file);
1821 else /* GETONE or GETALL */
1823 /* Let's try retrieving it anyway. */
1824 con->st |= ON_YOUR_OWN;
1825 res = ftp_loop_internal (u, NULL, con);
1829 freefileinfo (start);
1830 if (opt.quota && total_downloaded_bytes > opt.quota)
1833 /* #### Should we return `res' here? */
1837 /* The wrapper that calls an appropriate routine according to contents
1838 of URL. Inherently, its capabilities are limited on what can be
1839 encoded into a URL. */
1841 ftp_loop (struct url *u, int *dt, struct url *proxy)
1843 ccon con; /* FTP connection */
1848 memset (&con, 0, sizeof (con));
1850 rbuf_uninitialize (&con.rbuf);
1851 con.st = ON_YOUR_OWN;
1855 res = RETROK; /* in case it's not used */
1857 /* If the file name is empty, the user probably wants a directory
1858 index. We'll provide one, properly HTML-ized. Unless
1859 opt.htmlify is 0, of course. :-) */
1860 if (!*u->file && !opt.recursive)
1863 res = ftp_get_listing (u, &con, &f);
1867 if (opt.htmlify && !opt.spider)
1869 char *filename = (opt.output_document
1870 ? xstrdup (opt.output_document)
1871 : (con.target ? xstrdup (con.target)
1872 : url_file_name (u)));
1873 res = ftp_index (filename, u, f);
1874 if (res == FTPOK && opt.verbose)
1876 if (!opt.output_document)
1880 if (stat (filename, &st) == 0)
1884 logprintf (LOG_NOTQUIET,
1885 _("Wrote HTML-ized index to `%s' [%ld].\n"),
1889 logprintf (LOG_NOTQUIET,
1890 _("Wrote HTML-ized index to `%s'.\n"),
1900 int wild = has_wildcards_p (u->file);
1901 if ((opt.ftp_glob && wild) || opt.recursive || opt.timestamping)
1903 /* ftp_retrieve_glob is a catch-all function that gets called
1904 if we need globbing, time-stamping or recursion. Its
1905 third argument is just what we really need. */
1906 res = ftp_retrieve_glob (u, &con,
1907 (opt.ftp_glob && wild) ? GLOBALL : GETONE);
1910 res = ftp_loop_internal (u, NULL, &con);
1916 /* If a connection was left, quench it. */
1917 if (rbuf_initialized_p (&con.rbuf))
1918 xclose (RBUF_FD (&con.rbuf));
1919 xfree_null (con.id);
1921 xfree_null (con.target);
1926 /* Delete an element from the fileinfo linked list. Returns the
1927 address of the next element, or NULL if the list is exhausted. It
1928 can modify the start of the list. */
1929 static struct fileinfo *
1930 delelement (struct fileinfo *f, struct fileinfo **start)
1932 struct fileinfo *prev = f->prev;
1933 struct fileinfo *next = f->next;
1936 xfree_null (f->linkto);
1948 /* Free the fileinfo linked list of files. */
1950 freefileinfo (struct fileinfo *f)
1954 struct fileinfo *next = f->next;