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 */
62 extern LARGE_INT total_downloaded_bytes;
64 /* File where the "ls -al" listing will be saved. */
65 #define LIST_FILENAME ".listing"
67 extern char ftp_last_respline[];
71 int st; /* connection status */
72 int cmd; /* command code */
73 struct rbuf rbuf; /* control connection buffer */
74 double dltime; /* time of the download in msecs */
75 enum stype rs; /* remote system reported by ftp server */
76 char *id; /* initial directory */
77 char *target; /* target file name */
78 struct url *proxy; /* FTWK-style proxy */
82 /* Look for regexp "( *[0-9]+ *byte" (literal parenthesis) anywhere in
83 the string S, and return the number converted to long, if found, 0
86 ftp_expected_bytes (const char *s)
92 while (*s && *s != '(')
96 for (++s; *s && ISSPACE (*s); s++);
104 res = (*s - '0') + 10 * res;
107 while (*s && ISDIGIT (*s));
110 while (*s && ISSPACE (*s))
114 if (TOLOWER (*s) != 'b')
116 if (strncasecmp (s, "byte", 4))
128 struct sockaddr_storage ss;
129 struct sockaddr *sa = (struct sockaddr *)&ss;
130 socklen_t len = sizeof (ss);
134 if (getpeername (fd, sa, &len) < 0)
135 /* Mauro Tortonesi: HOW DO WE HANDLE THIS ERROR? */
138 return sa->sa_family;
142 * This function sets up a passive data connection with the FTP server.
143 * It is merely a wrapper around ftp_epsv, ftp_lpsv and ftp_pasv.
146 ftp_do_pasv (struct rbuf *rbuf, ip_address *addr, int *port)
151 family = getfamily (rbuf->fd);
152 assert (family == AF_INET || family == AF_INET6);
154 /* If our control connection is over IPv6, then we first try EPSV and then
155 * LPSV if the former is not supported. If the control connection is over
156 * IPv4, we simply issue the good old PASV request. */
157 if (family == AF_INET6)
159 if (!opt.server_response)
160 logputs (LOG_VERBOSE, "==> EPSV ... ");
161 err = ftp_epsv (rbuf, addr, port);
163 /* If EPSV is not supported try LPSV */
164 if (err == FTPNOPASV)
166 if (!opt.server_response)
167 logputs (LOG_VERBOSE, "==> LPSV ... ");
168 err = ftp_lpsv (rbuf, addr, port);
173 if (!opt.server_response)
174 logputs (LOG_VERBOSE, "==> PASV ... ");
175 err = ftp_pasv (rbuf, addr, port);
182 * This function sets up an active data connection with the FTP server.
183 * It is merely a wrapper around ftp_eprt, ftp_lprt and ftp_port.
186 ftp_do_port (struct rbuf *rbuf, int *local_sock)
191 assert (rbuf != NULL);
192 assert (rbuf_initialized_p (rbuf));
194 family = getfamily (rbuf->fd);
195 assert (family == AF_INET || family == AF_INET6);
197 /* If our control connection is over IPv6, then we first try EPRT and then
198 * LPRT if the former is not supported. If the control connection is over
199 * IPv4, we simply issue the good old PORT request. */
200 if (family == AF_INET6)
202 if (!opt.server_response)
203 logputs (LOG_VERBOSE, "==> EPRT ... ");
204 err = ftp_eprt (rbuf, local_sock);
206 /* If EPRT is not supported try LPRT */
207 if (err == FTPPORTERR)
209 if (!opt.server_response)
210 logputs (LOG_VERBOSE, "==> LPRT ... ");
211 err = ftp_lprt (rbuf, local_sock);
216 if (!opt.server_response)
217 logputs (LOG_VERBOSE, "==> PORT ... ");
218 err = ftp_port (rbuf, local_sock);
226 ftp_do_pasv (struct rbuf *rbuf, ip_address *addr, int *port)
228 if (!opt.server_response)
229 logputs (LOG_VERBOSE, "==> PASV ... ");
230 return ftp_pasv (rbuf, addr, port);
234 ftp_do_port (struct rbuf *rbuf, int *local_sock)
236 if (!opt.server_response)
237 logputs (LOG_VERBOSE, "==> PORT ... ");
238 return ftp_port (rbuf, local_sock);
242 /* Retrieves a file with denoted parameters through opening an FTP
243 connection to the server. It always closes the data connection,
244 and closes the control connection in case of error. */
246 getftp (struct url *u, long *len, long restval, ccon *con)
248 int csock, dtsock, local_sock, res;
251 char *user, *passwd, *respline;
254 int pasv_mode_open = 0;
255 long expected_bytes = 0L;
257 assert (con != NULL);
258 assert (con->target != NULL);
260 /* Debug-check of the sanity of the request by making sure that LIST
261 and RETR are never both requested (since we can handle only one
263 assert (!((cmd & DO_LIST) && (cmd & DO_RETR)));
264 /* Make sure that at least *something* is requested. */
265 assert ((cmd & (DO_LIST | DO_CWD | DO_RETR | DO_LOGIN)) != 0);
269 search_netrc (u->host, (const char **)&user, (const char **)&passwd, 1);
270 user = user ? user : opt.ftp_acc;
271 passwd = passwd ? passwd : opt.ftp_pass;
272 assert (user && passwd);
278 if (!(cmd & DO_LOGIN))
279 csock = RBUF_FD (&con->rbuf);
280 else /* cmd & DO_LOGIN */
283 char *host = con->proxy ? con->proxy->host : u->host;
284 int port = con->proxy ? con->proxy->port : u->port;
285 char *logname = user;
289 /* If proxy is in use, log in as username@target-site. */
290 logname = xmalloc (strlen (user) + 1 + strlen (u->host) + 1);
291 sprintf (logname, "%s@%s", user, u->host);
294 /* Login to the server: */
296 /* First: Establish the control connection. */
298 csock = connect_to_host (host, port);
302 return CONNECT_ERROR (errno);
304 if (cmd & LEAVE_PENDING)
305 rbuf_initialize (&con->rbuf, csock);
307 rbuf_uninitialize (&con->rbuf);
309 /* Since this is a new connection, we may safely discard
310 anything left in the buffer. */
311 rbuf_discard (&con->rbuf);
313 /* Second: Login with proper USER/PASS sequence. */
314 logprintf (LOG_VERBOSE, _("Logging in as %s ... "), user);
315 if (opt.server_response)
316 logputs (LOG_ALWAYS, "\n");
317 err = ftp_login (&con->rbuf, logname, passwd);
322 /* FTPRERR, FTPSRVERR, WRITEFAILED, FTPLOGREFUSED, FTPLOGINC */
326 logputs (LOG_VERBOSE, "\n");
327 logputs (LOG_NOTQUIET, _("\
328 Error in server response, closing control connection.\n"));
330 rbuf_uninitialize (&con->rbuf);
334 logputs (LOG_VERBOSE, "\n");
335 logputs (LOG_NOTQUIET, _("Error in server greeting.\n"));
337 rbuf_uninitialize (&con->rbuf);
341 logputs (LOG_VERBOSE, "\n");
342 logputs (LOG_NOTQUIET,
343 _("Write failed, closing control connection.\n"));
345 rbuf_uninitialize (&con->rbuf);
349 logputs (LOG_VERBOSE, "\n");
350 logputs (LOG_NOTQUIET, _("The server refuses login.\n"));
352 rbuf_uninitialize (&con->rbuf);
353 return FTPLOGREFUSED;
356 logputs (LOG_VERBOSE, "\n");
357 logputs (LOG_NOTQUIET, _("Login incorrect.\n"));
359 rbuf_uninitialize (&con->rbuf);
363 if (!opt.server_response)
364 logputs (LOG_VERBOSE, _("Logged in!\n"));
371 /* Third: Get the system type */
372 if (!opt.server_response)
373 logprintf (LOG_VERBOSE, "==> SYST ... ");
374 err = ftp_syst (&con->rbuf, &con->rs);
379 logputs (LOG_VERBOSE, "\n");
380 logputs (LOG_NOTQUIET, _("\
381 Error in server response, closing control connection.\n"));
383 rbuf_uninitialize (&con->rbuf);
387 logputs (LOG_VERBOSE, "\n");
388 logputs (LOG_NOTQUIET,
389 _("Server error, can't determine system type.\n"));
392 /* Everything is OK. */
398 if (!opt.server_response && err != FTPSRVERR)
399 logputs (LOG_VERBOSE, _("done. "));
401 /* Fourth: Find the initial ftp directory */
403 if (!opt.server_response)
404 logprintf (LOG_VERBOSE, "==> PWD ... ");
405 err = ftp_pwd(&con->rbuf, &con->id);
410 logputs (LOG_VERBOSE, "\n");
411 logputs (LOG_NOTQUIET, _("\
412 Error in server response, closing control connection.\n"));
414 rbuf_uninitialize (&con->rbuf);
418 /* PWD unsupported -- assume "/". */
419 FREE_MAYBE (con->id);
420 con->id = xstrdup ("/");
423 /* Everything is OK. */
429 /* VMS will report something like "PUB$DEVICE:[INITIAL.FOLDER]".
430 Convert it to "/INITIAL/FOLDER" */
431 if (con->rs == ST_VMS)
433 char *path = strchr (con->id, '[');
434 char *pathend = path ? strchr (path + 1, ']') : NULL;
435 if (!path || !pathend)
436 DEBUGP (("Initial VMS directory not in the form [...]!\n"));
439 char *idir = con->id;
440 DEBUGP (("Preprocessing the initial VMS directory\n"));
441 DEBUGP ((" old = '%s'\n", con->id));
442 /* We do the conversion in-place by copying the stuff
443 between [ and ] to the beginning, and changing dots
444 to slashes at the same time. */
446 for (++path; path < pathend; path++, idir++)
447 *idir = *path == '.' ? '/' : *path;
449 DEBUGP ((" new = '%s'\n\n", con->id));
452 if (!opt.server_response)
453 logputs (LOG_VERBOSE, _("done.\n"));
455 /* Fifth: Set the FTP type. */
456 type_char = ftp_process_type (u->params);
457 if (!opt.server_response)
458 logprintf (LOG_VERBOSE, "==> TYPE %c ... ", type_char);
459 err = ftp_type (&con->rbuf, type_char);
460 /* FTPRERR, WRITEFAILED, FTPUNKNOWNTYPE */
464 logputs (LOG_VERBOSE, "\n");
465 logputs (LOG_NOTQUIET, _("\
466 Error in server response, closing control connection.\n"));
468 rbuf_uninitialize (&con->rbuf);
472 logputs (LOG_VERBOSE, "\n");
473 logputs (LOG_NOTQUIET,
474 _("Write failed, closing control connection.\n"));
476 rbuf_uninitialize (&con->rbuf);
480 logputs (LOG_VERBOSE, "\n");
481 logprintf (LOG_NOTQUIET,
482 _("Unknown type `%c', closing control connection.\n"),
485 rbuf_uninitialize (&con->rbuf);
488 /* Everything is OK. */
494 if (!opt.server_response)
495 logputs (LOG_VERBOSE, _("done. "));
501 logputs (LOG_VERBOSE, _("==> CWD not needed.\n"));
504 char *target = u->dir;
506 DEBUGP (("changing working directory\n"));
508 /* Change working directory. To change to a non-absolute
509 Unix directory, we need to prepend initial directory
510 (con->id) to it. Absolute directories "just work".
512 A relative directory is one that does not begin with '/'
513 and, on non-Unix OS'es, one that doesn't begin with
516 This is not done for OS400, which doesn't use
517 "/"-delimited directories, nor does it support directory
518 hierarchies. "CWD foo" followed by "CWD bar" leaves us
519 in "bar", not in "foo/bar", as would be customary
523 && !(con->rs != ST_UNIX
524 && ISALPHA (target[0])
526 && con->rs != ST_OS400)
528 int idlen = strlen (con->id);
531 /* Strip trailing slash(es) from con->id. */
532 while (idlen > 0 && con->id[idlen - 1] == '/')
534 p = ntarget = (char *)alloca (idlen + 1 + strlen (u->dir) + 1);
535 memcpy (p, con->id, idlen);
540 DEBUGP (("Prepended initial PWD to relative path:\n"));
541 DEBUGP ((" pwd: '%s'\n old: '%s'\n new: '%s'\n",
542 con->id, target, ntarget));
546 /* If the FTP host runs VMS, we will have to convert the absolute
547 directory path in UNIX notation to absolute directory path in
548 VMS notation as VMS FTP servers do not like UNIX notation of
549 absolute paths. "VMS notation" is [dir.subdir.subsubdir]. */
551 if (con->rs == ST_VMS)
554 char *ntarget = (char *)alloca (strlen (target) + 2);
555 /* We use a converted initial dir, so directories in
556 TARGET will be separated with slashes, something like
557 "/INITIAL/FOLDER/DIR/SUBDIR". Convert that to
558 "[INITIAL.FOLDER.DIR.SUBDIR]". */
559 strcpy (ntarget, target);
560 assert (*ntarget == '/');
562 for (tmpp = ntarget + 1; *tmpp; tmpp++)
567 DEBUGP (("Changed file name to VMS syntax:\n"));
568 DEBUGP ((" Unix: '%s'\n VMS: '%s'\n", target, ntarget));
572 if (!opt.server_response)
573 logprintf (LOG_VERBOSE, "==> CWD %s ... ", target);
574 err = ftp_cwd (&con->rbuf, target);
575 /* FTPRERR, WRITEFAILED, FTPNSFOD */
579 logputs (LOG_VERBOSE, "\n");
580 logputs (LOG_NOTQUIET, _("\
581 Error in server response, closing control connection.\n"));
583 rbuf_uninitialize (&con->rbuf);
587 logputs (LOG_VERBOSE, "\n");
588 logputs (LOG_NOTQUIET,
589 _("Write failed, closing control connection.\n"));
591 rbuf_uninitialize (&con->rbuf);
595 logputs (LOG_VERBOSE, "\n");
596 logprintf (LOG_NOTQUIET, _("No such directory `%s'.\n\n"),
599 rbuf_uninitialize (&con->rbuf);
609 if (!opt.server_response)
610 logputs (LOG_VERBOSE, _("done.\n"));
613 else /* do not CWD */
614 logputs (LOG_VERBOSE, _("==> CWD not required.\n"));
616 if ((cmd & DO_RETR) && restval && *len == 0)
620 if (!opt.server_response)
621 logprintf (LOG_VERBOSE, "==> SIZE %s ... ", u->file);
624 err = ftp_size(&con->rbuf, u->file, len);
630 logputs (LOG_VERBOSE, "\n");
631 logputs (LOG_NOTQUIET, _("\
632 Error in server response, closing control connection.\n"));
634 rbuf_uninitialize (&con->rbuf);
638 /* Everything is OK. */
644 if (!opt.server_response)
645 logputs (LOG_VERBOSE, _("done.\n"));
648 /* If anything is to be retrieved, PORT (or PASV) must be sent. */
649 if (cmd & (DO_LIST | DO_RETR))
651 if (opt.ftp_pasv > 0)
653 ip_address passive_addr;
655 err = ftp_do_pasv (&con->rbuf, &passive_addr, &passive_port);
656 /* FTPRERR, WRITEFAILED, FTPNOPASV, FTPINVPASV */
660 logputs (LOG_VERBOSE, "\n");
661 logputs (LOG_NOTQUIET, _("\
662 Error in server response, closing control connection.\n"));
664 rbuf_uninitialize (&con->rbuf);
668 logputs (LOG_VERBOSE, "\n");
669 logputs (LOG_NOTQUIET,
670 _("Write failed, closing control connection.\n"));
672 rbuf_uninitialize (&con->rbuf);
676 logputs (LOG_VERBOSE, "\n");
677 logputs (LOG_NOTQUIET, _("Cannot initiate PASV transfer.\n"));
680 logputs (LOG_VERBOSE, "\n");
681 logputs (LOG_NOTQUIET, _("Cannot parse PASV response.\n"));
692 DEBUGP (("trying to connect to %s port %d\n",
693 pretty_print_address (&passive_addr),
695 dtsock = connect_to_ip (&passive_addr, passive_port, NULL);
698 int save_errno = errno;
700 rbuf_uninitialize (&con->rbuf);
701 logprintf (LOG_VERBOSE, _("couldn't connect to %s port %hu: %s\n"),
702 pretty_print_address (&passive_addr), passive_port,
703 strerror (save_errno));
704 return CONNECT_ERROR (save_errno);
707 pasv_mode_open = 1; /* Flag to avoid accept port */
708 if (!opt.server_response)
709 logputs (LOG_VERBOSE, _("done. "));
713 if (!pasv_mode_open) /* Try to use a port command if PASV failed */
715 err = ftp_do_port (&con->rbuf, &local_sock);
716 /* FTPRERR, WRITEFAILED, bindport (CONSOCKERR, CONPORTERR, BINDERR,
717 LISTENERR), HOSTERR, FTPPORTERR */
721 logputs (LOG_VERBOSE, "\n");
722 logputs (LOG_NOTQUIET, _("\
723 Error in server response, closing control connection.\n"));
727 rbuf_uninitialize (&con->rbuf);
731 logputs (LOG_VERBOSE, "\n");
732 logputs (LOG_NOTQUIET,
733 _("Write failed, closing control connection.\n"));
737 rbuf_uninitialize (&con->rbuf);
741 logputs (LOG_VERBOSE, "\n");
742 logprintf (LOG_NOTQUIET, "socket: %s\n", strerror (errno));
746 rbuf_uninitialize (&con->rbuf);
749 case CONPORTERR: case BINDERR: case LISTENERR:
750 /* What now? These problems are local... */
751 logputs (LOG_VERBOSE, "\n");
752 logprintf (LOG_NOTQUIET, _("Bind error (%s).\n"),
759 logputs (LOG_VERBOSE, "\n");
760 logputs (LOG_NOTQUIET, _("Invalid PORT.\n"));
764 rbuf_uninitialize (&con->rbuf);
774 if (!opt.server_response)
775 logputs (LOG_VERBOSE, _("done. "));
777 } /* cmd & (DO_LIST | DO_RETR) */
779 /* Restart if needed. */
780 if (restval && (cmd & DO_RETR))
782 if (!opt.server_response)
783 logprintf (LOG_VERBOSE, "==> REST %ld ... ", restval);
784 err = ftp_rest (&con->rbuf, restval);
786 /* FTPRERR, WRITEFAILED, FTPRESTFAIL */
790 logputs (LOG_VERBOSE, "\n");
791 logputs (LOG_NOTQUIET, _("\
792 Error in server response, closing control connection.\n"));
796 rbuf_uninitialize (&con->rbuf);
800 logputs (LOG_VERBOSE, "\n");
801 logputs (LOG_NOTQUIET,
802 _("Write failed, closing control connection.\n"));
806 rbuf_uninitialize (&con->rbuf);
810 /* If `-c' is specified and the file already existed when
811 Wget was started, it would be a bad idea for us to start
812 downloading it from scratch, effectively truncating it. */
813 if (opt.always_rest && (cmd & NO_TRUNCATE))
815 logprintf (LOG_NOTQUIET,
816 _("\nREST failed; will not truncate `%s'.\n"),
821 rbuf_uninitialize (&con->rbuf);
822 return CONTNOTSUPPORTED;
824 logputs (LOG_VERBOSE, _("\nREST failed, starting from scratch.\n"));
834 if (err != FTPRESTFAIL && !opt.server_response)
835 logputs (LOG_VERBOSE, _("done. "));
836 } /* restval && cmd & DO_RETR */
840 /* If we're in spider mode, don't really retrieve anything. The
841 fact that we got to this point should be proof enough that
842 the file exists, vaguely akin to HTTP's concept of a "HEAD"
849 rbuf_uninitialize (&con->rbuf);
855 if (!opt.server_response)
858 logputs (LOG_VERBOSE, "\n");
859 logprintf (LOG_VERBOSE, "==> RETR %s ... ", u->file);
863 err = ftp_retr (&con->rbuf, u->file);
864 /* FTPRERR, WRITEFAILED, FTPNSFOD */
868 logputs (LOG_VERBOSE, "\n");
869 logputs (LOG_NOTQUIET, _("\
870 Error in server response, closing control connection.\n"));
874 rbuf_uninitialize (&con->rbuf);
878 logputs (LOG_VERBOSE, "\n");
879 logputs (LOG_NOTQUIET,
880 _("Write failed, closing control connection.\n"));
884 rbuf_uninitialize (&con->rbuf);
888 logputs (LOG_VERBOSE, "\n");
889 logprintf (LOG_NOTQUIET, _("No such file `%s'.\n\n"), u->file);
902 if (!opt.server_response)
903 logputs (LOG_VERBOSE, _("done.\n"));
904 expected_bytes = ftp_expected_bytes (ftp_last_respline);
909 if (!opt.server_response)
910 logputs (LOG_VERBOSE, "==> LIST ... ");
911 /* As Maciej W. Rozycki (macro@ds2.pg.gda.pl) says, `LIST'
912 without arguments is better than `LIST .'; confirmed by
914 err = ftp_list (&con->rbuf, NULL);
915 /* FTPRERR, WRITEFAILED */
919 logputs (LOG_VERBOSE, "\n");
920 logputs (LOG_NOTQUIET, _("\
921 Error in server response, closing control connection.\n"));
925 rbuf_uninitialize (&con->rbuf);
929 logputs (LOG_VERBOSE, "\n");
930 logputs (LOG_NOTQUIET,
931 _("Write failed, closing control connection.\n"));
935 rbuf_uninitialize (&con->rbuf);
939 logputs (LOG_VERBOSE, "\n");
940 logprintf (LOG_NOTQUIET, _("No such file or directory `%s'.\n\n"),
953 if (!opt.server_response)
954 logputs (LOG_VERBOSE, _("done.\n"));
955 expected_bytes = ftp_expected_bytes (ftp_last_respline);
956 } /* cmd & DO_LIST */
958 if (!(cmd & (DO_LIST | DO_RETR)) || (opt.spider && !(cmd & DO_LIST)))
961 /* Some FTP servers return the total length of file after REST
962 command, others just return the remaining size. */
963 if (*len && restval && expected_bytes
964 && (expected_bytes == *len - restval))
966 DEBUGP (("Lying FTP server found, adjusting.\n"));
967 expected_bytes = *len;
970 /* If no transmission was required, then everything is OK. */
971 if (!pasv_mode_open) /* we are not using pasive mode so we need
974 /* Open the data transmission socket by calling acceptport(). */
975 err = acceptport (local_sock, &dtsock);
976 /* Possible errors: ACCEPTERR. */
977 if (err == ACCEPTERR)
979 logprintf (LOG_NOTQUIET, "accept: %s\n", strerror (errno));
984 /* Open the file -- if opt.dfp is set, use it instead. */
985 if (!opt.dfp || con->cmd & DO_LIST)
987 mkalldirs (con->target);
989 rotate_backups (con->target);
990 /* #### Is this correct? */
991 chmod (con->target, 0600);
993 fp = fopen (con->target, restval ? "ab" : "wb");
996 logprintf (LOG_NOTQUIET, "%s: %s\n", con->target, strerror (errno));
998 rbuf_uninitialize (&con->rbuf);
1006 extern int global_download_count;
1009 /* Rewind the output document if the download starts over and if
1010 this is the first download. See gethttp() for a longer
1012 if (!restval && global_download_count == 0 && opt.dfp != stdout)
1014 /* This will silently fail for streams that don't correspond
1015 to regular files, but that's OK. */
1017 /* ftruncate is needed because opt.dfp is opened in append
1018 mode if opt.always_rest is set. */
1019 ftruncate (fileno (fp), 0);
1026 logprintf (LOG_VERBOSE, _("Length: %s"), legible (*len));
1028 logprintf (LOG_VERBOSE, _(" [%s to go]"), legible (*len - restval));
1029 logputs (LOG_VERBOSE, "\n");
1030 expected_bytes = *len; /* for get_contents/show_progress */
1032 else if (expected_bytes)
1034 logprintf (LOG_VERBOSE, _("Length: %s"), legible (expected_bytes));
1036 logprintf (LOG_VERBOSE, _(" [%s to go]"),
1037 legible (expected_bytes - restval));
1038 logputs (LOG_VERBOSE, _(" (unauthoritative)\n"));
1041 /* Get the contents of the document. */
1042 res = get_contents (dtsock, fp, len, restval, expected_bytes, &con->rbuf,
1044 tms = time_str (NULL);
1045 tmrate = retr_rate (*len - restval, con->dltime, 0);
1046 /* Close data connection socket. */
1049 /* Close the local file. */
1051 /* Close or flush the file. We have to be careful to check for
1052 error here. Checking the result of fwrite() is not enough --
1053 errors could go unnoticed! */
1055 if (!opt.dfp || con->cmd & DO_LIST)
1056 flush_res = fclose (fp);
1058 flush_res = fflush (fp);
1059 if (flush_res == EOF)
1063 /* If get_contents couldn't write to fp, bail out. */
1066 logprintf (LOG_NOTQUIET, _("%s: %s, closing control connection.\n"),
1067 con->target, strerror (errno));
1069 rbuf_uninitialize (&con->rbuf);
1074 logprintf (LOG_NOTQUIET, _("%s (%s) - Data connection: %s; "),
1075 tms, tmrate, strerror (errno));
1076 if (opt.server_response)
1077 logputs (LOG_ALWAYS, "\n");
1080 /* Get the server to tell us if everything is retrieved. */
1081 err = ftp_response (&con->rbuf, &respline);
1082 /* ...and empty the buffer. */
1083 rbuf_discard (&con->rbuf);
1087 /* The control connection is decidedly closed. Print the time
1088 only if it hasn't already been printed. */
1090 logprintf (LOG_NOTQUIET, "%s (%s) - ", tms, tmrate);
1091 logputs (LOG_NOTQUIET, _("Control connection closed.\n"));
1092 /* If there is an error on the control connection, close it, but
1093 return FTPRETRINT, since there is a possibility that the
1094 whole file was retrieved nevertheless (but that is for
1095 ftp_loop_internal to decide). */
1097 rbuf_uninitialize (&con->rbuf);
1099 } /* err != FTPOK */
1100 /* If retrieval failed for any reason, return FTPRETRINT, but do not
1101 close socket, since the control connection is still alive. If
1102 there is something wrong with the control connection, it will
1103 become apparent later. */
1104 if (*respline != '2')
1108 logprintf (LOG_NOTQUIET, "%s (%s) - ", tms, tmrate);
1109 logputs (LOG_NOTQUIET, _("Data transfer aborted.\n"));
1116 /* What now? The data connection was erroneous, whereas the
1117 response says everything is OK. We shall play it safe. */
1121 if (!(cmd & LEAVE_PENDING))
1123 /* I should probably send 'QUIT' and check for a reply, but this
1124 is faster. #### Is it OK, though? */
1126 rbuf_uninitialize (&con->rbuf);
1128 /* If it was a listing, and opt.server_response is true,
1130 if (opt.server_response && (con->cmd & DO_LIST))
1132 mkalldirs (con->target);
1133 fp = fopen (con->target, "r");
1135 logprintf (LOG_ALWAYS, "%s: %s\n", con->target, strerror (errno));
1139 /* The lines are being read with read_whole_line because of
1140 no-buffering on opt.lfile. */
1141 while ((line = read_whole_line (fp)))
1143 logprintf (LOG_ALWAYS, "%s\n", line);
1148 } /* con->cmd & DO_LIST && server_response */
1150 return RETRFINISHED;
1153 /* A one-file FTP loop. This is the part where FTP retrieval is
1154 retried, and retried, and retried, and...
1156 This loop either gets commands from con, or (if ON_YOUR_OWN is
1157 set), makes them up to retrieve the file given by the URL. */
1159 ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
1164 char *tmrate = NULL;
1169 con->target = url_file_name (u);
1171 if (opt.noclobber && file_exists_p (con->target))
1173 logprintf (LOG_VERBOSE,
1174 _("File `%s' already there, not retrieving.\n"), con->target);
1175 /* If the file is there, we suppose it's retrieved OK. */
1179 /* Remove it if it's a link. */
1180 remove_link (con->target);
1181 if (!opt.output_document)
1184 locf = opt.output_document;
1188 if (con->st & ON_YOUR_OWN)
1189 con->st = ON_YOUR_OWN;
1191 orig_lp = con->cmd & LEAVE_PENDING ? 1 : 0;
1196 /* Increment the pass counter. */
1198 sleep_between_retrievals (count);
1199 if (con->st & ON_YOUR_OWN)
1202 con->cmd |= (DO_RETR | LEAVE_PENDING);
1203 if (rbuf_initialized_p (&con->rbuf))
1204 con->cmd &= ~ (DO_LOGIN | DO_CWD);
1206 con->cmd |= (DO_LOGIN | DO_CWD);
1208 else /* not on your own */
1210 if (rbuf_initialized_p (&con->rbuf))
1211 con->cmd &= ~DO_LOGIN;
1213 con->cmd |= DO_LOGIN;
1214 if (con->st & DONE_CWD)
1215 con->cmd &= ~DO_CWD;
1220 /* Assume no restarting. */
1222 if ((count > 1 || opt.always_rest)
1223 && !(con->cmd & DO_LIST)
1224 && file_exists_p (locf))
1225 if (stat (locf, &st) == 0 && S_ISREG (st.st_mode))
1226 restval = st.st_size;
1228 /* In `-c' is used, check whether the file we're writing to
1229 exists and is of non-zero length. If so, we'll refuse to
1230 truncate it if the server doesn't support continued
1232 if (opt.always_rest && restval > 0)
1233 con->cmd |= NO_TRUNCATE;
1235 /* Get the current time string. */
1236 tms = time_str (NULL);
1237 /* Print fetch message, if opt.verbose. */
1240 char *hurl = url_string (u, 1);
1244 sprintf (tmp, _("(try:%2d)"), count);
1245 logprintf (LOG_VERBOSE, "--%s-- %s\n %s => `%s'\n",
1246 tms, hurl, tmp, locf);
1248 ws_changetitle (hurl, 1);
1252 /* Send getftp the proper length, if fileinfo was provided. */
1257 err = getftp (u, &len, restval, con);
1259 if (!rbuf_initialized_p (&con->rbuf))
1260 con->st &= ~DONE_CWD;
1262 con->st |= DONE_CWD;
1266 case HOSTERR: case CONIMPOSSIBLE: case FWRITEERR: case FOPENERR:
1267 case FTPNSFOD: case FTPLOGINC: case FTPNOPASV: case CONTNOTSUPPORTED:
1268 /* Fatal errors, give up. */
1271 case CONSOCKERR: case CONERROR: case FTPSRVERR: case FTPRERR:
1272 case WRITEFAILED: case FTPUNKNOWNTYPE: case CONPORTERR:
1273 case BINDERR: case LISTENERR: case ACCEPTERR:
1274 case FTPPORTERR: case FTPLOGREFUSED: case FTPINVPASV:
1275 printwhat (count, opt.ntry);
1276 /* non-fatal errors */
1280 /* If the control connection was closed, the retrieval
1281 will be considered OK if f->size == len. */
1282 if (!f || len != f->size)
1284 printwhat (count, opt.ntry);
1296 tms = time_str (NULL);
1298 tmrate = retr_rate (len - restval, con->dltime, 0);
1300 /* If we get out of the switch above without continue'ing, we've
1301 successfully downloaded a file. Remember this fact. */
1302 downloaded_file (FILE_DOWNLOADED_NORMALLY, locf);
1304 if (con->st & ON_YOUR_OWN)
1306 CLOSE (RBUF_FD (&con->rbuf));
1307 rbuf_uninitialize (&con->rbuf);
1310 logprintf (LOG_VERBOSE, _("%s (%s) - `%s' saved [%ld]\n\n"),
1311 tms, tmrate, locf, len);
1312 if (!opt.verbose && !opt.quiet)
1314 /* Need to hide the password from the URL. The `if' is here
1315 so that we don't do the needless allocation every
1317 char *hurl = url_string (u, 1);
1318 logprintf (LOG_NONVERBOSE, "%s URL: %s [%ld] -> \"%s\" [%d]\n",
1319 tms, hurl, len, locf, count);
1323 if ((con->cmd & DO_LIST))
1324 /* This is a directory listing file. */
1326 if (!opt.remove_listing)
1327 /* --dont-remove-listing was specified, so do count this towards the
1328 number of bytes and files downloaded. */
1330 total_downloaded_bytes += len;
1334 /* Deletion of listing files is not controlled by --delete-after, but
1335 by the more specific option --dont-remove-listing, and the code
1336 to do this deletion is in another function. */
1338 else if (!opt.spider)
1339 /* This is not a directory listing file. */
1341 /* Unlike directory listing files, don't pretend normal files weren't
1342 downloaded if they're going to be deleted. People seeding proxies,
1343 for instance, may want to know how many bytes and files they've
1344 downloaded through it. */
1345 total_downloaded_bytes += len;
1348 if (opt.delete_after)
1350 DEBUGP (("Removing file due to --delete-after in"
1351 " ftp_loop_internal():\n"));
1352 logprintf (LOG_VERBOSE, _("Removing %s.\n"), locf);
1354 logprintf (LOG_NOTQUIET, "unlink: %s\n", strerror (errno));
1358 /* Restore the original leave-pendingness. */
1360 con->cmd |= LEAVE_PENDING;
1362 con->cmd &= ~LEAVE_PENDING;
1364 } while (!opt.ntry || (count < opt.ntry));
1366 if (rbuf_initialized_p (&con->rbuf) && (con->st & ON_YOUR_OWN))
1368 CLOSE (RBUF_FD (&con->rbuf));
1369 rbuf_uninitialize (&con->rbuf);
1374 /* Return the directory listing in a reusable format. The directory
1375 is specifed in u->dir. */
1377 ftp_get_listing (struct url *u, ccon *con, struct fileinfo **f)
1380 char *uf; /* url file name */
1381 char *lf; /* list file name */
1382 char *old_target = con->target;
1384 con->st &= ~ON_YOUR_OWN;
1385 con->cmd |= (DO_LIST | LEAVE_PENDING);
1386 con->cmd &= ~DO_RETR;
1388 /* Find the listing file name. We do it by taking the file name of
1389 the URL and replacing the last component with the listing file
1391 uf = url_file_name (u);
1392 lf = file_merge (uf, LIST_FILENAME);
1394 DEBUGP ((_("Using `%s' as listing tmp file.\n"), lf));
1397 err = ftp_loop_internal (u, NULL, con);
1398 con->target = old_target;
1401 *f = ftp_parse_ls (lf, con->rs);
1404 if (opt.remove_listing)
1407 logprintf (LOG_NOTQUIET, "unlink: %s\n", strerror (errno));
1409 logprintf (LOG_VERBOSE, _("Removed `%s'.\n"), lf);
1412 con->cmd &= ~DO_LIST;
1416 static uerr_t ftp_retrieve_dirs PARAMS ((struct url *, struct fileinfo *,
1418 static uerr_t ftp_retrieve_glob PARAMS ((struct url *, ccon *, int));
1419 static struct fileinfo *delelement PARAMS ((struct fileinfo *,
1420 struct fileinfo **));
1421 static void freefileinfo PARAMS ((struct fileinfo *f));
1423 /* Retrieve a list of files given in struct fileinfo linked list. If
1424 a file is a symbolic link, do not retrieve it, but rather try to
1425 set up a similar link on the local disk, if the symlinks are
1428 If opt.recursive is set, after all files have been retrieved,
1429 ftp_retrieve_dirs will be called to retrieve the directories. */
1431 ftp_retrieve_list (struct url *u, struct fileinfo *f, ccon *con)
1433 static int depth = 0;
1435 struct fileinfo *orig;
1440 /* Increase the depth. */
1442 if (opt.reclevel != INFINITE_RECURSION && depth > opt.reclevel)
1444 DEBUGP ((_("Recursion depth %d exceeded max. depth %d.\n"),
1445 depth, opt.reclevel));
1453 con->st &= ~ON_YOUR_OWN;
1454 if (!(con->st & DONE_CWD))
1457 con->cmd &= ~DO_CWD;
1458 con->cmd |= (DO_RETR | LEAVE_PENDING);
1460 if (!rbuf_initialized_p (&con->rbuf))
1461 con->cmd |= DO_LOGIN;
1463 con->cmd &= ~DO_LOGIN;
1465 err = RETROK; /* in case it's not used */
1469 char *old_target, *ofile;
1471 if (opt.quota && total_downloaded_bytes > opt.quota)
1476 old_target = con->target;
1478 ofile = xstrdup (u->file);
1479 url_set_file (u, f->name);
1481 con->target = url_file_name (u);
1485 if (opt.timestamping && f->type == FT_PLAINFILE)
1488 /* If conversion of HTML files retrieved via FTP is ever implemented,
1489 we'll need to stat() <file>.orig here when -K has been specified.
1490 I'm not implementing it now since files on an FTP server are much
1491 more likely than files on an HTTP server to legitimately have a
1493 if (!stat (con->target, &st))
1497 /* Else, get it from the file. */
1498 local_size = st.st_size;
1501 /* Modification time granularity is 2 seconds for Windows, so
1502 increase local time by 1 second for later comparison. */
1505 /* Compare file sizes only for servers that tell us correct
1506 values. Assumme sizes being equal for servers that lie
1508 cor_val = (con->rs == ST_UNIX || con->rs == ST_WINNT);
1509 eq_size = cor_val ? (local_size == f->size) : 1 ;
1510 if (f->tstamp <= tml && eq_size)
1512 /* Remote file is older, file sizes can be compared and
1514 logprintf (LOG_VERBOSE, _("\
1515 Remote file no newer than local file `%s' -- not retrieving.\n"), con->target);
1520 /* Remote file is newer or sizes cannot be matched */
1521 logprintf (LOG_VERBOSE, _("\
1522 Remote file is newer than local file `%s' -- retrieving.\n\n"),
1527 /* Sizes do not match */
1528 logprintf (LOG_VERBOSE, _("\
1529 The sizes do not match (local %ld) -- retrieving.\n\n"), local_size);
1532 } /* opt.timestamping && f->type == FT_PLAINFILE */
1536 /* If opt.retr_symlinks is defined, we treat symlinks as
1537 if they were normal files. There is currently no way
1538 to distinguish whether they might be directories, and
1540 if (!opt.retr_symlinks)
1544 logputs (LOG_NOTQUIET,
1545 _("Invalid name of the symlink, skipping.\n"));
1549 /* Check whether we already have the correct
1551 int rc = lstat (con->target, &st);
1554 size_t len = strlen (f->linkto) + 1;
1555 if (S_ISLNK (st.st_mode))
1557 char *link_target = (char *)alloca (len);
1558 size_t n = readlink (con->target, link_target, len);
1560 && (memcmp (link_target, f->linkto, n) == 0))
1562 logprintf (LOG_VERBOSE, _("\
1563 Already have correct symlink %s -> %s\n\n"),
1564 con->target, f->linkto);
1570 logprintf (LOG_VERBOSE, _("Creating symlink %s -> %s\n"),
1571 con->target, f->linkto);
1572 /* Unlink before creating symlink! */
1573 unlink (con->target);
1574 if (symlink (f->linkto, con->target) == -1)
1575 logprintf (LOG_NOTQUIET, "symlink: %s\n",
1577 logputs (LOG_VERBOSE, "\n");
1578 } /* have f->linkto */
1579 #else /* not HAVE_SYMLINK */
1580 logprintf (LOG_NOTQUIET,
1581 _("Symlinks not supported, skipping symlink `%s'.\n"),
1583 #endif /* not HAVE_SYMLINK */
1585 else /* opt.retr_symlinks */
1588 err = ftp_loop_internal (u, f, con);
1589 } /* opt.retr_symlinks */
1593 logprintf (LOG_NOTQUIET, _("Skipping directory `%s'.\n"),
1597 /* Call the retrieve loop. */
1599 err = ftp_loop_internal (u, f, con);
1602 logprintf (LOG_NOTQUIET, _("%s: unknown/unsupported file type.\n"),
1607 /* Set the time-stamp information to the local file. Symlinks
1608 are not to be stamped because it sets the stamp on the
1610 if (!(f->type == FT_SYMLINK && !opt.retr_symlinks)
1613 && file_exists_p (con->target))
1615 /* #### This code repeats in http.c and ftp.c. Move it to a
1617 const char *fl = NULL;
1618 if (opt.output_document)
1620 if (opt.od_known_regular)
1621 fl = opt.output_document;
1626 touch (fl, f->tstamp);
1628 else if (f->tstamp == -1)
1629 logprintf (LOG_NOTQUIET, _("%s: corrupt time-stamp.\n"), con->target);
1631 if (f->perms && f->type == FT_PLAINFILE && dlthis)
1632 chmod (con->target, f->perms);
1634 DEBUGP (("Unrecognized permissions for %s.\n", con->target));
1636 xfree (con->target);
1637 con->target = old_target;
1639 url_set_file (u, ofile);
1642 /* Break on fatals. */
1643 if (err == QUOTEXC || err == HOSTERR || err == FWRITEERR)
1645 con->cmd &= ~ (DO_CWD | DO_LOGIN);
1649 /* We do not want to call ftp_retrieve_dirs here */
1650 if (opt.recursive &&
1651 !(opt.reclevel != INFINITE_RECURSION && depth >= opt.reclevel))
1652 err = ftp_retrieve_dirs (u, orig, con);
1653 else if (opt.recursive)
1654 DEBUGP ((_("Will not retrieve dirs since depth is %d (max %d).\n"),
1655 depth, opt.reclevel));
1660 /* Retrieve the directories given in a file list. This function works
1661 by simply going through the linked list and calling
1662 ftp_retrieve_glob on each directory entry. The function knows
1663 about excluded directories. */
1665 ftp_retrieve_dirs (struct url *u, struct fileinfo *f, ccon *con)
1667 char *container = NULL;
1668 int container_size = 0;
1670 for (; f; f = f->next)
1673 char *odir, *newdir;
1675 if (opt.quota && total_downloaded_bytes > opt.quota)
1677 if (f->type != FT_DIRECTORY)
1680 /* Allocate u->dir off stack, but reallocate only if a larger
1681 string is needed. It's a pity there's no "realloca" for an
1682 item on the bottom of the stack. */
1683 size = strlen (u->dir) + 1 + strlen (f->name) + 1;
1684 if (size > container_size)
1685 container = (char *)alloca (size);
1690 || (*odir == '/' && *(odir + 1) == '\0'))
1691 /* If ODIR is empty or just "/", simply append f->name to
1692 ODIR. (In the former case, to preserve u->dir being
1693 relative; in the latter case, to avoid double slash.) */
1694 sprintf (newdir, "%s%s", odir, f->name);
1696 /* Else, use a separator. */
1697 sprintf (newdir, "%s/%s", odir, f->name);
1699 DEBUGP (("Composing new CWD relative to the initial directory.\n"));
1700 DEBUGP ((" odir = '%s'\n f->name = '%s'\n newdir = '%s'\n\n",
1701 odir, f->name, newdir));
1702 if (!accdir (newdir, ALLABS))
1704 logprintf (LOG_VERBOSE, _("\
1705 Not descending to `%s' as it is excluded/not-included.\n"), newdir);
1709 con->st &= ~DONE_CWD;
1711 odir = xstrdup (u->dir); /* because url_set_dir will free
1713 url_set_dir (u, newdir);
1714 ftp_retrieve_glob (u, con, GETALL);
1715 url_set_dir (u, odir);
1718 /* Set the time-stamp? */
1721 if (opt.quota && total_downloaded_bytes > opt.quota)
1727 /* Return non-zero if S has a leading '/' or contains '../' */
1729 has_insecure_name_p (const char *s)
1734 if (strstr(s, "../") != 0)
1740 /* A near-top-level function to retrieve the files in a directory.
1741 The function calls ftp_get_listing, to get a linked list of files.
1742 Then it weeds out the file names that do not match the pattern.
1743 ftp_retrieve_list is called with this updated list as an argument.
1745 If the argument ACTION is GETONE, just download the file (but first
1746 get the listing, so that the time-stamp is heeded); if it's GLOBALL,
1747 use globbing; if it's GETALL, download the whole directory. */
1749 ftp_retrieve_glob (struct url *u, ccon *con, int action)
1751 struct fileinfo *f, *start;
1754 con->cmd |= LEAVE_PENDING;
1756 res = ftp_get_listing (u, con, &start);
1759 /* First: weed out that do not conform the global rules given in
1760 opt.accepts and opt.rejects. */
1761 if (opt.accepts || opt.rejects)
1766 if (f->type != FT_DIRECTORY && !acceptable (f->name))
1768 logprintf (LOG_VERBOSE, _("Rejecting `%s'.\n"), f->name);
1769 f = delelement (f, &start);
1775 /* Remove all files with possible harmful names */
1779 if (has_insecure_name_p (f->name))
1781 logprintf (LOG_VERBOSE, _("Rejecting `%s'.\n"), f->name);
1782 f = delelement (f, &start);
1787 /* Now weed out the files that do not match our globbing pattern.
1788 If we are dealing with a globbing pattern, that is. */
1789 if (*u->file && (action == GLOBALL || action == GETONE))
1796 matchres = fnmatch (u->file, f->name, 0);
1799 logprintf (LOG_NOTQUIET, "%s: %s\n", con->target,
1803 if (matchres == FNM_NOMATCH)
1804 f = delelement (f, &start); /* delete the element from the list */
1806 f = f->next; /* leave the element in the list */
1810 freefileinfo (start);
1811 return RETRBADPATTERN;
1817 /* Just get everything. */
1818 ftp_retrieve_list (u, start, con);
1822 if (action == GLOBALL)
1825 /* #### This message SUCKS. We should see what was the
1826 reason that nothing was retrieved. */
1827 logprintf (LOG_VERBOSE, _("No matches on pattern `%s'.\n"), u->file);
1829 else /* GETONE or GETALL */
1831 /* Let's try retrieving it anyway. */
1832 con->st |= ON_YOUR_OWN;
1833 res = ftp_loop_internal (u, NULL, con);
1837 freefileinfo (start);
1838 if (opt.quota && total_downloaded_bytes > opt.quota)
1841 /* #### Should we return `res' here? */
1845 /* The wrapper that calls an appropriate routine according to contents
1846 of URL. Inherently, its capabilities are limited on what can be
1847 encoded into a URL. */
1849 ftp_loop (struct url *u, int *dt, struct url *proxy)
1851 ccon con; /* FTP connection */
1856 memset (&con, 0, sizeof (con));
1858 rbuf_uninitialize (&con.rbuf);
1859 con.st = ON_YOUR_OWN;
1863 res = RETROK; /* in case it's not used */
1865 /* If the file name is empty, the user probably wants a directory
1866 index. We'll provide one, properly HTML-ized. Unless
1867 opt.htmlify is 0, of course. :-) */
1868 if (!*u->file && !opt.recursive)
1871 res = ftp_get_listing (u, &con, &f);
1875 if (opt.htmlify && !opt.spider)
1877 char *filename = (opt.output_document
1878 ? xstrdup (opt.output_document)
1879 : (con.target ? xstrdup (con.target)
1880 : url_file_name (u)));
1881 res = ftp_index (filename, u, f);
1882 if (res == FTPOK && opt.verbose)
1884 if (!opt.output_document)
1888 if (stat (filename, &st) == 0)
1892 logprintf (LOG_NOTQUIET,
1893 _("Wrote HTML-ized index to `%s' [%ld].\n"),
1897 logprintf (LOG_NOTQUIET,
1898 _("Wrote HTML-ized index to `%s'.\n"),
1908 int wild = has_wildcards_p (u->file);
1909 if ((opt.ftp_glob && wild) || opt.recursive || opt.timestamping)
1911 /* ftp_retrieve_glob is a catch-all function that gets called
1912 if we need globbing, time-stamping or recursion. Its
1913 third argument is just what we really need. */
1914 res = ftp_retrieve_glob (u, &con,
1915 (opt.ftp_glob && wild) ? GLOBALL : GETONE);
1918 res = ftp_loop_internal (u, NULL, &con);
1924 /* If a connection was left, quench it. */
1925 if (rbuf_initialized_p (&con.rbuf))
1926 CLOSE (RBUF_FD (&con.rbuf));
1927 FREE_MAYBE (con.id);
1929 FREE_MAYBE (con.target);
1934 /* Delete an element from the fileinfo linked list. Returns the
1935 address of the next element, or NULL if the list is exhausted. It
1936 can modify the start of the list. */
1937 static struct fileinfo *
1938 delelement (struct fileinfo *f, struct fileinfo **start)
1940 struct fileinfo *prev = f->prev;
1941 struct fileinfo *next = f->next;
1944 FREE_MAYBE (f->linkto);
1956 /* Free the fileinfo linked list of files. */
1958 freefileinfo (struct fileinfo *f)
1962 struct fileinfo *next = f->next;