diff options
Diffstat (limited to 'src/vfs/ftpfs/ftpfs_parse_ls.c')
-rw-r--r-- | src/vfs/ftpfs/ftpfs_parse_ls.c | 1237 |
1 files changed, 1237 insertions, 0 deletions
diff --git a/src/vfs/ftpfs/ftpfs_parse_ls.c b/src/vfs/ftpfs/ftpfs_parse_ls.c new file mode 100644 index 0000000..f4f137d --- /dev/null +++ b/src/vfs/ftpfs/ftpfs_parse_ls.c @@ -0,0 +1,1237 @@ +/* + Virtual File System: FTP file system + + Copyright (C) 2015-2022 + The Free Software Foundation, Inc. + + Written by: Andrew Borodin <aborodin@vmail.ru>, 2013 + + This file is part of the Midnight Commander. + + The Midnight Commander 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 3 of the License, + or (at your option) any later version. + + The Midnight Commander is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + 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 + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file + * \brief Source: Virtual File System: FTP file system + * \author Andrew Borodin + * \date 2015 + * + * Parser of ftp long file list (reply to "LIST -la" command). + * Borrowed from lftp project (http://http://lftp.yar.ru/). + * Author of original lftp code: Alexander V. Lukyanov (lav@yars.free.net) + */ + +#include <config.h> + +#include <ctype.h> /* isdigit() */ +#include <stdio.h> /* sscanf() */ +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> /* mode_t */ +#include <time.h> +#include <unistd.h> +#include <sys/types.h> + +#include "lib/global.h" + +#include "lib/vfs/vfs.h" +#include "lib/vfs/utilvfs.h" + +#include "ftpfs.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +#define number_of_parsers 7 + +#define NO_SIZE ((off_t) (-1L)) +#define NO_DATE ((time_t) (-1L)) + +#define FIRST_TOKEN strtok (line, " \t") +#define NEXT_TOKEN strtok (NULL, " \t") +#define FIRST_TOKEN_R strtok_r (line, " \t", &next) +#define NEXT_TOKEN_R strtok_r (NULL, " \t", &next) + +#define ERR2 do { (*err)++; return FALSE; } while (FALSE) + +/*** file scope type declarations ****************************************************************/ + +typedef enum +{ + UNKNOWN = 0, + DIRECTORY, + SYMLINK, + NORMAL +} filetype; + +typedef gboolean (*ftpfs_line_parser) (char *line, struct stat * s, char **filename, + char **linkname, int *err); + +/* formard declarations */ +static gboolean ftpfs_parse_long_list_UNIX (char *line, struct stat *s, char **filename, + char **linkname, int *err); +static gboolean ftpfs_parse_long_list_NT (char *line, struct stat *s, char **filename, + char **linkname, int *err); +static gboolean ftpfs_parse_long_list_EPLF (char *line, struct stat *s, char **filename, + char **linkname, int *err); +static gboolean ftpfs_parse_long_list_MLSD (char *line, struct stat *s, char **filename, + char **linkname, int *err); +static gboolean ftpfs_parse_long_list_AS400 (char *line, struct stat *s, char **filename, + char **linkname, int *err); +static gboolean ftpfs_parse_long_list_OS2 (char *line, struct stat *s, char **filename, + char **linkname, int *err); +static gboolean ftpfs_parse_long_list_MacWebStar (char *line, struct stat *s, char **filename, + char **linkname, int *err); + +/*** file scope variables ************************************************************************/ + +static time_t rawnow; +static struct tm now; + +static ftpfs_line_parser line_parsers[number_of_parsers] = { + ftpfs_parse_long_list_UNIX, + ftpfs_parse_long_list_NT, + ftpfs_parse_long_list_EPLF, + ftpfs_parse_long_list_MLSD, + ftpfs_parse_long_list_AS400, + ftpfs_parse_long_list_OS2, + ftpfs_parse_long_list_MacWebStar +}; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static inline uid_t +ftpfs_get_uid (const char *s) +{ + uid_t u; + + if (*s < '0' || *s > '9') + u = vfs_finduid (s); + else + u = (uid_t) atol (s); + + return u; +} + +/* --------------------------------------------------------------------------------------------- */ + +static inline gid_t +ftpfs_get_gid (const char *s) +{ + gid_t g; + + if (*s < '0' || *s > '9') + g = vfs_findgid (s); + else + g = (gid_t) atol (s); + + return g; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +ftpfs_init_time (void) +{ + time (&rawnow); + now = *localtime (&rawnow); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +guess_year (int month, int day, int hour, int minute) +{ + int year; + + (void) hour; + (void) minute; + + year = now.tm_year + 1900; + + if (month * 32 + day > now.tm_mon * 32 + now.tm_mday + 6) + year--; + + return year; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +parse_year_or_time (const char *year_or_time, int *year, int *hour, int *minute) +{ + if (year_or_time[2] == ':') + { + if (sscanf (year_or_time, "%2d:%2d", hour, minute) != 2) + return FALSE; + + *year = -1; + } + else + { + if (sscanf (year_or_time, "%d", year) != 1) + return FALSE; + + *hour = *minute = 0; + } + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +/* Converts struct tm to time_t, assuming the data in tm is UTC rather + than local timezone (mktime assumes the latter). + + Contributed by Roger Beeman <beeman@cisco.com>, with the help of + Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO. */ +static time_t +mktime_from_utc (const struct tm *t) +{ + struct tm tc; + time_t tl, tb; + + memcpy (&tc, t, sizeof (struct tm)); + + /* UTC times are never DST; if we say -1, we'll introduce odd localtime- + * dependant errors. */ + + tc.tm_isdst = 0; + + tl = mktime (&tc); + if (tl == -1) + return (-1); + + tb = mktime (gmtime (&tl)); + + return (tl <= tb ? (tl + (tl - tb)) : (tl - (tb - tl))); +} + +/* --------------------------------------------------------------------------------------------- */ + +static time_t +ftpfs_convert_date (const char *s) +{ + struct tm tm; + int year, month, day, hour, minute, second; + int skip = 0; + int n; + + memset (&tm, 0, sizeof (tm)); + + n = sscanf (s, "%4d%n", &year, &skip); + + /* try to workaround server's y2k bug * + * I hope in the next 300 years the y2k bug will be finally fixed :) */ + if (n == 1 && year >= 1910 && year <= 1930) + { + n = sscanf (s, "%5d%n", &year, &skip); + year = year - 19100 + 2000; + } + + if (n != 1) + return NO_DATE; + + n = sscanf (s + skip, "%2d%2d%2d%2d%2d", &month, &day, &hour, &minute, &second); + + if (n != 5) + return NO_DATE; + + tm.tm_year = year - 1900; + tm.tm_mon = month - 1; + tm.tm_mday = day; + tm.tm_hour = hour; + tm.tm_min = minute; + tm.tm_sec = second; + + return mktime_from_utc (&tm); +} + +/* --------------------------------------------------------------------------------------------- */ + +/* + -rwxr-xr-x 1 lav root 4771 Sep 12 1996 install-sh + -rw-r--r-- 1 lav root 1349 Feb 2 14:10 lftp.lsm + drwxr-xr-x 4 lav root 1024 Feb 22 15:32 lib + lrwxrwxrwx 1 lav root 33 Feb 14 17:45 ltconfig -> /usr/share/libtool/ltconfig + + NOTE: group may be missing. + */ + +static gboolean +parse_ls_line (char *line, struct stat *s, char **filename, char **linkname) +{ + char *next = NULL; + char *t; + mode_t type, mode = 0; + char *group_or_size; + struct tm date; + const char *day_of_month; + gboolean year_anomaly = FALSE; + char *name; + + /* parse perms */ + t = FIRST_TOKEN_R; + if (t == NULL) + return FALSE; + + if (!vfs_parse_filetype (t, NULL, &type)) + return FALSE; + + if (vfs_parse_fileperms (t + 1, NULL, &mode)) + mode |= type; + + s->st_mode = mode; + + /* link count */ + t = NEXT_TOKEN_R; + if (t == NULL) + return FALSE; + s->st_nlink = atol (t); + + /* user */ + t = NEXT_TOKEN_R; + if (t == NULL) + return FALSE; + + s->st_uid = ftpfs_get_uid (t); + + /* group or size */ + group_or_size = NEXT_TOKEN_R; + + /* size or month */ + t = NEXT_TOKEN_R; + if (t == NULL) + return FALSE; + if (isdigit ((unsigned char) *t)) + { + /* it's size, so the previous was group: */ + long long size; + int n; + + s->st_gid = ftpfs_get_gid (group_or_size); + + if (sscanf (t, "%lld%n", &size, &n) == 1 && t[n] == '\0') + s->st_size = (off_t) size; + t = NEXT_TOKEN_R; + if (t == NULL) + return FALSE; + } + else + { + /* it was month, so the previous was size: */ + long long size; + int n; + + if (sscanf (group_or_size, "%lld%n", &size, &n) == 1 && group_or_size[n] == '\0') + s->st_size = (off_t) size; + } + +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + s->st_blksize = 512; +#endif +#ifdef HAVE_STRUCT_STAT_ST_BLOCKS + s->st_blocks = (s->st_size + 511) / 512; +#endif + + memset (&date, 0, sizeof (date)); + + if (!vfs_parse_month (t, &date)) + date.tm_mon = 0; + + day_of_month = NEXT_TOKEN_R; + if (day_of_month == NULL) + return FALSE; + date.tm_mday = atoi (day_of_month); + + /* time or year */ + t = NEXT_TOKEN_R; + if (t == NULL) + return FALSE; + date.tm_isdst = -1; + date.tm_hour = date.tm_min = 0; + date.tm_sec = 30; + + if (sscanf (t, "%2d:%2d", &date.tm_hour, &date.tm_min) == 2) + date.tm_year = guess_year (date.tm_mon, date.tm_mday, date.tm_hour, date.tm_min) - 1900; + else + { + if (day_of_month + strlen (day_of_month) + 1 == t) + year_anomaly = TRUE; + date.tm_year = atoi (t) - 1900; + /* We don't know the hour. Set it to something other than 0, or + * DST -1 will end up changing the date. */ + date.tm_hour = 12; + date.tm_min = 0; + date.tm_sec = 0; + } + + s->st_mtime = mktime (&date); + /* Use resulting time value */ + s->st_atime = s->st_ctime = s->st_mtime; + + name = strtok_r (NULL, "", &next); + if (name == NULL) + return FALSE; + + /* there are ls which output extra space after year. */ + if (year_anomaly && *name == ' ') + name++; + + if (!S_ISLNK (s->st_mode)) + *linkname = NULL; + else + { + char *arrow; + + for (arrow = name; (arrow = strstr (arrow, " -> ")) != NULL; arrow++) + if (arrow != name && arrow[4] != '\0') + { + *arrow = '\0'; + *linkname = g_strdup (arrow + 4); + break; + } + } + + *filename = g_strdup (name); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +ftpfs_parse_long_list_UNIX (char *line, struct stat *s, char **filename, char **linkname, int *err) +{ + int tmp; + gboolean ret; + + if (sscanf (line, "total %d", &tmp) == 1) + return FALSE; + + if (strncasecmp (line, "Status of ", 10) == 0) + return FALSE; /* STAT output. */ + if (strchr ("bcpsD", line[0]) != NULL) /* block, char, pipe, socket, Door. */ + return FALSE; + + ret = parse_ls_line (line, s, filename, linkname); + if (!ret) + (*err)++; + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +/* + 07-13-98 09:06PM <DIR> aix + 07-13-98 09:06PM <DIR> hpux + 07-13-98 09:06PM <DIR> linux + 07-13-98 09:06PM <DIR> ncr + 07-13-98 09:06PM <DIR> solaris + 03-18-98 06:01AM 2109440 nlxb318e.tar + 07-02-98 11:17AM 13844 Whatsnew.txt + */ + +static gboolean +ftpfs_parse_long_list_NT (char *line, struct stat *s, char **filename, char **linkname, int *err) +{ + char *t; + int month, day, year, hour, minute; + char am; + struct tm tms; + long long size; + + t = FIRST_TOKEN; + if (t == NULL) + ERR2; + if (sscanf (t, "%2d-%2d-%2d", &month, &day, &year) != 3) + ERR2; + if (year >= 70) + year += 1900; + else + year += 2000; + + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + am = 'A'; /* AM/PM is optional */ + if (sscanf (t, "%2d:%2d%c", &hour, &minute, &am) < 2) + ERR2; + + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + + if (am == 'P') /* PM - after noon */ + { + hour += 12; + if (hour == 24) + hour = 0; + } + + tms.tm_sec = 30; /* seconds after the minute [0, 61] */ + tms.tm_min = minute; /* minutes after the hour [0, 59] */ + tms.tm_hour = hour; /* hour since midnight [0, 23] */ + tms.tm_mday = day; /* day of the month [1, 31] */ + tms.tm_mon = month - 1; /* months since January [0, 11] */ + tms.tm_year = year - 1900; /* years since 1900 */ + tms.tm_isdst = -1; + + + s->st_mtime = mktime (&tms); + /* Use resulting time value */ + s->st_atime = s->st_ctime = s->st_mtime; + + if (strcmp (t, "<DIR>") == 0) + s->st_mode = S_IFDIR; + else + { + s->st_mode = S_IFREG; + if (sscanf (t, "%lld", &size) != 1) + ERR2; + s->st_size = (off_t) size; + } + + t = strtok (NULL, ""); + if (t == NULL) + ERR2; + while (*t == ' ') + t++; + if (*t == '\0') + ERR2; + + *filename = g_strdup (t); + *linkname = NULL; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +/* + +i774.71425,m951188401,/, users + +i774.49602,m917883130,r,s79126, jgr_www2.exe + + starts with + + comma separated + first character of field is type: + i - ? + m - modification time + / - means directory + r - means plain file + s - size + up - permissions in octal + \t - file name follows. + */ + +static gboolean +ftpfs_parse_long_list_EPLF (char *line, struct stat *s, char **filename, char **linkname, int *err) +{ + size_t len; + const char *b; + const char *name = NULL; + size_t name_len = 0; + off_t size = NO_SIZE; + time_t date = NO_DATE; + long date_l; + long long size_ll; + gboolean dir = FALSE; + gboolean type_known = FALSE; + int perms = -1; + const char *scan; + ssize_t scan_len; + + len = strlen (line); + b = line; + + if (len < 2 || b[0] != '+') + ERR2; + + scan = b + 1; + scan_len = len - 1; + + while (scan != NULL && scan_len > 0) + { + const char *comma; + + switch (*scan) + { + case '\t': /* the rest is file name. */ + name = scan + 1; + name_len = scan_len - 1; + scan = NULL; + break; + case 's': + if (sscanf (scan + 1, "%lld", &size_ll) != 1) + break; + size = size_ll; + break; + case 'm': + if (sscanf (scan + 1, "%ld", &date_l) != 1) + break; + date = date_l; + break; + case '/': + dir = TRUE; + type_known = TRUE; + break; + case 'r': + dir = FALSE; + type_known = TRUE; + break; + case 'i': + break; + case 'u': + if (scan[1] == 'p') /* permissions. */ + if (sscanf (scan + 2, "%o", (unsigned int *) &perms) != 1) + perms = -1; + break; + default: + name = NULL; + scan = NULL; + break; + } + if (scan == NULL || scan_len == 0) + break; + + comma = (const char *) memchr (scan, ',', scan_len); + if (comma == NULL) + break; + + scan_len -= comma + 1 - scan; + scan = comma + 1; + } + + if (name == NULL || !type_known) + ERR2; + + *filename = g_strndup (name, name_len); + *linkname = NULL; + + if (size != NO_SIZE) + s->st_size = size; + if (date != NO_DATE) + { + s->st_mtime = date; + /* Use resulting time value */ + s->st_atime = s->st_ctime = s->st_mtime; + } + if (type_known) + s->st_mode = dir ? S_IFDIR : S_IFREG; + if (perms != -1) + s->st_mode |= perms; /* FIXME */ + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* + Type=cdir;Modify=20021029173810;Perm=el;Unique=BP8AAjJufAA; / + Type=pdir;Modify=20021029173810;Perm=el;Unique=BP8AAjJufAA; .. + Type=dir;Modify=20010118144705;Perm=e;Unique=BP8AAjNufAA; bin + Type=dir;Modify=19981021003019;Perm=el;Unique=BP8AAlhufAA; pub + Type=file;Size=12303;Modify=19970124132601;Perm=r;Unique=BP8AAo9ufAA; mailserv.FAQ + modify=20161215062118;perm=flcdmpe;type=dir;UNIX.group=503;UNIX.mode=0700; directory-name + modify=20161213121618;perm=adfrw;size=6369064;type=file;UNIX.group=503;UNIX.mode=0644; file-name + modify=20120103123744;perm=adfrw;size=11;type=OS.unix=symlink;UNIX.group=0;UNIX.mode=0777; www + */ + +static gboolean +ftpfs_parse_long_list_MLSD (char *line, struct stat *s, char **filename, char **linkname, int *err) +{ + const char *name = NULL; + off_t size = NO_SIZE; + time_t date = NO_DATE; + const char *owner = NULL; + const char *group = NULL; + filetype type = UNKNOWN; + int perms = -1; + char *space; + char *tok; + + space = strstr (line, "; "); + if (space != NULL) + { + name = space + 2; + *space = '\0'; + } + else + { + /* NcFTPd does not put a semicolon after last fact, workaround it. */ + space = strchr (line, ' '); + if (space == NULL) + ERR2; + name = space + 1; + *space = '\0'; + } + + for (tok = strtok (line, ";"); tok != NULL; tok = strtok (NULL, ";")) + { + if (strcasecmp (tok, "Type=cdir") == 0 + || strcasecmp (tok, "Type=pdir") == 0 || strcasecmp (tok, "Type=dir") == 0) + { + type = DIRECTORY; + continue; + } + if (strcasecmp (tok, "Type=file") == 0) + { + type = NORMAL; + continue; + } + if (strcasecmp (tok, "Type=OS.unix=symlink") == 0) + { + type = SYMLINK; + continue; + } + if (strncasecmp (tok, "Modify=", 7) == 0) + { + date = ftpfs_convert_date (tok + 7); + continue; + } + if (strncasecmp (tok, "Size=", 5) == 0) + { + long long size_ll; + + if (sscanf (tok + 5, "%lld", &size_ll) == 1) + size = size_ll; + continue; + } + if (strncasecmp (tok, "Perm=", 5) == 0) + { + perms = 0; + for (tok += 5; *tok != '\0'; tok++) + { + switch (g_ascii_tolower (*tok)) + { + case 'e': + perms |= 0111; + break; + case 'l': + perms |= 0444; + break; + case 'r': + perms |= 0444; + break; + case 'c': + perms |= 0200; + break; + case 'w': + perms |= 0200; + break; + default: + break; + } + } + continue; + } + if (strncasecmp (tok, "UNIX.mode=", 10) == 0) + { + if (sscanf (tok + 10, "%o", (unsigned int *) &perms) != 1) + perms = -1; + continue; + } + if (strncasecmp (tok, "UNIX.owner=", 11) == 0) + { + owner = tok + 11; + continue; + } + if (strncasecmp (tok, "UNIX.group=", 11) == 0) + { + group = tok + 11; + continue; + } + if (strncasecmp (tok, "UNIX.uid=", 9) == 0) + { + if (owner == NULL) + owner = tok + 9; + continue; + } + if (strncasecmp (tok, "UNIX.gid=", 9) == 0) + { + if (group == NULL) + group = tok + 9; + continue; + } + } + if (name == NULL || name[0] == '\0' || type == UNKNOWN) + ERR2; + + *filename = g_strdup (name); + *linkname = NULL; + + if (size != NO_SIZE) + s->st_size = size; + if (date != NO_DATE) + { + s->st_mtime = date; + /* Use resulting time value */ + s->st_atime = s->st_ctime = s->st_mtime; + } + switch (type) + { + case DIRECTORY: + s->st_mode = S_IFDIR; + break; + case SYMLINK: + s->st_mode = S_IFLNK; + break; + case NORMAL: + s->st_mode = S_IFREG; + break; + default: + g_assert_not_reached (); + } + if (perms != -1) + s->st_mode |= perms; /* FIXME */ + if (owner != NULL) + s->st_uid = ftpfs_get_uid (owner); + if (group != NULL) + s->st_gid = ftpfs_get_gid (group); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +/* + ASUSER 8192 04/26/05 13:54:16 *DIR dir/ + ASUSER 8192 04/26/05 13:57:34 *DIR dir1/ + ASUSER 365255 02/28/01 15:41:40 *STMF readme.txt + ASUSER 8489625 03/18/03 09:37:00 *STMF saved.zip + ASUSER 365255 02/28/01 15:41:40 *STMF unist.old + */ + +static gboolean +ftpfs_parse_long_list_AS400 (char *line, struct stat *s, char **filename, char **linkname, int *err) +{ + char *t; + char *user; + long long size; + int month, day, year, hour, minute, second; + struct tm tms; + time_t mtime; + mode_t type; + char *slash; + + t = FIRST_TOKEN; + if (t == NULL) + ERR2; + user = t; + + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + if (sscanf (t, "%lld", &size) != 1) + ERR2; + + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + if (sscanf (t, "%2d/%2d/%2d", &month, &day, &year) != 3) + ERR2; + if (year >= 70) + year += 1900; + else + year += 2000; + + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + if (sscanf (t, "%2d:%2d:%2d", &hour, &minute, &second) != 3) + ERR2; + + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + + tms.tm_sec = second; /* seconds after the minute [0, 61] */ + tms.tm_min = minute; /* minutes after the hour [0, 59] */ + tms.tm_hour = hour; /* hour since midnight [0, 23] */ + tms.tm_mday = day; /* day of the month [1, 31] */ + tms.tm_mon = month - 1; /* months since January [0, 11] */ + tms.tm_year = year - 1900; /* years since 1900 */ + tms.tm_isdst = -1; + mtime = mktime (&tms); + + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + if (strcmp (t, "*DIR") == 0) + type = S_IFDIR; + else + type = S_IFREG; + + t = strtok (NULL, ""); + if (t == NULL) + ERR2; + while (*t == ' ') + t++; + if (*t == '\0') + ERR2; + + *linkname = NULL; + + slash = strchr (t, '/'); + if (slash != NULL) + { + if (slash == t) + return FALSE; + + *slash = '\0'; + type = S_IFDIR; + if (slash[1] != '\0') + { + *filename = g_strdup (t); + s->st_mode = type; /* FIXME */ + return TRUE; + } + } + + *filename = g_strdup (t); + s->st_mode = type; + s->st_size = (off_t) size; + s->st_mtime = mtime; + /* Use resulting time value */ + s->st_atime = s->st_ctime = s->st_mtime; + s->st_uid = ftpfs_get_uid (user); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +/* + 0 DIR 06-27-96 11:57 PROTOCOL + 169 11-29-94 09:20 SYSLEVEL.MPT + */ + +static gboolean +ftpfs_parse_long_list_OS2 (char *line, struct stat *s, char **filename, char **linkname, int *err) +{ + char *t; + long long size; + int month, day, year, hour, minute; + struct tm tms; + + t = FIRST_TOKEN; + if (t == NULL) + ERR2; + + if (sscanf (t, "%lld", &size) != 1) + ERR2; + s->st_size = (off_t) size; + + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + s->st_mode = S_IFREG; + if (strcmp (t, "DIR") == 0) + { + s->st_mode = S_IFDIR; + t = NEXT_TOKEN; + + if (t == NULL) + ERR2; + } + + if (sscanf (t, "%2d-%2d-%2d", &month, &day, &year) != 3) + ERR2; + if (year >= 70) + year += 1900; + else + year += 2000; + + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + if (sscanf (t, "%2d:%2d", &hour, &minute) != 3) + ERR2; + + tms.tm_sec = 30; /* seconds after the minute [0, 61] */ + tms.tm_min = minute; /* minutes after the hour [0, 59] */ + tms.tm_hour = hour; /* hour since midnight [0, 23] */ + tms.tm_mday = day; /* day of the month [1, 31] */ + tms.tm_mon = month - 1; /* months since January [0, 11] */ + tms.tm_year = year - 1900; /* years since 1900 */ + tms.tm_isdst = -1; + s->st_mtime = mktime (&tms); + /* Use resulting time value */ + s->st_atime = s->st_ctime = s->st_mtime; + + t = strtok (NULL, ""); + if (t == NULL) + ERR2; + while (*t == ' ') + t++; + if (*t == '\0') + ERR2; + *filename = g_strdup (t); + *linkname = NULL; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +ftpfs_parse_long_list_MacWebStar (char *line, struct stat *s, char **filename, + char **linkname, int *err) +{ + char *t; + mode_t type, mode; + struct tm date; + const char *day_of_month; + char *name; + + t = FIRST_TOKEN; + if (t == NULL) + ERR2; + + if (!vfs_parse_filetype (t, NULL, &type)) + ERR2; + + s->st_mode = type; + + if (!vfs_parse_fileperms (t + 1, NULL, &mode)) + ERR2; + /* permissions are meaningless here. */ + + /* "folder" or 0 */ + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + + if (strcmp (t, "folder") != 0) + { + long long size; + + /* size? */ + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + /* size */ + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + if (!isdigit ((unsigned char) *t)) + ERR2; + + if (sscanf (t, "%lld", &size) == 1) + s->st_size = (off_t) size; + } + else + { + /* ?? */ + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + } + + /* month */ + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + + memset (&date, 0, sizeof (date)); + + if (!vfs_parse_month (t, &date)) + ERR2; + + day_of_month = NEXT_TOKEN; + if (day_of_month == NULL) + ERR2; + + date.tm_mday = atoi (day_of_month); + + /* time or year */ + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + if (!parse_year_or_time (t, &date.tm_year, &date.tm_hour, &date.tm_min)) + ERR2; + + date.tm_isdst = -1; + date.tm_sec = 30; + if (date.tm_year == -1) + date.tm_year = guess_year (date.tm_mon, date.tm_mday, date.tm_hour, date.tm_min) - 1900; + else + date.tm_hour = 12; + + s->st_mtime = mktime (&date); + /* Use resulting time value */ + s->st_atime = s->st_ctime = s->st_mtime; + + name = strtok (NULL, ""); + if (name == NULL) + ERR2; + + /* no symlinks on Mac, but anyway. */ + if (!S_ISLNK (s->st_mode)) + *linkname = NULL; + else + { + char *arrow; + + for (arrow = name; (arrow = strstr (arrow, " -> ")) != NULL; arrow++) + if (arrow != name && arrow[4] != '\0') + { + *arrow = '\0'; + *linkname = g_strdup (arrow + 4); + break; + } + } + + *filename = g_strdup (name); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +GSList * +ftpfs_parse_long_list (struct vfs_class * me, struct vfs_s_inode * dir, GSList * buf, int *err_ret) +{ + int err[number_of_parsers]; + GSList *set[number_of_parsers]; /* arrays of struct vfs_s_entry */ + size_t i; + GSList *bufp; + ftpfs_line_parser guessed_parser = NULL; + GSList **the_set = NULL; + int *the_err = NULL; + int *best_err1 = &err[0]; + int *best_err2 = &err[1]; + + ftpfs_init_time (); + + if (err_ret != NULL) + *err_ret = 0; + + memset (&err, 0, sizeof (err)); + memset (&set, 0, sizeof (set)); + + for (bufp = buf; bufp != NULL; bufp = g_slist_next (bufp)) + { + char *b = (char *) bufp->data; + size_t blen; + + blen = strlen (b); + + if (b[blen - 1] == '\r') + { + b[blen - 1] = '\0'; + blen--; + } + + if (blen == 0) + continue; + + if (guessed_parser == NULL) + { + for (i = 0; i < number_of_parsers; i++) + { + struct vfs_s_entry *info; + gboolean ok; + char *tmp_line; + int nlink; + + /* parser can clobber the line - work on a copy */ + tmp_line = g_strndup (b, blen); + + info = vfs_s_generate_entry (me, NULL, dir, 0); + nlink = info->ino->st.st_nlink; + ok = (*line_parsers[i]) (tmp_line, &info->ino->st, &info->name, + &info->ino->linkname, &err[i]); + if (ok && strchr (info->name, '/') == NULL) + { + info->ino->st.st_nlink = nlink; /* Ouch, we need to preserve our counts :-( */ + set[i] = g_slist_prepend (set[i], info); + } + else + vfs_s_free_entry (me, info); + + g_free (tmp_line); + + if (*best_err1 > err[i]) + best_err1 = &err[i]; + if (*best_err2 > err[i] && best_err1 != &err[i]) + best_err2 = &err[i]; + + if (*best_err1 > 16) + goto leave; /* too many errors with best parser. */ + } + + if (*best_err2 > (*best_err1 + 1) * 16) + { + i = (size_t) (best_err1 - err); + guessed_parser = line_parsers[i]; + the_set = &set[i]; + the_err = &err[i]; + } + } + else + { + struct vfs_s_entry *info; + gboolean ok; + char *tmp_line; + int nlink; + + /* parser can clobber the line - work on a copy */ + tmp_line = g_strndup (b, blen); + + info = vfs_s_generate_entry (me, NULL, dir, 0); + nlink = info->ino->st.st_nlink; + ok = guessed_parser (tmp_line, &info->ino->st, &info->name, &info->ino->linkname, + the_err); + if (ok && strchr (info->name, '/') == NULL) + { + info->ino->st.st_nlink = nlink; /* Ouch, we need to preserve our counts :-( */ + *the_set = g_slist_prepend (*the_set, info); + } + else + vfs_s_free_entry (me, info); + + g_free (tmp_line); + } + } + + if (the_set == NULL) + { + i = best_err1 - err; + the_set = &set[i]; + the_err = &err[i]; + } + + leave: + for (i = 0; i < number_of_parsers; i++) + if (&set[i] != the_set) + { + for (bufp = set[i]; bufp != NULL; bufp = g_slist_next (bufp)) + vfs_s_free_entry (me, VFS_ENTRY (bufp->data)); + + g_slist_free (set[i]); + } + + if (err_ret != NULL && the_err != NULL) + *err_ret = *the_err; + + return the_set != NULL ? g_slist_reverse (*the_set) : NULL; +} + +/* --------------------------------------------------------------------------------------------- */ |