+ 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)
+ {
+ /* 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)