]> sjero.net Git - wget/blobdiff - src/ftp-ls.c
Dependency improvements (but still needs some work).
[wget] / src / ftp-ls.c
index 19ce6a8b33cf6dd759a6c7b2af030e3e656d60a1..409996c350d2ad2f9e25e030e03b16558b4b9be9 100644 (file)
@@ -1,12 +1,12 @@
 /* Parsing FTP `ls' output.
 /* Parsing FTP `ls' output.
-   Copyright (C) 1995, 1996, 1997, 2000, 2001
-   Free Software Foundation, Inc. 
+   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+   2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. 
 
 This file is part of GNU Wget.
 
 GNU Wget is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 
 This file is part of GNU Wget.
 
 GNU Wget is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
+the Free Software Foundation; either version 3 of the License, or
 (at your option) any later version.
 
 GNU Wget is distributed in the hope that it will be useful,
 (at your option) any later version.
 
 GNU Wget is distributed in the hope that it will be useful,
@@ -15,28 +15,34 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with Wget; if not, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+along with Wget.  If not, see <http://www.gnu.org/licenses/>.
 
 
-#include <config.h>
+Additional permission under GNU GPL version 3 section 7
+
+If you modify this program, or any covered work, by linking or
+combining it with the OpenSSL project's OpenSSL library (or a
+modified version of that library), containing parts covered by the
+terms of the OpenSSL or SSLeay licenses, the Free Software Foundation
+grants you additional permission to convey the resulting work.
+Corresponding Source for a non-source form of such a combination
+shall include the source code for the parts of OpenSSL used as well
+as that of the covered work.  */
+
+#include "wget.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 
 #include <stdio.h>
 #include <stdlib.h>
-#ifdef HAVE_STRING_H
-# include <string.h>
-#else
-# include <strings.h>
-#endif
+#include <string.h>
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
 #endif
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
 #endif
-#include <sys/types.h>
 #include <errno.h>
 #include <errno.h>
-
-#include "wget.h"
+#include <time.h>
 #include "utils.h"
 #include "ftp.h"
 #include "url.h"
 #include "utils.h"
 #include "ftp.h"
 #include "url.h"
+#include "convert.h"            /* for html_quote_string prototype */
+#include "retr.h"               /* for output_stream */
 
 /* Converts symbolic permissions to number-style ones, e.g. string
    rwxr-xr-x to 755.  For now, it knows nothing of
 
 /* Converts symbolic permissions to number-style ones, e.g. string
    rwxr-xr-x to 755.  For now, it knows nothing of
@@ -52,7 +58,7 @@ symperms (const char *s)
     {
       perms <<= 3;
       perms += (((s[0] == 'r') << 2) + ((s[1] == 'w') << 1) +
     {
       perms <<= 3;
       perms += (((s[0] == 'r') << 2) + ((s[1] == 'w') << 1) +
-               (s[2] == 'x' || s[2] == 's'));
+                (s[2] == 'x' || s[2] == 's'));
     }
   return perms;
 }
     }
   return perms;
 }
@@ -92,12 +98,12 @@ ftp_parse_unix_ls (const char *file, int ignore_perms)
     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
   };
   int next, len, i, error, ignore;
     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
   };
   int next, len, i, error, ignore;
-  int year, month, day;                /* for time analysis */
+  int year, month, day;         /* for time analysis */
   int hour, min, sec;
   struct tm timestruct, *tnow;
   time_t timenow;
 
   int hour, min, sec;
   struct tm timestruct, *tnow;
   time_t timenow;
 
-  char *line, *tok;            /* tokenizer */
+  char *line, *tok, *ptok;      /* tokenizer */
   struct fileinfo *dir, *l, cur; /* list creation */
 
   fp = fopen (file, "rb");
   struct fileinfo *dir, *l, cur; /* list creation */
 
   fp = fopen (file, "rb");
@@ -109,265 +115,275 @@ ftp_parse_unix_ls (const char *file, int ignore_perms)
   dir = l = NULL;
 
   /* Line loop to end of file: */
   dir = l = NULL;
 
   /* Line loop to end of file: */
-  while ((line = read_whole_line (fp)))
+  while ((line = read_whole_line (fp)) != NULL)
     {
       len = clean_line (line);
       /* Skip if total...  */
       if (!strncasecmp (line, "total", 5))
     {
       len = clean_line (line);
       /* Skip if total...  */
       if (!strncasecmp (line, "total", 5))
-       {
-         xfree (line);
-         continue;
-       }
+        {
+          xfree (line);
+          continue;
+        }
       /* Get the first token (permissions).  */
       tok = strtok (line, " ");
       if (!tok)
       /* Get the first token (permissions).  */
       tok = strtok (line, " ");
       if (!tok)
-       {
-         xfree (line);
-         continue;
-       }
+        {
+          xfree (line);
+          continue;
+        }
 
       cur.name = NULL;
       cur.linkto = NULL;
 
       /* Decide whether we deal with a file or a directory.  */
       switch (*tok)
 
       cur.name = NULL;
       cur.linkto = NULL;
 
       /* Decide whether we deal with a file or a directory.  */
       switch (*tok)
-       {
-       case '-':
-         cur.type = FT_PLAINFILE;
-         DEBUGP (("PLAINFILE; "));
-         break;
-       case 'd':
-         cur.type = FT_DIRECTORY;
-         DEBUGP (("DIRECTORY; "));
-         break;
-       case 'l':
-         cur.type = FT_SYMLINK;
-         DEBUGP (("SYMLINK; "));
-         break;
-       default:
-         cur.type = FT_UNKNOWN;
-         DEBUGP (("UNKNOWN; "));
-         break;
-       }
+        {
+        case '-':
+          cur.type = FT_PLAINFILE;
+          DEBUGP (("PLAINFILE; "));
+          break;
+        case 'd':
+          cur.type = FT_DIRECTORY;
+          DEBUGP (("DIRECTORY; "));
+          break;
+        case 'l':
+          cur.type = FT_SYMLINK;
+          DEBUGP (("SYMLINK; "));
+          break;
+        default:
+          cur.type = FT_UNKNOWN;
+          DEBUGP (("UNKNOWN; "));
+          break;
+        }
 
       if (ignore_perms)
 
       if (ignore_perms)
-       {
-         switch (cur.type)
-           {
-           case FT_PLAINFILE:
-             cur.perms = 0644;
-             break;
-           case FT_DIRECTORY:
-             cur.perms = 0755;
-             break;
-           default:
-             /*cur.perms = 1023;*/     /* #### What is this?  --hniksic */
-             cur.perms = 0644;
-           }
-         DEBUGP (("implicit perms %0o; ", cur.perms));
-       }
+        {
+          switch (cur.type)
+            {
+            case FT_PLAINFILE:
+              cur.perms = 0644;
+              break;
+            case FT_DIRECTORY:
+              cur.perms = 0755;
+              break;
+            default:
+              /*cur.perms = 1023;*/     /* #### What is this?  --hniksic */
+              cur.perms = 0644;
+            }
+          DEBUGP (("implicit perms %0o; ", cur.perms));
+        }
        else
          {
        else
          {
-          cur.perms = symperms (tok + 1);
-          DEBUGP (("perms %0o; ", cur.perms));
-        }
+           cur.perms = symperms (tok + 1);
+           DEBUGP (("perms %0o; ", cur.perms));
+         }
 
       error = ignore = 0;       /* Erroneous and ignoring entries are
 
       error = ignore = 0;       /* Erroneous and ignoring entries are
-                                  treated equally for now.  */
+                                   treated equally for now.  */
       year = hour = min = sec = 0; /* Silence the compiler.  */
       month = day = 0;
       next = -1;
       /* While there are tokens on the line, parse them.  Next is the
       year = hour = min = sec = 0; /* Silence the compiler.  */
       month = day = 0;
       next = -1;
       /* While there are tokens on the line, parse them.  Next is the
-        number of tokens left until the filename.
-
-        Use the month-name token as the "anchor" (the place where the
-        position wrt the file name is "known").  When a month name is
-        encountered, `next' is set to 5.  Also, the preceding
-        characters are parsed to get the file size.
-
-        This tactic is quite dubious when it comes to
-        internationalization issues (non-English month names), but it
-        works for now.  */
-      while ((tok = strtok (NULL, " ")))
-       {
-         --next;
-         if (next < 0)         /* a month name was not encountered */
-           {
-             for (i = 0; i < 12; i++)
-               if (!strcmp (tok, months[i]))
-                 break;
-             /* If we got a month, it means the token before it is the
-                size, and the filename is three tokens away.  */
-             if (i != 12)
-               {
-                 char *t = tok - 2;
-                 long mul = 1;
-
-                 for (cur.size = 0; t > line && ISDIGIT (*t); mul *= 10, t--)
-                   cur.size += mul * (*t - '0');
-                 if (t == line)
-                   {
-                     /* Something is seriously wrong.  */
-                     error = 1;
-                     break;
-                   }
-                 month = i;
-                 next = 5;
-                 DEBUGP (("month: %s; ", months[month]));
-               }
-           }
-         else if (next == 4)   /* days */
-           {
-             if (tok[1])       /* two-digit... */
-               day = 10 * (*tok - '0') + tok[1] - '0';
-             else              /* ...or one-digit */
-               day = *tok - '0';
-             DEBUGP (("day: %d; ", day));
-           }
-         else if (next == 3)
-           {
-             /* This ought to be either the time, or the year.  Let's
-                be flexible!
-
-                If we have a number x, it's a year.  If we have x:y,
-                it's hours and minutes.  If we have x:y:z, z are
-                seconds.  */
-             year = 0;
-             min = hour = sec = 0;
-             /* We must deal with digits.  */
-             if (ISDIGIT (*tok))
-               {
-                 /* Suppose it's year.  */
-                 for (; ISDIGIT (*tok); tok++)
-                   year = (*tok - '0') + 10 * year;
-                 if (*tok == ':')
-                   {
-                     /* This means these were hours!  */
-                     hour = year;
-                     year = 0;
-                     ++tok;
-                     /* Get the minutes...  */
-                     for (; ISDIGIT (*tok); tok++)
-                       min = (*tok - '0') + 10 * min;
-                     if (*tok == ':')
-                       {
-                         /* ...and the seconds.  */
-                         ++tok;
-                         for (; ISDIGIT (*tok); tok++)
-                           sec = (*tok - '0') + 10 * sec;
-                       }
-                   }
-               }
-             if (year)
-               DEBUGP (("year: %d (no tm); ", year));
-             else
-               DEBUGP (("time: %02d:%02d:%02d (no yr); ", hour, min, sec));
-           }
-         else if (next == 2)    /* The file name */
-           {
-             int fnlen;
-             char *p;
-
-             /* Since the file name may contain a SPC, it is possible
-                for strtok to handle it wrong.  */
-             fnlen = strlen (tok);
-             if (fnlen < len - (tok - line))
-               {
-                 /* So we have a SPC in the file name.  Restore the
-                    original.  */
-                 tok[fnlen] = ' ';
-                 /* If the file is a symbolic link, it should have a
-                    ` -> ' somewhere.  */
-                 if (cur.type == FT_SYMLINK)
-                   {
-                     p = strstr (tok, " -> ");
-                     if (!p)
-                       {
-                         error = 1;
-                         break;
-                       }
-                     cur.linkto = xstrdup (p + 4);
-                     DEBUGP (("link to: %s\n", cur.linkto));
-                     /* And separate it from the file name.  */
-                     *p = '\0';
-                   }
-               }
-             /* If we have the filename, add it to the list of files or
-                directories.  */
-             /* "." and ".." are an exception!  */
-             if (!strcmp (tok, ".") || !strcmp (tok, ".."))
-               {
-                 DEBUGP (("\nIgnoring `.' and `..'; "));
-                 ignore = 1;
-                 break;
-               }
-             /* Some FTP sites choose to have ls -F as their default
-                LIST output, which marks the symlinks with a trailing
-                `@', directory names with a trailing `/' and
-                executables with a trailing `*'.  This is no problem
-                unless encountering a symbolic link ending with `@',
-                or an executable ending with `*' on a server without
-                default -F output.  I believe these cases are very
-                rare.  */
-             fnlen = strlen (tok); /* re-calculate `fnlen' */
-             cur.name = (char *)xmalloc (fnlen + 1);
-             memcpy (cur.name, tok, fnlen + 1);
-             if (fnlen)
-               {
-                 if (cur.type == FT_DIRECTORY && cur.name[fnlen - 1] == '/')
-                   {
-                     cur.name[fnlen - 1] = '\0';
-                     DEBUGP (("trailing `/' on dir.\n"));
-                   }
-                 else if (cur.type == FT_SYMLINK && cur.name[fnlen - 1] == '@')
-                   {
-                     cur.name[fnlen - 1] = '\0';
-                     DEBUGP (("trailing `@' on link.\n"));
-                   }
-                 else if (cur.type == FT_PLAINFILE
-                          && (cur.perms & 0111)
-                          && cur.name[fnlen - 1] == '*')
-                   {
-                     cur.name[fnlen - 1] = '\0';
-                     DEBUGP (("trailing `*' on exec.\n"));
-                   }
-               } /* if (fnlen) */
-             else
-               error = 1;
-             break;
-           }
-         else
-           abort ();
-       } /* while */
+         number of tokens left until the filename.
+
+         Use the month-name token as the "anchor" (the place where the
+         position wrt the file name is "known").  When a month name is
+         encountered, `next' is set to 5.  Also, the preceding
+         characters are parsed to get the file size.
+
+         This tactic is quite dubious when it comes to
+         internationalization issues (non-English month names), but it
+         works for now.  */
+      tok = line;
+      while (ptok = tok,
+             (tok = strtok (NULL, " ")) != NULL)
+        {
+          --next;
+          if (next < 0)         /* a month name was not encountered */
+            {
+              for (i = 0; i < 12; i++)
+                if (!strcmp (tok, months[i]))
+                  break;
+              /* If we got a month, it means the token before it is the
+                 size, and the filename is three tokens away.  */
+              if (i != 12)
+                {
+                  wgint size;
+
+                  /* Parse the previous token with str_to_wgint.  */
+                  if (ptok == line)
+                    {
+                      /* Something has gone wrong during parsing. */
+                      error = 1;
+                      break;
+                    }
+                  errno = 0;
+                  size = str_to_wgint (ptok, NULL, 10);
+                  if (size == WGINT_MAX && errno == ERANGE)
+                    /* Out of range -- ignore the size.  #### Should
+                       we refuse to start the download.  */
+                    cur.size = 0;
+                  else
+                    cur.size = size;
+                  DEBUGP (("size: %s; ", number_to_static_string(cur.size)));
+
+                  month = i;
+                  next = 5;
+                  DEBUGP (("month: %s; ", months[month]));
+                }
+            }
+          else if (next == 4)   /* days */
+            {
+              if (tok[1])       /* two-digit... */
+                day = 10 * (*tok - '0') + tok[1] - '0';
+              else              /* ...or one-digit */
+                day = *tok - '0';
+              DEBUGP (("day: %d; ", day));
+            }
+          else if (next == 3)
+            {
+              /* This ought to be either the time, or the year.  Let's
+                 be flexible!
+
+                 If we have a number x, it's a year.  If we have x:y,
+                 it's hours and minutes.  If we have x:y:z, z are
+                 seconds.  */
+              year = 0;
+              min = hour = sec = 0;
+              /* We must deal with digits.  */
+              if (c_isdigit (*tok))
+                {
+                  /* Suppose it's year.  */
+                  for (; c_isdigit (*tok); tok++)
+                    year = (*tok - '0') + 10 * year;
+                  if (*tok == ':')
+                    {
+                      /* This means these were hours!  */
+                      hour = year;
+                      year = 0;
+                      ++tok;
+                      /* Get the minutes...  */
+                      for (; c_isdigit (*tok); tok++)
+                        min = (*tok - '0') + 10 * min;
+                      if (*tok == ':')
+                        {
+                          /* ...and the seconds.  */
+                          ++tok;
+                          for (; c_isdigit (*tok); tok++)
+                            sec = (*tok - '0') + 10 * sec;
+                        }
+                    }
+                }
+              if (year)
+                DEBUGP (("year: %d (no tm); ", year));
+              else
+                DEBUGP (("time: %02d:%02d:%02d (no yr); ", hour, min, sec));
+            }
+          else if (next == 2)    /* The file name */
+            {
+              int fnlen;
+              char *p;
+
+              /* Since the file name may contain a SPC, it is possible
+                 for strtok to handle it wrong.  */
+              fnlen = strlen (tok);
+              if (fnlen < len - (tok - line))
+                {
+                  /* So we have a SPC in the file name.  Restore the
+                     original.  */
+                  tok[fnlen] = ' ';
+                  /* If the file is a symbolic link, it should have a
+                     ` -> ' somewhere.  */
+                  if (cur.type == FT_SYMLINK)
+                    {
+                      p = strstr (tok, " -> ");
+                      if (!p)
+                        {
+                          error = 1;
+                          break;
+                        }
+                      cur.linkto = xstrdup (p + 4);
+                      DEBUGP (("link to: %s\n", cur.linkto));
+                      /* And separate it from the file name.  */
+                      *p = '\0';
+                    }
+                }
+              /* If we have the filename, add it to the list of files or
+                 directories.  */
+              /* "." and ".." are an exception!  */
+              if (!strcmp (tok, ".") || !strcmp (tok, ".."))
+                {
+                  DEBUGP (("\nIgnoring `.' and `..'; "));
+                  ignore = 1;
+                  break;
+                }
+              /* Some FTP sites choose to have ls -F as their default
+                 LIST output, which marks the symlinks with a trailing
+                 `@', directory names with a trailing `/' and
+                 executables with a trailing `*'.  This is no problem
+                 unless encountering a symbolic link ending with `@',
+                 or an executable ending with `*' on a server without
+                 default -F output.  I believe these cases are very
+                 rare.  */
+              fnlen = strlen (tok); /* re-calculate `fnlen' */
+              cur.name = xmalloc (fnlen + 1);
+              memcpy (cur.name, tok, fnlen + 1);
+              if (fnlen)
+                {
+                  if (cur.type == FT_DIRECTORY && cur.name[fnlen - 1] == '/')
+                    {
+                      cur.name[fnlen - 1] = '\0';
+                      DEBUGP (("trailing `/' on dir.\n"));
+                    }
+                  else if (cur.type == FT_SYMLINK && cur.name[fnlen - 1] == '@')
+                    {
+                      cur.name[fnlen - 1] = '\0';
+                      DEBUGP (("trailing `@' on link.\n"));
+                    }
+                  else if (cur.type == FT_PLAINFILE
+                           && (cur.perms & 0111)
+                           && cur.name[fnlen - 1] == '*')
+                    {
+                      cur.name[fnlen - 1] = '\0';
+                      DEBUGP (("trailing `*' on exec.\n"));
+                    }
+                } /* if (fnlen) */
+              else
+                error = 1;
+              break;
+            }
+          else
+            abort ();
+        } /* while */
 
       if (!cur.name || (cur.type == FT_SYMLINK && !cur.linkto))
 
       if (!cur.name || (cur.type == FT_SYMLINK && !cur.linkto))
-       error = 1;
+        error = 1;
 
 
-      DEBUGP (("\n"));
+      DEBUGP (("%s\n", cur.name ? cur.name : ""));
 
       if (error || ignore)
 
       if (error || ignore)
-       {
-         DEBUGP (("Skipping.\n"));
-         FREE_MAYBE (cur.name);
-         FREE_MAYBE (cur.linkto);
-         xfree (line);
-         continue;
-       }
+        {
+          DEBUGP (("Skipping.\n"));
+          xfree_null (cur.name);
+          xfree_null (cur.linkto);
+          xfree (line);
+          continue;
+        }
 
       if (!dir)
 
       if (!dir)
-       {
-         l = dir = (struct fileinfo *)xmalloc (sizeof (struct fileinfo));
-         memcpy (l, &cur, sizeof (cur));
-         l->prev = l->next = NULL;
-       }
+        {
+          l = dir = xnew (struct fileinfo);
+          memcpy (l, &cur, sizeof (cur));
+          l->prev = l->next = NULL;
+        }
       else
       else
-       {
-         cur.prev = l;
-         l->next = (struct fileinfo *)xmalloc (sizeof (struct fileinfo));
-         l = l->next;
-         memcpy (l, &cur, sizeof (cur));
-         l->next = NULL;
-       }
+        {
+          cur.prev = l;
+          l->next = xnew (struct fileinfo);
+          l = l->next;
+          memcpy (l, &cur, sizeof (cur));
+          l->next = NULL;
+        }
       /* Get the current time.  */
       timenow = time (NULL);
       tnow = localtime (&timenow);
       /* Get the current time.  */
       timenow = time (NULL);
       tnow = localtime (&timenow);
@@ -378,21 +394,21 @@ ftp_parse_unix_ls (const char *file, int ignore_perms)
       timestruct.tm_mday  = day;
       timestruct.tm_mon   = month;
       if (year == 0)
       timestruct.tm_mday  = day;
       timestruct.tm_mon   = month;
       if (year == 0)
-       {
-         /* Some listings will not specify the year if it is "obvious"
-            that the file was from the previous year.  E.g. if today
-            is 97-01-12, and you see a file of Dec 15th, its year is
-            1996, not 1997.  Thanks to Vladimir Volovich for
-            mentioning this!  */
-         if (month > tnow->tm_mon)
-           timestruct.tm_year = tnow->tm_year - 1;
-         else
-           timestruct.tm_year = tnow->tm_year;
-       }
+        {
+          /* Some listings will not specify the year if it is "obvious"
+             that the file was from the previous year.  E.g. if today
+             is 97-01-12, and you see a file of Dec 15th, its year is
+             1996, not 1997.  Thanks to Vladimir Volovich for
+             mentioning this!  */
+          if (month > tnow->tm_mon)
+            timestruct.tm_year = tnow->tm_year - 1;
+          else
+            timestruct.tm_year = tnow->tm_year;
+        }
       else
       else
-       timestruct.tm_year = year;
+        timestruct.tm_year = year;
       if (timestruct.tm_year >= 1900)
       if (timestruct.tm_year >= 1900)
-       timestruct.tm_year -= 1900;
+        timestruct.tm_year -= 1900;
       timestruct.tm_wday  = 0;
       timestruct.tm_yday  = 0;
       timestruct.tm_isdst = -1;
       timestruct.tm_wday  = 0;
       timestruct.tm_yday  = 0;
       timestruct.tm_isdst = -1;
@@ -426,7 +442,7 @@ ftp_parse_winnt_ls (const char *file)
   dir = l = NULL;
 
   /* Line loop to end of file: */
   dir = l = NULL;
 
   /* Line loop to end of file: */
-  while ((line = read_whole_line (fp)))
+  while ((line = read_whole_line (fp)) != NULL)
     {
       len = clean_line (line);
 
     {
       len = clean_line (line);
 
@@ -442,13 +458,16 @@ ftp_parse_winnt_ls (const char *file)
       DEBUGP(("Name: '%s'\n", cur.name));
 
       /* First column: mm-dd-yy. Should atoi() on the month fail, january
       DEBUGP(("Name: '%s'\n", cur.name));
 
       /* First column: mm-dd-yy. Should atoi() on the month fail, january
-        will be assumed.  */
+         will be assumed.  */
       tok = strtok(line, "-");
       tok = strtok(line, "-");
+      if (tok == NULL) continue;
       month = atoi(tok) - 1;
       if (month < 0) month = 0;
       tok = strtok(NULL, "-");
       month = atoi(tok) - 1;
       if (month < 0) month = 0;
       tok = strtok(NULL, "-");
+      if (tok == NULL) continue;
       day = atoi(tok);
       tok = strtok(NULL, " ");
       day = atoi(tok);
       tok = strtok(NULL, " ");
+      if (tok == NULL) continue;
       year = atoi(tok);
       /* Assuming the epoch starting at 1.1.1970 */
       if (year <= 70) year += 100;
       year = atoi(tok);
       /* Assuming the epoch starting at 1.1.1970 */
       if (year <= 70) year += 100;
@@ -456,8 +475,10 @@ ftp_parse_winnt_ls (const char *file)
       /* Second column: hh:mm[AP]M, listing does not contain value for
          seconds */
       tok = strtok(NULL,  ":");
       /* Second column: hh:mm[AP]M, listing does not contain value for
          seconds */
       tok = strtok(NULL,  ":");
+      if (tok == NULL) continue;
       hour = atoi(tok);
       tok = strtok(NULL,  "M");
       hour = atoi(tok);
       tok = strtok(NULL,  "M");
+      if (tok == NULL) continue;
       min = atoi(tok);
       /* Adjust hour from AM/PM. Just for the record, the sequence goes
          11:00AM, 12:00PM, 01:00PM ... 11:00PM, 12:00AM, 01:00AM . */
       min = atoi(tok);
       /* Adjust hour from AM/PM. Just for the record, the sequence goes
          11:00AM, 12:00PM, 01:00PM ... 11:00PM, 12:00AM, 01:00AM . */
@@ -487,41 +508,49 @@ ftp_parse_winnt_ls (const char *file)
          directories as the listing does not give us a clue) and filetype
          here. */
       tok = strtok(NULL, " ");
          directories as the listing does not give us a clue) and filetype
          here. */
       tok = strtok(NULL, " ");
-      while (*tok == '\0')  tok = strtok(NULL, " ");
+      if (tok == NULL) continue;
+      while ((tok != NULL) && (*tok == '\0'))  tok = strtok(NULL, " ");
+      if (tok == NULL) continue;
       if (*tok == '<')
       if (*tok == '<')
-       {
-         cur.type  = FT_DIRECTORY;
-         cur.size  = 0;
-         cur.perms = 0755;
-         DEBUGP(("Directory\n"));
-       }
+        {
+          cur.type  = FT_DIRECTORY;
+          cur.size  = 0;
+          cur.perms = 0755;
+          DEBUGP(("Directory\n"));
+        }
       else
       else
-       {
-         cur.type  = FT_PLAINFILE;
-         cur.size  = atoi(tok);
-         cur.perms = 0644;
-         DEBUGP(("File, size %ld bytes\n", cur.size));
-       }
+        {
+          wgint size;
+          cur.type  = FT_PLAINFILE;
+          errno = 0;
+          size = str_to_wgint (tok, NULL, 10);
+          if (size == WGINT_MAX && errno == ERANGE)
+            cur.size = 0;       /* overflow */
+          else
+            cur.size = size;
+          cur.perms = 0644;
+          DEBUGP(("File, size %s bytes\n", number_to_static_string (cur.size)));
+        }
 
       cur.linkto = NULL;
 
       /* And put everything into the linked list */
       if (!dir)
 
       cur.linkto = NULL;
 
       /* And put everything into the linked list */
       if (!dir)
-       {
-         l = dir = (struct fileinfo *)xmalloc (sizeof (struct fileinfo));
-         memcpy (l, &cur, sizeof (cur));
-         l->prev = l->next = NULL;
-       }
+        {
+          l = dir = xnew (struct fileinfo);
+          memcpy (l, &cur, sizeof (cur));
+          l->prev = l->next = NULL;
+        }
       else
       else
-       {
-         cur.prev = l;
-         l->next = (struct fileinfo *)xmalloc (sizeof (struct fileinfo));
-         l = l->next;
-         memcpy (l, &cur, sizeof (cur));
-         l->next = NULL;
-       }
-
-      xfree(line);
+        {
+          cur.prev = l;
+          l->next = xnew (struct fileinfo);
+          l = l->next;
+          memcpy (l, &cur, sizeof (cur));
+          l->next = NULL;
+        }
+
+      xfree (line);
     }
 
   fclose(fp);
     }
 
   fclose(fp);
@@ -566,7 +595,7 @@ ftp_parse_vms_ls (const char *file)
   int hour, min, sec;
   struct tm timestruct;
 
   int hour, min, sec;
   struct tm timestruct;
 
-  char *line, *tok;             /* tokenizer */
+  char *line, *tok;              /* tokenizer */
   struct fileinfo *dir, *l, cur; /* list creation */
 
   fp = fopen (file, "rb");
   struct fileinfo *dir, *l, cur; /* list creation */
 
   fp = fopen (file, "rb");
@@ -577,19 +606,28 @@ ftp_parse_vms_ls (const char *file)
     }
   dir = l = NULL;
 
     }
   dir = l = NULL;
 
-  /* Empty line */
-  read_whole_line (fp);
-  /* "Directory PUB$DEVICE[PUB]" */
-  read_whole_line (fp);
-  /* Empty line */
-  read_whole_line (fp);
+  /* Skip empty line. */
+  line = read_whole_line (fp);
+  xfree_null (line);
+
+  /* Skip "Directory PUB$DEVICE[PUB]" */
+  line = read_whole_line (fp);
+  xfree_null (line);
+
+  /* Skip empty line. */
+  line = read_whole_line (fp);
+  xfree_null (line);
 
   /* Line loop to end of file: */
 
   /* Line loop to end of file: */
-  while ((line = read_whole_line (fp)))
+  while ((line = read_whole_line (fp)) != NULL)
     {
       char *p;
       i = clean_line (line);
     {
       char *p;
       i = clean_line (line);
-      if (!i) break;
+      if (!i)
+        {
+          xfree (line);
+          break;
+        }
 
       /* First column: Name. A bit of black magic again. The name my be
          either ABCD.EXT or ABCD.EXT;NUM and it might be on a separate
 
       /* First column: Name. A bit of black magic again. The name my be
          either ABCD.EXT or ABCD.EXT;NUM and it might be on a separate
@@ -602,7 +640,8 @@ ftp_parse_vms_ls (const char *file)
       tok = strtok(line, " ");
       if (tok == NULL) tok = line;
       DEBUGP(("file name: '%s'\n", tok));
       tok = strtok(line, " ");
       if (tok == NULL) tok = line;
       DEBUGP(("file name: '%s'\n", tok));
-      for (p = tok ; *p && *p != ';' ; p++);
+      for (p = tok ; *p && *p != ';' ; p++)
+        ;
       if (*p == ';') *p = '\0';
       p   = tok + strlen(tok) - 4;
       if (!strcmp(p, ".DIR")) *p = '\0';
       if (*p == ';') *p = '\0';
       p   = tok + strlen(tok) - 4;
       if (!strcmp(p, ".DIR")) *p = '\0';
@@ -646,6 +685,7 @@ ftp_parse_vms_ls (const char *file)
         if (!i) 
         {
           DEBUGP(("confusing VMS listing item, leaving listing parser\n"));
         if (!i) 
         {
           DEBUGP(("confusing VMS listing item, leaving listing parser\n"));
+          xfree (line);
           break;
         }
         tok = strtok(line, " ");
           break;
         }
         tok = strtok(line, " ");
@@ -655,6 +695,7 @@ ftp_parse_vms_ls (const char *file)
       /* Third/Second column: Date DD-MMM-YYYY. */
 
       tok = strtok(NULL, "-");
       /* Third/Second column: Date DD-MMM-YYYY. */
 
       tok = strtok(NULL, "-");
+      if (tok == NULL) continue;
       DEBUGP(("day: '%s'\n",tok));
       day = atoi(tok);
       tok = strtok(NULL, "-");
       DEBUGP(("day: '%s'\n",tok));
       day = atoi(tok);
       tok = strtok(NULL, "-");
@@ -665,26 +706,31 @@ ftp_parse_vms_ls (const char *file)
            the first strtok(NULL, "-") will return everything until the end
            of the line and only the next strtok() call will return NULL. */
         DEBUGP(("nonsense in VMS listing, skipping this line\n"));
            the first strtok(NULL, "-") will return everything until the end
            of the line and only the next strtok() call will return NULL. */
         DEBUGP(("nonsense in VMS listing, skipping this line\n"));
+        xfree (line);
         break;
       }
       for (i=0; i<12; i++) if (!strcmp(tok,months[i])) break;
       /* Uknown months are mapped to January */
       month = i % 12 ; 
       tok = strtok (NULL, " ");
         break;
       }
       for (i=0; i<12; i++) if (!strcmp(tok,months[i])) break;
       /* Uknown months are mapped to January */
       month = i % 12 ; 
       tok = strtok (NULL, " ");
+      if (tok == NULL) continue;
       year = atoi (tok) - 1900;
       DEBUGP(("date parsed\n"));
 
       /* Fourth/Third column: Time hh:mm[:ss] */
       tok = strtok (NULL, " ");
       year = atoi (tok) - 1900;
       DEBUGP(("date parsed\n"));
 
       /* Fourth/Third column: Time hh:mm[:ss] */
       tok = strtok (NULL, " ");
-      hour = min = sec = 0;
+      if (tok == NULL) continue;
+      min = sec = 0;
       p = tok;
       hour = atoi (p);
       p = tok;
       hour = atoi (p);
-      for (; *p && *p != ':'; ++p);
+      for (; *p && *p != ':'; ++p)
+        ;
       if (*p)
       if (*p)
-       min = atoi (++p);
-      for (; *p && *p != ':'; ++p);
+        min = atoi (++p);
+      for (; *p && *p != ':'; ++p)
+        ;
       if (*p)
       if (*p)
-       sec = atoi (++p);
+        sec = atoi (++p);
 
       DEBUGP(("YYYY/MM/DD HH:MM:SS - %d/%02d/%02d %02d:%02d:%02d\n", 
               year+1900, month, day, hour, min, sec));
 
       DEBUGP(("YYYY/MM/DD HH:MM:SS - %d/%02d/%02d %02d:%02d:%02d\n", 
               year+1900, month, day, hour, min, sec));
@@ -706,14 +752,17 @@ ftp_parse_vms_ls (const char *file)
       /* Skip the fifth column */
 
       tok = strtok(NULL, " ");
       /* Skip the fifth column */
 
       tok = strtok(NULL, " ");
+      if (tok == NULL) continue;
 
       /* Sixth column: Permissions */
 
       tok = strtok(NULL, ","); /* Skip the VMS-specific SYSTEM permissons */
 
       /* Sixth column: Permissions */
 
       tok = strtok(NULL, ","); /* Skip the VMS-specific SYSTEM permissons */
+      if (tok == NULL) continue;
       tok = strtok(NULL, ")");
       if (tok == NULL)
         {
           DEBUGP(("confusing VMS permissions, skipping line\n"));
       tok = strtok(NULL, ")");
       if (tok == NULL)
         {
           DEBUGP(("confusing VMS permissions, skipping line\n"));
+          xfree (line);
           continue;
         }
       /* Permissons have the format "RWED,RWED,RE" */
           continue;
         }
       /* Permissons have the format "RWED,RWED,RE" */
@@ -725,14 +774,14 @@ ftp_parse_vms_ls (const char *file)
       /* And put everything into the linked list */
       if (!dir)
         {
       /* And put everything into the linked list */
       if (!dir)
         {
-          l = dir = (struct fileinfo *)xmalloc (sizeof (struct fileinfo));
+          l = dir = xnew (struct fileinfo);
           memcpy (l, &cur, sizeof (cur));
           l->prev = l->next = NULL;
         }
       else
         {
           cur.prev = l;
           memcpy (l, &cur, sizeof (cur));
           l->prev = l->next = NULL;
         }
       else
         {
           cur.prev = l;
-          l->next = (struct fileinfo *)xmalloc (sizeof (struct fileinfo));
+          l->next = xnew (struct fileinfo);
           l = l->next;
           memcpy (l, &cur, sizeof (cur));
           l->next = NULL;
           l = l->next;
           memcpy (l, &cur, sizeof (cur));
           l->next = NULL;
@@ -758,35 +807,35 @@ ftp_parse_ls (const char *file, const enum stype system_type)
   switch (system_type)
     {
     case ST_UNIX:
   switch (system_type)
     {
     case ST_UNIX:
-      return ftp_parse_unix_ls (file, FALSE);
+      return ftp_parse_unix_ls (file, 0);
     case ST_WINNT:
       {
     case ST_WINNT:
       {
-       /* Detect whether the listing is simulating the UNIX format */
-       FILE *fp;
-       int   c;
-       fp = fopen (file, "rb");
-       if (!fp)
-       {
-         logprintf (LOG_NOTQUIET, "%s: %s\n", file, strerror (errno));
-         return NULL;
+        /* Detect whether the listing is simulating the UNIX format */
+        FILE *fp;
+        int   c;
+        fp = fopen (file, "rb");
+        if (!fp)
+        {
+          logprintf (LOG_NOTQUIET, "%s: %s\n", file, strerror (errno));
+          return NULL;
         }
         }
-       c = fgetc(fp);
-       fclose(fp);
-       /* If the first character of the file is '0'-'9', it's WINNT
-          format. */
-       if (c >= '0' && c <='9')
-         return ftp_parse_winnt_ls (file);
+        c = fgetc(fp);
+        fclose(fp);
+        /* If the first character of the file is '0'-'9', it's WINNT
+           format. */
+        if (c >= '0' && c <='9')
+          return ftp_parse_winnt_ls (file);
         else
         else
-          return ftp_parse_unix_ls (file, TRUE);
+          return ftp_parse_unix_ls (file, 1);
       }
     case ST_VMS:
       return ftp_parse_vms_ls (file);
     case ST_MACOS:
       }
     case ST_VMS:
       return ftp_parse_vms_ls (file);
     case ST_MACOS:
-      return ftp_parse_unix_ls (file, TRUE);
+      return ftp_parse_unix_ls (file, 1);
     default:
       logprintf (LOG_NOTQUIET, _("\
     default:
       logprintf (LOG_NOTQUIET, _("\
-Usupported listing type, trying Unix listing parser.\n"));
-      return ftp_parse_unix_ls (file, FALSE);
+Unsupported listing type, trying Unix listing parser.\n"));
+      return ftp_parse_unix_ls (file, 0);
     }
 }
 \f
     }
 }
 \f
@@ -796,34 +845,35 @@ Usupported listing type, trying Unix listing parser.\n"));
    directories and files on the appropriate host.  The references are
    FTP.  */
 uerr_t
    directories and files on the appropriate host.  The references are
    FTP.  */
 uerr_t
-ftp_index (const char *file, struct urlinfo *u, struct fileinfo *f)
+ftp_index (const char *file, struct url *u, struct fileinfo *f)
 {
   FILE *fp;
   char *upwd;
 {
   FILE *fp;
   char *upwd;
-  char *htclfile;              /* HTML-clean file name */
+  char *htclfile;               /* HTML-clean file name */
 
 
-  if (!opt.dfp)
+  if (!output_stream)
     {
       fp = fopen (file, "wb");
       if (!fp)
     {
       fp = fopen (file, "wb");
       if (!fp)
-       {
-         logprintf (LOG_NOTQUIET, "%s: %s\n", file, strerror (errno));
-         return FOPENERR;
-       }
+        {
+          logprintf (LOG_NOTQUIET, "%s: %s\n", file, strerror (errno));
+          return FOPENERR;
+        }
     }
   else
     }
   else
-    fp = opt.dfp;
+    fp = output_stream;
   if (u->user)
     {
       char *tmpu, *tmpp;        /* temporary, clean user and passwd */
 
   if (u->user)
     {
       char *tmpu, *tmpp;        /* temporary, clean user and passwd */
 
-      tmpu = encode_string (u->user);
-      tmpp = u->passwd ? encode_string (u->passwd) : NULL;
-      upwd = (char *)xmalloc (strlen (tmpu)
-                            + (tmpp ? (1 + strlen (tmpp)) : 0) + 2);
-      sprintf (upwd, "%s%s%s@", tmpu, tmpp ? ":" : "", tmpp ? tmpp : "");
+      tmpu = url_escape (u->user);
+      tmpp = u->passwd ? url_escape (u->passwd) : NULL;
+      if (tmpp)
+        upwd = concat_strings (tmpu, ":", tmpp, "@", (char *) 0);
+      else
+        upwd = concat_strings (tmpu, "@", (char *) 0);
       xfree (tmpu);
       xfree (tmpu);
-      FREE_MAYBE (tmpp);
+      xfree_null (tmpp);
     }
   else
     upwd = xstrdup ("");
     }
   else
     upwd = xstrdup ("");
@@ -837,63 +887,65 @@ ftp_index (const char *file, struct urlinfo *u, struct fileinfo *f)
     {
       fprintf (fp, "  ");
       if (f->tstamp != -1)
     {
       fprintf (fp, "  ");
       if (f->tstamp != -1)
-       {
-         /* #### Should we translate the months? */
-         static char *months[] = {
-           "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-           "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-         };
-         struct tm *ptm = localtime ((time_t *)&f->tstamp);
-
-         fprintf (fp, "%d %s %02d ", ptm->tm_year + 1900, months[ptm->tm_mon],
-                 ptm->tm_mday);
-         if (ptm->tm_hour)
-           fprintf (fp, "%02d:%02d  ", ptm->tm_hour, ptm->tm_min);
-         else
-           fprintf (fp, "       ");
-       }
+        {
+          /* #### Should we translate the months?  Or, even better, use
+             ISO 8601 dates?  */
+          static const char *months[] = {
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+            "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+          };
+          time_t tstamp = f->tstamp;
+          struct tm *ptm = localtime (&tstamp);
+
+          fprintf (fp, "%d %s %02d ", ptm->tm_year + 1900, months[ptm->tm_mon],
+                  ptm->tm_mday);
+          if (ptm->tm_hour)
+            fprintf (fp, "%02d:%02d  ", ptm->tm_hour, ptm->tm_min);
+          else
+            fprintf (fp, "       ");
+        }
       else
       else
-       fprintf (fp, _("time unknown       "));
+        fprintf (fp, _("time unknown       "));
       switch (f->type)
       switch (f->type)
-       {
-       case FT_PLAINFILE:
-         fprintf (fp, _("File        "));
-         break;
-       case FT_DIRECTORY:
-         fprintf (fp, _("Directory   "));
-         break;
-       case FT_SYMLINK:
-         fprintf (fp, _("Link        "));
-         break;
-       default:
-         fprintf (fp, _("Not sure    "));
-         break;
-       }
+        {
+        case FT_PLAINFILE:
+          fprintf (fp, _("File        "));
+          break;
+        case FT_DIRECTORY:
+          fprintf (fp, _("Directory   "));
+          break;
+        case FT_SYMLINK:
+          fprintf (fp, _("Link        "));
+          break;
+        default:
+          fprintf (fp, _("Not sure    "));
+          break;
+        }
       htclfile = html_quote_string (f->name);
       htclfile = html_quote_string (f->name);
-      fprintf (fp, "<a href=\"ftp://%s%s:%hu", upwd, u->host, u->port);
+      fprintf (fp, "<a href=\"ftp://%s%s:%d", upwd, u->host, u->port);
       if (*u->dir != '/')
       if (*u->dir != '/')
-       putc ('/', fp);
+        putc ('/', fp);
       fprintf (fp, "%s", u->dir);
       if (*u->dir)
       fprintf (fp, "%s", u->dir);
       if (*u->dir)
-       putc ('/', fp);
+        putc ('/', fp);
       fprintf (fp, "%s", htclfile);
       if (f->type == FT_DIRECTORY)
       fprintf (fp, "%s", htclfile);
       if (f->type == FT_DIRECTORY)
-       putc ('/', fp);
+        putc ('/', fp);
       fprintf (fp, "\">%s", htclfile);
       if (f->type == FT_DIRECTORY)
       fprintf (fp, "\">%s", htclfile);
       if (f->type == FT_DIRECTORY)
-       putc ('/', fp);
+        putc ('/', fp);
       fprintf (fp, "</a> ");
       if (f->type == FT_PLAINFILE)
       fprintf (fp, "</a> ");
       if (f->type == FT_PLAINFILE)
-       fprintf (fp, _(" (%s bytes)"), legible (f->size));
+        fprintf (fp, _(" (%s bytes)"), number_to_static_string (f->size));
       else if (f->type == FT_SYMLINK)
       else if (f->type == FT_SYMLINK)
-       fprintf (fp, "-> %s", f->linkto ? f->linkto : "(nil)");
+        fprintf (fp, "-> %s", f->linkto ? f->linkto : "(nil)");
       putc ('\n', fp);
       xfree (htclfile);
       f = f->next;
     }
   fprintf (fp, "</pre>\n</body>\n</html>\n");
   xfree (upwd);
       putc ('\n', fp);
       xfree (htclfile);
       f = f->next;
     }
   fprintf (fp, "</pre>\n</body>\n</html>\n");
   xfree (upwd);
-  if (!opt.dfp)
+  if (!output_stream)
     fclose (fp);
   else
     fflush (fp);
     fclose (fp);
   else
     fflush (fp);