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 (CONSOCKERR, CONPORTERR, BINDERR,
709 LISTENERR), HOSTERR, FTPPORTERR */
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);
741 case CONPORTERR: case BINDERR: case LISTENERR:
742 /* What now? These problems are local... */
743 logputs (LOG_VERBOSE, "\n");
744 logprintf (LOG_NOTQUIET, _("Bind error (%s).\n"),
751 logputs (LOG_VERBOSE, "\n");
752 logputs (LOG_NOTQUIET, _("Invalid PORT.\n"));
756 rbuf_uninitialize (&con->rbuf);
766 if (!opt.server_response)
767 logputs (LOG_VERBOSE, _("done. "));
769 } /* cmd & (DO_LIST | DO_RETR) */
771 /* Restart if needed. */
772 if (restval && (cmd & DO_RETR))
774 if (!opt.server_response)
775 logprintf (LOG_VERBOSE, "==> REST %ld ... ", restval);
776 err = ftp_rest (&con->rbuf, restval);
778 /* FTPRERR, WRITEFAILED, FTPRESTFAIL */
782 logputs (LOG_VERBOSE, "\n");
783 logputs (LOG_NOTQUIET, _("\
784 Error in server response, closing control connection.\n"));
788 rbuf_uninitialize (&con->rbuf);
792 logputs (LOG_VERBOSE, "\n");
793 logputs (LOG_NOTQUIET,
794 _("Write failed, closing control connection.\n"));
798 rbuf_uninitialize (&con->rbuf);
802 /* If `-c' is specified and the file already existed when
803 Wget was started, it would be a bad idea for us to start
804 downloading it from scratch, effectively truncating it. */
805 if (opt.always_rest && (cmd & NO_TRUNCATE))
807 logprintf (LOG_NOTQUIET,
808 _("\nREST failed; will not truncate `%s'.\n"),
813 rbuf_uninitialize (&con->rbuf);
814 return CONTNOTSUPPORTED;
816 logputs (LOG_VERBOSE, _("\nREST failed, starting from scratch.\n"));
826 if (err != FTPRESTFAIL && !opt.server_response)
827 logputs (LOG_VERBOSE, _("done. "));
828 } /* restval && cmd & DO_RETR */
832 /* If we're in spider mode, don't really retrieve anything. The
833 fact that we got to this point should be proof enough that
834 the file exists, vaguely akin to HTTP's concept of a "HEAD"
841 rbuf_uninitialize (&con->rbuf);
847 if (!opt.server_response)
850 logputs (LOG_VERBOSE, "\n");
851 logprintf (LOG_VERBOSE, "==> RETR %s ... ", u->file);
855 err = ftp_retr (&con->rbuf, u->file);
856 /* FTPRERR, WRITEFAILED, FTPNSFOD */
860 logputs (LOG_VERBOSE, "\n");
861 logputs (LOG_NOTQUIET, _("\
862 Error in server response, closing control connection.\n"));
866 rbuf_uninitialize (&con->rbuf);
870 logputs (LOG_VERBOSE, "\n");
871 logputs (LOG_NOTQUIET,
872 _("Write failed, closing control connection.\n"));
876 rbuf_uninitialize (&con->rbuf);
880 logputs (LOG_VERBOSE, "\n");
881 logprintf (LOG_NOTQUIET, _("No such file `%s'.\n\n"), u->file);
894 if (!opt.server_response)
895 logputs (LOG_VERBOSE, _("done.\n"));
896 expected_bytes = ftp_expected_bytes (ftp_last_respline);
901 if (!opt.server_response)
902 logputs (LOG_VERBOSE, "==> LIST ... ");
903 /* As Maciej W. Rozycki (macro@ds2.pg.gda.pl) says, `LIST'
904 without arguments is better than `LIST .'; confirmed by
906 err = ftp_list (&con->rbuf, NULL);
907 /* FTPRERR, WRITEFAILED */
911 logputs (LOG_VERBOSE, "\n");
912 logputs (LOG_NOTQUIET, _("\
913 Error in server response, closing control connection.\n"));
917 rbuf_uninitialize (&con->rbuf);
921 logputs (LOG_VERBOSE, "\n");
922 logputs (LOG_NOTQUIET,
923 _("Write failed, closing control connection.\n"));
927 rbuf_uninitialize (&con->rbuf);
931 logputs (LOG_VERBOSE, "\n");
932 logprintf (LOG_NOTQUIET, _("No such file or directory `%s'.\n\n"),
945 if (!opt.server_response)
946 logputs (LOG_VERBOSE, _("done.\n"));
947 expected_bytes = ftp_expected_bytes (ftp_last_respline);
948 } /* cmd & DO_LIST */
950 if (!(cmd & (DO_LIST | DO_RETR)) || (opt.spider && !(cmd & DO_LIST)))
953 /* Some FTP servers return the total length of file after REST
954 command, others just return the remaining size. */
955 if (*len && restval && expected_bytes
956 && (expected_bytes == *len - restval))
958 DEBUGP (("Lying FTP server found, adjusting.\n"));
959 expected_bytes = *len;
962 /* If no transmission was required, then everything is OK. */
963 if (!pasv_mode_open) /* we are not using pasive mode so we need
966 /* Open the data transmission socket by calling acceptport(). */
967 err = acceptport (local_sock, &dtsock);
968 /* Possible errors: ACCEPTERR. */
969 if (err == ACCEPTERR)
971 logprintf (LOG_NOTQUIET, "accept: %s\n", strerror (errno));
976 /* Open the file -- if opt.dfp is set, use it instead. */
977 if (!opt.dfp || con->cmd & DO_LIST)
979 mkalldirs (con->target);
981 rotate_backups (con->target);
982 /* #### Is this correct? */
983 chmod (con->target, 0600);
985 fp = fopen (con->target, restval ? "ab" : "wb");
988 logprintf (LOG_NOTQUIET, "%s: %s\n", con->target, strerror (errno));
990 rbuf_uninitialize (&con->rbuf);
998 extern int global_download_count;
1001 /* Rewind the output document if the download starts over and if
1002 this is the first download. See gethttp() for a longer
1004 if (!restval && global_download_count == 0 && opt.dfp != stdout)
1006 /* This will silently fail for streams that don't correspond
1007 to regular files, but that's OK. */
1009 /* ftruncate is needed because opt.dfp is opened in append
1010 mode if opt.always_rest is set. */
1011 ftruncate (fileno (fp), 0);
1018 logprintf (LOG_VERBOSE, _("Length: %s"), legible (*len));
1020 logprintf (LOG_VERBOSE, _(" [%s to go]"), legible (*len - restval));
1021 logputs (LOG_VERBOSE, "\n");
1022 expected_bytes = *len; /* for get_contents/show_progress */
1024 else if (expected_bytes)
1026 logprintf (LOG_VERBOSE, _("Length: %s"), legible (expected_bytes));
1028 logprintf (LOG_VERBOSE, _(" [%s to go]"),
1029 legible (expected_bytes - restval));
1030 logputs (LOG_VERBOSE, _(" (unauthoritative)\n"));
1033 /* Get the contents of the document. */
1034 res = get_contents (dtsock, fp, len, restval, expected_bytes, &con->rbuf,
1036 tms = time_str (NULL);
1037 tmrate = retr_rate (*len - restval, con->dltime, 0);
1038 /* Close data connection socket. */
1040 xclose (local_sock);
1041 /* Close the local file. */
1043 /* Close or flush the file. We have to be careful to check for
1044 error here. Checking the result of fwrite() is not enough --
1045 errors could go unnoticed! */
1047 if (!opt.dfp || con->cmd & DO_LIST)
1048 flush_res = fclose (fp);
1050 flush_res = fflush (fp);
1051 if (flush_res == EOF)
1055 /* If get_contents couldn't write to fp, bail out. */
1058 logprintf (LOG_NOTQUIET, _("%s: %s, closing control connection.\n"),
1059 con->target, strerror (errno));
1061 rbuf_uninitialize (&con->rbuf);
1066 logprintf (LOG_NOTQUIET, _("%s (%s) - Data connection: %s; "),
1067 tms, tmrate, strerror (errno));
1068 if (opt.server_response)
1069 logputs (LOG_ALWAYS, "\n");
1072 /* Get the server to tell us if everything is retrieved. */
1073 err = ftp_response (&con->rbuf, &respline);
1074 /* ...and empty the buffer. */
1075 rbuf_discard (&con->rbuf);
1079 /* The control connection is decidedly closed. Print the time
1080 only if it hasn't already been printed. */
1082 logprintf (LOG_NOTQUIET, "%s (%s) - ", tms, tmrate);
1083 logputs (LOG_NOTQUIET, _("Control connection closed.\n"));
1084 /* If there is an error on the control connection, close it, but
1085 return FTPRETRINT, since there is a possibility that the
1086 whole file was retrieved nevertheless (but that is for
1087 ftp_loop_internal to decide). */
1089 rbuf_uninitialize (&con->rbuf);
1091 } /* err != FTPOK */
1092 /* If retrieval failed for any reason, return FTPRETRINT, but do not
1093 close socket, since the control connection is still alive. If
1094 there is something wrong with the control connection, it will
1095 become apparent later. */
1096 if (*respline != '2')
1100 logprintf (LOG_NOTQUIET, "%s (%s) - ", tms, tmrate);
1101 logputs (LOG_NOTQUIET, _("Data transfer aborted.\n"));
1108 /* What now? The data connection was erroneous, whereas the
1109 response says everything is OK. We shall play it safe. */
1113 if (!(cmd & LEAVE_PENDING))
1115 /* I should probably send 'QUIT' and check for a reply, but this
1116 is faster. #### Is it OK, though? */
1118 rbuf_uninitialize (&con->rbuf);
1120 /* If it was a listing, and opt.server_response is true,
1122 if (opt.server_response && (con->cmd & DO_LIST))
1124 mkalldirs (con->target);
1125 fp = fopen (con->target, "r");
1127 logprintf (LOG_ALWAYS, "%s: %s\n", con->target, strerror (errno));
1131 /* The lines are being read with read_whole_line because of
1132 no-buffering on opt.lfile. */
1133 while ((line = read_whole_line (fp)))
1135 logprintf (LOG_ALWAYS, "%s\n", line);
1140 } /* con->cmd & DO_LIST && server_response */
1142 return RETRFINISHED;
1145 /* A one-file FTP loop. This is the part where FTP retrieval is
1146 retried, and retried, and retried, and...
1148 This loop either gets commands from con, or (if ON_YOUR_OWN is
1149 set), makes them up to retrieve the file given by the URL. */
1151 ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
1156 char *tmrate = NULL;
1161 con->target = url_file_name (u);
1163 if (opt.noclobber && file_exists_p (con->target))
1165 logprintf (LOG_VERBOSE,
1166 _("File `%s' already there, not retrieving.\n"), con->target);
1167 /* If the file is there, we suppose it's retrieved OK. */
1171 /* Remove it if it's a link. */
1172 remove_link (con->target);
1173 if (!opt.output_document)
1176 locf = opt.output_document;
1180 if (con->st & ON_YOUR_OWN)
1181 con->st = ON_YOUR_OWN;
1183 orig_lp = con->cmd & LEAVE_PENDING ? 1 : 0;
1188 /* Increment the pass counter. */
1190 sleep_between_retrievals (count);
1191 if (con->st & ON_YOUR_OWN)
1194 con->cmd |= (DO_RETR | LEAVE_PENDING);
1195 if (rbuf_initialized_p (&con->rbuf))
1196 con->cmd &= ~ (DO_LOGIN | DO_CWD);
1198 con->cmd |= (DO_LOGIN | DO_CWD);
1200 else /* not on your own */
1202 if (rbuf_initialized_p (&con->rbuf))
1203 con->cmd &= ~DO_LOGIN;
1205 con->cmd |= DO_LOGIN;
1206 if (con->st & DONE_CWD)
1207 con->cmd &= ~DO_CWD;
1212 /* Assume no restarting. */
1214 if ((count > 1 || opt.always_rest)
1215 && !(con->cmd & DO_LIST)
1216 && file_exists_p (locf))
1217 if (stat (locf, &st) == 0 && S_ISREG (st.st_mode))
1218 restval = st.st_size;
1220 /* In `-c' is used, check whether the file we're writing to
1221 exists and is of non-zero length. If so, we'll refuse to
1222 truncate it if the server doesn't support continued
1224 if (opt.always_rest && restval > 0)
1225 con->cmd |= NO_TRUNCATE;
1227 /* Get the current time string. */
1228 tms = time_str (NULL);
1229 /* Print fetch message, if opt.verbose. */
1232 char *hurl = url_string (u, 1);
1236 sprintf (tmp, _("(try:%2d)"), count);
1237 logprintf (LOG_VERBOSE, "--%s-- %s\n %s => `%s'\n",
1238 tms, hurl, tmp, locf);
1240 ws_changetitle (hurl, 1);
1244 /* Send getftp the proper length, if fileinfo was provided. */
1249 err = getftp (u, &len, restval, con);
1251 if (!rbuf_initialized_p (&con->rbuf))
1252 con->st &= ~DONE_CWD;
1254 con->st |= DONE_CWD;
1258 case HOSTERR: case CONIMPOSSIBLE: case FWRITEERR: case FOPENERR:
1259 case FTPNSFOD: case FTPLOGINC: case FTPNOPASV: case CONTNOTSUPPORTED:
1260 /* Fatal errors, give up. */
1263 case CONSOCKERR: case CONERROR: case FTPSRVERR: case FTPRERR:
1264 case WRITEFAILED: case FTPUNKNOWNTYPE: case CONPORTERR:
1265 case BINDERR: case LISTENERR: case ACCEPTERR:
1266 case FTPPORTERR: case FTPLOGREFUSED: case FTPINVPASV:
1267 printwhat (count, opt.ntry);
1268 /* non-fatal errors */
1272 /* If the control connection was closed, the retrieval
1273 will be considered OK if f->size == len. */
1274 if (!f || len != f->size)
1276 printwhat (count, opt.ntry);
1288 tms = time_str (NULL);
1290 tmrate = retr_rate (len - restval, con->dltime, 0);
1292 /* If we get out of the switch above without continue'ing, we've
1293 successfully downloaded a file. Remember this fact. */
1294 downloaded_file (FILE_DOWNLOADED_NORMALLY, locf);
1296 if (con->st & ON_YOUR_OWN)
1298 xclose (RBUF_FD (&con->rbuf));
1299 rbuf_uninitialize (&con->rbuf);
1302 logprintf (LOG_VERBOSE, _("%s (%s) - `%s' saved [%ld]\n\n"),
1303 tms, tmrate, locf, len);
1304 if (!opt.verbose && !opt.quiet)
1306 /* Need to hide the password from the URL. The `if' is here
1307 so that we don't do the needless allocation every
1309 char *hurl = url_string (u, 1);
1310 logprintf (LOG_NONVERBOSE, "%s URL: %s [%ld] -> \"%s\" [%d]\n",
1311 tms, hurl, len, locf, count);
1315 if ((con->cmd & DO_LIST))
1316 /* This is a directory listing file. */
1318 if (!opt.remove_listing)
1319 /* --dont-remove-listing was specified, so do count this towards the
1320 number of bytes and files downloaded. */
1322 total_downloaded_bytes += len;
1326 /* Deletion of listing files is not controlled by --delete-after, but
1327 by the more specific option --dont-remove-listing, and the code
1328 to do this deletion is in another function. */
1330 else if (!opt.spider)
1331 /* This is not a directory listing file. */
1333 /* Unlike directory listing files, don't pretend normal files weren't
1334 downloaded if they're going to be deleted. People seeding proxies,
1335 for instance, may want to know how many bytes and files they've
1336 downloaded through it. */
1337 total_downloaded_bytes += len;
1340 if (opt.delete_after)
1342 DEBUGP (("Removing file due to --delete-after in"
1343 " ftp_loop_internal():\n"));
1344 logprintf (LOG_VERBOSE, _("Removing %s.\n"), locf);
1346 logprintf (LOG_NOTQUIET, "unlink: %s\n", strerror (errno));
1350 /* Restore the original leave-pendingness. */
1352 con->cmd |= LEAVE_PENDING;
1354 con->cmd &= ~LEAVE_PENDING;
1356 } while (!opt.ntry || (count < opt.ntry));
1358 if (rbuf_initialized_p (&con->rbuf) && (con->st & ON_YOUR_OWN))
1360 xclose (RBUF_FD (&con->rbuf));
1361 rbuf_uninitialize (&con->rbuf);
1366 /* Return the directory listing in a reusable format. The directory
1367 is specifed in u->dir. */
1369 ftp_get_listing (struct url *u, ccon *con, struct fileinfo **f)
1372 char *uf; /* url file name */
1373 char *lf; /* list file name */
1374 char *old_target = con->target;
1376 con->st &= ~ON_YOUR_OWN;
1377 con->cmd |= (DO_LIST | LEAVE_PENDING);
1378 con->cmd &= ~DO_RETR;
1380 /* Find the listing file name. We do it by taking the file name of
1381 the URL and replacing the last component with the listing file
1383 uf = url_file_name (u);
1384 lf = file_merge (uf, LIST_FILENAME);
1386 DEBUGP ((_("Using `%s' as listing tmp file.\n"), lf));
1389 err = ftp_loop_internal (u, NULL, con);
1390 con->target = old_target;
1393 *f = ftp_parse_ls (lf, con->rs);
1396 if (opt.remove_listing)
1399 logprintf (LOG_NOTQUIET, "unlink: %s\n", strerror (errno));
1401 logprintf (LOG_VERBOSE, _("Removed `%s'.\n"), lf);
1404 con->cmd &= ~DO_LIST;
1408 static uerr_t ftp_retrieve_dirs PARAMS ((struct url *, struct fileinfo *,
1410 static uerr_t ftp_retrieve_glob PARAMS ((struct url *, ccon *, int));
1411 static struct fileinfo *delelement PARAMS ((struct fileinfo *,
1412 struct fileinfo **));
1413 static void freefileinfo PARAMS ((struct fileinfo *f));
1415 /* Retrieve a list of files given in struct fileinfo linked list. If
1416 a file is a symbolic link, do not retrieve it, but rather try to
1417 set up a similar link on the local disk, if the symlinks are
1420 If opt.recursive is set, after all files have been retrieved,
1421 ftp_retrieve_dirs will be called to retrieve the directories. */
1423 ftp_retrieve_list (struct url *u, struct fileinfo *f, ccon *con)
1425 static int depth = 0;
1427 struct fileinfo *orig;
1432 /* Increase the depth. */
1434 if (opt.reclevel != INFINITE_RECURSION && depth > opt.reclevel)
1436 DEBUGP ((_("Recursion depth %d exceeded max. depth %d.\n"),
1437 depth, opt.reclevel));
1445 con->st &= ~ON_YOUR_OWN;
1446 if (!(con->st & DONE_CWD))
1449 con->cmd &= ~DO_CWD;
1450 con->cmd |= (DO_RETR | LEAVE_PENDING);
1452 if (!rbuf_initialized_p (&con->rbuf))
1453 con->cmd |= DO_LOGIN;
1455 con->cmd &= ~DO_LOGIN;
1457 err = RETROK; /* in case it's not used */
1461 char *old_target, *ofile;
1463 if (opt.quota && total_downloaded_bytes > opt.quota)
1468 old_target = con->target;
1470 ofile = xstrdup (u->file);
1471 url_set_file (u, f->name);
1473 con->target = url_file_name (u);
1477 if (opt.timestamping && f->type == FT_PLAINFILE)
1480 /* If conversion of HTML files retrieved via FTP is ever implemented,
1481 we'll need to stat() <file>.orig here when -K has been specified.
1482 I'm not implementing it now since files on an FTP server are much
1483 more likely than files on an HTTP server to legitimately have a
1485 if (!stat (con->target, &st))
1489 /* Else, get it from the file. */
1490 local_size = st.st_size;
1493 /* Modification time granularity is 2 seconds for Windows, so
1494 increase local time by 1 second for later comparison. */
1497 /* Compare file sizes only for servers that tell us correct
1498 values. Assumme sizes being equal for servers that lie
1500 cor_val = (con->rs == ST_UNIX || con->rs == ST_WINNT);
1501 eq_size = cor_val ? (local_size == f->size) : 1 ;
1502 if (f->tstamp <= tml && eq_size)
1504 /* Remote file is older, file sizes can be compared and
1506 logprintf (LOG_VERBOSE, _("\
1507 Remote file no newer than local file `%s' -- not retrieving.\n"), con->target);
1512 /* Remote file is newer or sizes cannot be matched */
1513 logprintf (LOG_VERBOSE, _("\
1514 Remote file is newer than local file `%s' -- retrieving.\n\n"),
1519 /* Sizes do not match */
1520 logprintf (LOG_VERBOSE, _("\
1521 The sizes do not match (local %ld) -- retrieving.\n\n"), local_size);
1524 } /* opt.timestamping && f->type == FT_PLAINFILE */
1528 /* If opt.retr_symlinks is defined, we treat symlinks as
1529 if they were normal files. There is currently no way
1530 to distinguish whether they might be directories, and
1532 if (!opt.retr_symlinks)
1536 logputs (LOG_NOTQUIET,
1537 _("Invalid name of the symlink, skipping.\n"));
1541 /* Check whether we already have the correct
1543 int rc = lstat (con->target, &st);
1546 size_t len = strlen (f->linkto) + 1;
1547 if (S_ISLNK (st.st_mode))
1549 char *link_target = (char *)alloca (len);
1550 size_t n = readlink (con->target, link_target, len);
1552 && (memcmp (link_target, f->linkto, n) == 0))
1554 logprintf (LOG_VERBOSE, _("\
1555 Already have correct symlink %s -> %s\n\n"),
1556 con->target, f->linkto);
1562 logprintf (LOG_VERBOSE, _("Creating symlink %s -> %s\n"),
1563 con->target, f->linkto);
1564 /* Unlink before creating symlink! */
1565 unlink (con->target);
1566 if (symlink (f->linkto, con->target) == -1)
1567 logprintf (LOG_NOTQUIET, "symlink: %s\n",
1569 logputs (LOG_VERBOSE, "\n");
1570 } /* have f->linkto */
1571 #else /* not HAVE_SYMLINK */
1572 logprintf (LOG_NOTQUIET,
1573 _("Symlinks not supported, skipping symlink `%s'.\n"),
1575 #endif /* not HAVE_SYMLINK */
1577 else /* opt.retr_symlinks */
1580 err = ftp_loop_internal (u, f, con);
1581 } /* opt.retr_symlinks */
1585 logprintf (LOG_NOTQUIET, _("Skipping directory `%s'.\n"),
1589 /* Call the retrieve loop. */
1591 err = ftp_loop_internal (u, f, con);
1594 logprintf (LOG_NOTQUIET, _("%s: unknown/unsupported file type.\n"),
1599 /* Set the time-stamp information to the local file. Symlinks
1600 are not to be stamped because it sets the stamp on the
1602 if (!(f->type == FT_SYMLINK && !opt.retr_symlinks)
1605 && file_exists_p (con->target))
1607 /* #### This code repeats in http.c and ftp.c. Move it to a
1609 const char *fl = NULL;
1610 if (opt.output_document)
1612 if (opt.od_known_regular)
1613 fl = opt.output_document;
1618 touch (fl, f->tstamp);
1620 else if (f->tstamp == -1)
1621 logprintf (LOG_NOTQUIET, _("%s: corrupt time-stamp.\n"), con->target);
1623 if (f->perms && f->type == FT_PLAINFILE && dlthis)
1625 if (opt.preserve_perm)
1626 chmod (con->target, f->perms);
1629 DEBUGP (("Unrecognized permissions for %s.\n", con->target));
1631 xfree (con->target);
1632 con->target = old_target;
1634 url_set_file (u, ofile);
1637 /* Break on fatals. */
1638 if (err == QUOTEXC || err == HOSTERR || err == FWRITEERR)
1640 con->cmd &= ~ (DO_CWD | DO_LOGIN);
1644 /* We do not want to call ftp_retrieve_dirs here */
1645 if (opt.recursive &&
1646 !(opt.reclevel != INFINITE_RECURSION && depth >= opt.reclevel))
1647 err = ftp_retrieve_dirs (u, orig, con);
1648 else if (opt.recursive)
1649 DEBUGP ((_("Will not retrieve dirs since depth is %d (max %d).\n"),
1650 depth, opt.reclevel));
1655 /* Retrieve the directories given in a file list. This function works
1656 by simply going through the linked list and calling
1657 ftp_retrieve_glob on each directory entry. The function knows
1658 about excluded directories. */
1660 ftp_retrieve_dirs (struct url *u, struct fileinfo *f, ccon *con)
1662 char *container = NULL;
1663 int container_size = 0;
1665 for (; f; f = f->next)
1668 char *odir, *newdir;
1670 if (opt.quota && total_downloaded_bytes > opt.quota)
1672 if (f->type != FT_DIRECTORY)
1675 /* Allocate u->dir off stack, but reallocate only if a larger
1676 string is needed. It's a pity there's no "realloca" for an
1677 item on the bottom of the stack. */
1678 size = strlen (u->dir) + 1 + strlen (f->name) + 1;
1679 if (size > container_size)
1680 container = (char *)alloca (size);
1685 || (*odir == '/' && *(odir + 1) == '\0'))
1686 /* If ODIR is empty or just "/", simply append f->name to
1687 ODIR. (In the former case, to preserve u->dir being
1688 relative; in the latter case, to avoid double slash.) */
1689 sprintf (newdir, "%s%s", odir, f->name);
1691 /* Else, use a separator. */
1692 sprintf (newdir, "%s/%s", odir, f->name);
1694 DEBUGP (("Composing new CWD relative to the initial directory.\n"));
1695 DEBUGP ((" odir = '%s'\n f->name = '%s'\n newdir = '%s'\n\n",
1696 odir, f->name, newdir));
1697 if (!accdir (newdir, ALLABS))
1699 logprintf (LOG_VERBOSE, _("\
1700 Not descending to `%s' as it is excluded/not-included.\n"), newdir);
1704 con->st &= ~DONE_CWD;
1706 odir = xstrdup (u->dir); /* because url_set_dir will free
1708 url_set_dir (u, newdir);
1709 ftp_retrieve_glob (u, con, GETALL);
1710 url_set_dir (u, odir);
1713 /* Set the time-stamp? */
1716 if (opt.quota && total_downloaded_bytes > opt.quota)
1722 /* Return non-zero if S has a leading '/' or contains '../' */
1724 has_insecure_name_p (const char *s)
1729 if (strstr(s, "../") != 0)
1735 /* A near-top-level function to retrieve the files in a directory.
1736 The function calls ftp_get_listing, to get a linked list of files.
1737 Then it weeds out the file names that do not match the pattern.
1738 ftp_retrieve_list is called with this updated list as an argument.
1740 If the argument ACTION is GETONE, just download the file (but first
1741 get the listing, so that the time-stamp is heeded); if it's GLOBALL,
1742 use globbing; if it's GETALL, download the whole directory. */
1744 ftp_retrieve_glob (struct url *u, ccon *con, int action)
1746 struct fileinfo *f, *start;
1749 con->cmd |= LEAVE_PENDING;
1751 res = ftp_get_listing (u, con, &start);
1754 /* First: weed out that do not conform the global rules given in
1755 opt.accepts and opt.rejects. */
1756 if (opt.accepts || opt.rejects)
1761 if (f->type != FT_DIRECTORY && !acceptable (f->name))
1763 logprintf (LOG_VERBOSE, _("Rejecting `%s'.\n"), f->name);
1764 f = delelement (f, &start);
1770 /* Remove all files with possible harmful names */
1774 if (has_insecure_name_p (f->name))
1776 logprintf (LOG_VERBOSE, _("Rejecting `%s'.\n"), f->name);
1777 f = delelement (f, &start);
1782 /* Now weed out the files that do not match our globbing pattern.
1783 If we are dealing with a globbing pattern, that is. */
1784 if (*u->file && (action == GLOBALL || action == GETONE))
1791 matchres = fnmatch (u->file, f->name, 0);
1794 logprintf (LOG_NOTQUIET, "%s: %s\n", con->target,
1798 if (matchres == FNM_NOMATCH)
1799 f = delelement (f, &start); /* delete the element from the list */
1801 f = f->next; /* leave the element in the list */
1805 freefileinfo (start);
1806 return RETRBADPATTERN;
1812 /* Just get everything. */
1813 ftp_retrieve_list (u, start, con);
1817 if (action == GLOBALL)
1820 /* #### This message SUCKS. We should see what was the
1821 reason that nothing was retrieved. */
1822 logprintf (LOG_VERBOSE, _("No matches on pattern `%s'.\n"), u->file);
1824 else /* GETONE or GETALL */
1826 /* Let's try retrieving it anyway. */
1827 con->st |= ON_YOUR_OWN;
1828 res = ftp_loop_internal (u, NULL, con);
1832 freefileinfo (start);
1833 if (opt.quota && total_downloaded_bytes > opt.quota)
1836 /* #### Should we return `res' here? */
1840 /* The wrapper that calls an appropriate routine according to contents
1841 of URL. Inherently, its capabilities are limited on what can be
1842 encoded into a URL. */
1844 ftp_loop (struct url *u, int *dt, struct url *proxy)
1846 ccon con; /* FTP connection */
1851 memset (&con, 0, sizeof (con));
1853 rbuf_uninitialize (&con.rbuf);
1854 con.st = ON_YOUR_OWN;
1858 res = RETROK; /* in case it's not used */
1860 /* If the file name is empty, the user probably wants a directory
1861 index. We'll provide one, properly HTML-ized. Unless
1862 opt.htmlify is 0, of course. :-) */
1863 if (!*u->file && !opt.recursive)
1866 res = ftp_get_listing (u, &con, &f);
1870 if (opt.htmlify && !opt.spider)
1872 char *filename = (opt.output_document
1873 ? xstrdup (opt.output_document)
1874 : (con.target ? xstrdup (con.target)
1875 : url_file_name (u)));
1876 res = ftp_index (filename, u, f);
1877 if (res == FTPOK && opt.verbose)
1879 if (!opt.output_document)
1883 if (stat (filename, &st) == 0)
1887 logprintf (LOG_NOTQUIET,
1888 _("Wrote HTML-ized index to `%s' [%ld].\n"),
1892 logprintf (LOG_NOTQUIET,
1893 _("Wrote HTML-ized index to `%s'.\n"),
1903 int wild = has_wildcards_p (u->file);
1904 if ((opt.ftp_glob && wild) || opt.recursive || opt.timestamping)
1906 /* ftp_retrieve_glob is a catch-all function that gets called
1907 if we need globbing, time-stamping or recursion. Its
1908 third argument is just what we really need. */
1909 res = ftp_retrieve_glob (u, &con,
1910 (opt.ftp_glob && wild) ? GLOBALL : GETONE);
1913 res = ftp_loop_internal (u, NULL, &con);
1919 /* If a connection was left, quench it. */
1920 if (rbuf_initialized_p (&con.rbuf))
1921 xclose (RBUF_FD (&con.rbuf));
1922 xfree_null (con.id);
1924 xfree_null (con.target);
1929 /* Delete an element from the fileinfo linked list. Returns the
1930 address of the next element, or NULL if the list is exhausted. It
1931 can modify the start of the list. */
1932 static struct fileinfo *
1933 delelement (struct fileinfo *f, struct fileinfo **start)
1935 struct fileinfo *prev = f->prev;
1936 struct fileinfo *next = f->next;
1939 xfree_null (f->linkto);
1951 /* Free the fileinfo linked list of files. */
1953 freefileinfo (struct fileinfo *f)
1957 struct fileinfo *next = f->next;