if present. */
- if (len && line[len - 1] == '\n')
- line[--len] = '\0';
- if (len && line[len - 1] == '\r')
- line[--len] = '\0';
-
- if (ftpparse(&fp, line, len))
- {
- cur.size = fp.size;
- cur.name = (char *)xmalloc (fp.namelen + 1);
- memcpy (cur.name, fp.name, fp.namelen);
- cur.name[fp.namelen] = '\0';
- DEBUGP (("%s\n", cur.name));
- /* No links on non-UNIX systems */
- cur.linkto = NULL;
- /* ftpparse won't tell us correct permisions. So lets just invent
- something. */
- if (fp.flagtrycwd)
+ char *p;
+
+ /* The first token is the file name. After a long name, other
+ data may be on the following line. A valid directory name ends
+ in ".DIR;1" (any case), although some VMS FTP servers may omit
+ the version number (";1").
+ */
+
+ tok = strtok(line, " ");
+ if (tok == NULL) tok = line;
+ DEBUGP(("file name: '%s'\n", tok));
+
+ /* Stripping the version number on a VMS system would be wrong.
+ It may be foolish on a non-VMS system, too, but that's someone
+ else's problem. (Define PRESERVE_VMS_VERSIONS for proper
+ operation on other operating systems.)
+
+ 2005-02-23 SMS.
+ ODS5 extended file names may contain escaped semi-colons, so
+ the version number is identified as right-side decimal digits
+ led by a non-escaped semi-colon. It may be absent.
+ */
+
+#if (!defined( __VMS) && !defined( PRESERVE_VMS_VERSIONS))
+ for (p = tok+ strlen( tok); (--p > tok) && c_isdigit( *p); );
+ if ((*p == ';') && (*(p- 1) != '^'))
+ {
+ *p = '\0';
+ }
+#endif /* (!defined( __VMS) && !defined( PRESERVE_VMS_VERSIONS)) */
+
+ /* 2005-02-23 SMS.
+ Eliminate "^" escape characters from ODS5 extended file name.
+ (A caret is invalid in an ODS2 name, so this is always safe.)
+ */
+ eat_carets( tok);
+ DEBUGP(("file name-^: '%s'\n", tok));
+
+ /* Differentiate between a directory and any other file. A VMS
+ listing may not include file protections (permissions). Set a
+ default permissions value (according to the file type), which
+ may be overwritten later. Store directory names without the
+ ".DIR;1" file type and version number, as the plain name is
+ what will work in a CWD command.
+ */
+ len = strlen( tok);
+ if (!strncasecmp( (tok+ (len- 4)), ".DIR", 4))
+ {
+ *(tok+ (len -= 4)) = '\0'; /* Discard ".DIR". */
+ cur.type = FT_DIRECTORY;
+ cur.perms = VMS_DEFAULT_PROT_DIR;
+ DEBUGP(("Directory (nv)\n"));
+ }
+ else if (!strncasecmp( (tok+ (len- 6)), ".DIR;1", 6))
+ {
+ *(tok+ (len -= 6)) = '\0'; /* Discard ".DIR;1". */
+ cur.type = FT_DIRECTORY;
+ cur.perms = VMS_DEFAULT_PROT_DIR;
+ DEBUGP(("Directory (v)\n"));
+ }
+ else
+ {
+ cur.type = FT_PLAINFILE;
+ cur.perms = VMS_DEFAULT_PROT_FILE;
+ DEBUGP(("File\n"));
+ }
+ cur.name = xstrdup(tok);
+ DEBUGP(("Name: '%s'\n", cur.name));
+
+ /* Null the date and time string. */
+ *date_str = '\0';
+
+ /* VMS lacks symbolic links. */
+ cur.linkto = NULL;
+
+ /* VMS reports file sizes in (512-byte) disk blocks, not bytes,
+ hence useless for an integrity check based on byte-count.
+ Set size to unknown.
+ */
+ cur.size = 0;
+
+ /* Get token 2, if any. A long name may force all other data onto
+ a second line. If needed, read the second line.
+ */
+
+ tok = strtok(NULL, " ");
+ if (tok == NULL)
+ {
+ DEBUGP(("Getting additional line.\n"));
+ xfree (line);
+ line = read_whole_line (fp);
+ if (!line)
+ {
+ DEBUGP(("EOF. Leaving listing parser.\n"));
+ break;
+ }
+
+ /* Second line must begin with " ". Otherwise, it's a first
+ line (and we may be confused).
+ */
+ if (i <= 0)
{
- cur.type = FT_DIRECTORY;
- cur.perms = 0755;
- }
- else
+ /* Blank line. End of significant file listing. */
+ DEBUGP(("Blank line. Leaving listing parser.\n"));
+ xfree (line); /* Free useless line storage. */
+ break;
+ }
+ else if (line[ 0] != ' ')
+ {
+ DEBUGP(("Non-blank in column 1. Must be a new file name?\n"));
+ continue;
+ }
+ else
+ {
+ tok = strtok (line, " ");
+ if (tok == NULL)
+ {
+ /* Unexpected non-empty but apparently blank line. */
+ DEBUGP(("Null token. Leaving listing parser.\n"));
+ xfree (line); /* Free useless line storage. */
+ break;
+ }
+ }
+ }
+
+ /* Analyze tokens. (Order is not significant, except date must
+ precede time.)
+
+ Size: ddd or ddd/ddd (where "ddd" is a decimal number)
+ Date: DD-MMM-YYYY
+ Time: HH:MM or HH:MM:SS or HH:MM:SS.CC
+ Owner: [user] or [user,group]
+ Protection: (ppp,ppp,ppp,ppp) (where "ppp" is "RWED" or some
+ subset thereof, for System, Owner, Group, World.
+
+ If permission is lacking, info may be replaced by the string:
+ "No privilege for attempted operation".
+ */
+ while (tok != NULL)
+ {
+ DEBUGP (("Token: >%s<: ", tok));
+
+ if ((strlen( tok) < 12) && (strchr( tok, '-') != NULL))
{
- cur.type = FT_PLAINFILE;
- cur.perms = 0644;
+ /* Date. */
+ DEBUGP (("Date.\n"));
+ strcpy( date_str, tok);
+ strcat( date_str, " ");
}
- if (!dir)
+ else if ((strlen( tok) < 12) && (strchr( tok, ':') != NULL))
{
- l = dir = (struct fileinfo *)xmalloc (sizeof (struct fileinfo));
- memcpy (l, &cur, sizeof (cur));
- l->prev = l->next = NULL;
+ /* Time. */
+ DEBUGP (("Time. "));
+ strncat( date_str,
+ tok,
+ (sizeof( date_str)- strlen( date_str)- 1));
+ DEBUGP (("Date time: >%s<\n", date_str));
}
- else
+ else if (strchr( tok, '[') != NULL)
{
- cur.prev = l;
- l->next = (struct fileinfo *)xmalloc (sizeof (struct fileinfo));
- l = l->next;
- memcpy (l, &cur, sizeof (cur));
- l->next = NULL;
+ /* Owner. (Ignore.) */
+ DEBUGP (("Owner.\n"));
}
- l->tstamp = fp.mtime;
- }
+ else if (strchr( tok, '(') != NULL)
+ {
+ /* Protections (permissions). */
+ perms = 0;
+ j = 0;
+ for (i = 0; i < strlen( tok); i++)
+ {
+ switch (tok[ i])
+ {
+ case '(':
+ break;
+ case ')':
+ break;
+ case ',':
+ if (j == 0)
+ {
+ perms = 0;
+ j = 1;
+ }
+ else
+ {
+ perms <<= 3;
+ }
+ break;
+ case 'R':
+ perms |= 4;
+ break;
+ case 'W':
+ perms |= 2;
+ break;
+ case 'E':
+ perms |= 1;
+ break;
+ case 'D':
+ perms |= 2;
+ break;
+ }
+ }
+ cur.perms = perms;
+ DEBUGP (("Prot. perms = %0o.\n", cur.perms));
+ }
+ else
+ {
+ /* Nondescript. Probably size(s), probably in blocks.
+ Could be "No privilege ..." message. (Ignore.)
+ */
+ DEBUGP (("Ignored (size?).\n"));
+ }
+
+ tok = strtok (NULL, " ");
+ }
+
+ /* Tokens exhausted. Interpret the data, and fill in the
+ structure.
+ */
+ /* Fill tm timestruct according to date-time string. Fractional
+ seconds are ignored. Default to current time, if conversion
+ fails.
+ */
+ timenow = time( NULL);
+ localtime_r( &timenow, ×truct);
+ strptime( date_str, "%d-%b-%Y %H:%M:%S", ×truct);
+
+ /* Convert struct tm local time to time_t local time. */
+ timenow = mktime (×truct);
+ /* Offset local time according to environment variable (seconds). */
+ if ((tok = getenv( "WGET_TIMEZONE_DIFFERENTIAL")) != NULL)
+ {
+ dt = atoi( tok);
+ DEBUGP (("Time differential = %d.\n", dt));
+ }
+ else
+ {
+ dt = 0;
+ }
+
+ if (dt >= 0)
+ {
+ timenow += dt;
+ }
+ else
+ {
+ timenow -= (-dt);
+ }
+ cur.tstamp = timenow; /* Store the time-stamp. */
+ DEBUGP(("Timestamp: %ld\n", cur.tstamp));
+ /* Add the data for this item to the linked list, */
+ if (!dir)
+ {
+ l = dir = (struct fileinfo *)xmalloc (sizeof (struct fileinfo));
+ memcpy (l, &cur, sizeof (cur));
+ l->prev = l->next = NULL;
+ }
+ else
+ {
+ cur.prev = l;
+ l->next = (struct fileinfo *)xmalloc (sizeof (struct fileinfo));
+ l = l->next;
+ memcpy (l, &cur, sizeof (cur));
+ l->next = NULL;
+ }
+
+ /* Free old line storage. Read a new line. */
xfree (line);
+ line = read_whole_line (fp);
+ if (line != NULL)
+ {
+ i = clean_line (line);
+ if (i <= 0)
+ {
+ /* Blank line. End of significant file listing. */
+ xfree (line); /* Free useless line storage. */
+ break;
+ }
+ }
}
fclose (fp);
return dir;
}
-#endif
-/* This function switches between the correct parsing routine
- depending on the SYSTEM_TYPE. If system type is ST_UNIX, we use
- our home-grown ftp_parse_unix_ls; otherwise, we use our interface
- to ftpparse, also known as ftp_parse_nonunix_ls. The system type
- should be based on the result of the "SYST" response of the FTP
- server. */
+
+/* This function switches between the correct parsing routine depending on
+ the SYSTEM_TYPE. The system type should be based on the result of the
+ "SYST" response of the FTP server. According to this repsonse we will
+ use on of the three different listing parsers that cover the most of FTP
+ servers used nowadays. */
struct fileinfo *
ftp_parse_ls (const char *file, const enum stype system_type)
@@ -625,34 +1036,35 @@ ftp_parse_ls (const char *file, const enum stype system_type)
switch (system_type)
{
case ST_UNIX:
- return ftp_parse_unix_ls (file, FALSE);
+ return ftp_parse_unix_ls (file, 0);
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;
- }
- 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
- return ftp_parse_unix_ls (file, TRUE);
+ /* 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);
+ else
+ return ftp_parse_unix_ls (file, 1);
}
+ case ST_VMS:
+ return ftp_parse_vms_ls (file);
+ case ST_MACOS:
+ return ftp_parse_unix_ls (file, 1);
default:
-#ifdef HAVE_FTPPARSE
- return ftp_parse_nonunix_ls (file);
-#else
- /* #### Maybe log some warning here? */
- return ftp_parse_unix_ls (file);
-#endif
+ logprintf (LOG_NOTQUIET, _("\
+Unsupported listing type, trying Unix listing parser.\n"));
+ return ftp_parse_unix_ls (file, 0);
}
}
@@ -662,104 +1074,120 @@ ftp_parse_ls (const char *file, const enum stype system_type)
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;
- char *htclfile; /* HTML-clean file name */
+ char *htcldir; /* HTML-clean dir name */
+ char *htclfile; /* HTML-clean file name */
+ char *urlclfile; /* URL-clean file name */
- if (!opt.dfp)
+ if (!output_stream)
{
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
- fp = opt.dfp;
+ fp = output_stream;
if (u->user)
{
char *tmpu, *tmpp; /* temporary, clean user and passwd */
- tmpu = CLEANDUP (u->user);
- tmpp = u->passwd ? CLEANDUP (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);
- FREE_MAYBE (tmpp);
+ xfree_null (tmpp);
}
else
upwd = xstrdup ("");
+
+ htcldir = html_quote_string (u->dir);
+
fprintf (fp, "\n");
fprintf (fp, "\n\n");
- fprintf (fp, _("Index of /%s on %s:%d"), u->dir, u->host, u->port);
+ fprintf (fp, _("Index of /%s on %s:%d"), htcldir, u->host, u->port);
fprintf (fp, "\n\n\n");
- fprintf (fp, _("Index of /%s on %s:%d"), u->dir, u->host, u->port);
+ fprintf (fp, _("Index of /%s on %s:%d"), htcldir, u->host, u->port);
fprintf (fp, "
\n
\n\n");
+
while (f)
{
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
- fprintf (fp, _("time unknown "));
+ fprintf (fp, _("time unknown "));
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);
- fprintf (fp, "host, u->port);
+ urlclfile = url_escape_unsafe_and_reserved (f->name);
+ fprintf (fp, "host, u->port);
if (*u->dir != '/')
- putc ('/', fp);
- fprintf (fp, "%s", u->dir);
+ putc ('/', fp);
+ /* XXX: Should probably URL-escape dir components here, rather
+ * than just HTML-escape, for consistency with the next bit where
+ * we use urlclfile for the file component. Anyway, this is safer
+ * than what we had... */
+ fprintf (fp, "%s", htcldir);
if (*u->dir)
- putc ('/', fp);
- fprintf (fp, "%s", htclfile);
+ putc ('/', fp);
+ fprintf (fp, "%s", urlclfile);
if (f->type == FT_DIRECTORY)
- putc ('/', fp);
+ putc ('/', fp);
fprintf (fp, "\">%s", htclfile);
if (f->type == FT_DIRECTORY)
- putc ('/', fp);
+ putc ('/', fp);
fprintf (fp, " ");
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)
- fprintf (fp, "-> %s", f->linkto ? f->linkto : "(nil)");
+ fprintf (fp, "-> %s", f->linkto ? f->linkto : "(nil)");
putc ('\n', fp);
xfree (htclfile);
+ xfree (urlclfile);
f = f->next;
}
fprintf (fp, "
\n\n\n");
+ xfree (htcldir);
xfree (upwd);
- if (!opt.dfp)
+ if (!output_stream)
fclose (fp);
else
fflush (fp);