tar: avoid need for base64_init and extra table.
[midnight-commander.git] / src / vfs / ftpfs / ftpfs_parse_ls.c
blob2befd20eddd809442291f39ff48fc7148871e135
1 /*
2 Virtual File System: FTP file system
4 Copyright (C) 2015-2024
5 The Free Software Foundation, Inc.
7 Written by: Andrew Borodin <aborodin@vmail.ru>, 2013
9 This file is part of the Midnight Commander.
11 The Midnight Commander is free software: you can redistribute it
12 and/or modify it under the terms of the GNU General Public License as
13 published by the Free Software Foundation, either version 3 of the License,
14 or (at your option) any later version.
16 The Midnight Commander is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 /** \file
26 * \brief Source: Virtual File System: FTP file system
27 * \author Andrew Borodin
28 * \date 2015
30 * Parser of ftp long file list (reply to "LIST -la" command).
31 * Borrowed from lftp project (http://http://lftp.yar.ru/).
32 * Author of original lftp code: Alexander V. Lukyanov (lav@yars.free.net)
35 #include <config.h>
37 #include <ctype.h> /* isdigit() */
38 #include <stdio.h> /* sscanf() */
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/stat.h> /* mode_t */
42 #include <time.h>
43 #include <unistd.h>
44 #include <sys/types.h>
46 #include "lib/global.h"
48 #include "lib/vfs/vfs.h"
49 #include "lib/vfs/utilvfs.h"
51 #include "ftpfs.h"
53 /*** global variables ****************************************************************************/
55 /*** file scope macro definitions ****************************************************************/
57 #define number_of_parsers 7
59 #define NO_SIZE ((off_t) (-1L))
60 #define NO_DATE ((time_t) (-1L))
62 #define FIRST_TOKEN strtok (line, " \t")
63 #define NEXT_TOKEN strtok (NULL, " \t")
64 #define FIRST_TOKEN_R strtok_r (line, " \t", &next)
65 #define NEXT_TOKEN_R strtok_r (NULL, " \t", &next)
67 #define ERR2 do { (*err)++; return FALSE; } while (FALSE)
69 /*** file scope type declarations ****************************************************************/
71 typedef enum
73 UNKNOWN = 0,
74 DIRECTORY,
75 SYMLINK,
76 NORMAL
77 } filetype;
79 typedef gboolean (*ftpfs_line_parser) (char *line, struct stat * s, char **filename,
80 char **linkname, int *err);
82 /*** forward declarations (file scope functions) *************************************************/
84 static gboolean ftpfs_parse_long_list_UNIX (char *line, struct stat *s, char **filename,
85 char **linkname, int *err);
86 static gboolean ftpfs_parse_long_list_NT (char *line, struct stat *s, char **filename,
87 char **linkname, int *err);
88 static gboolean ftpfs_parse_long_list_EPLF (char *line, struct stat *s, char **filename,
89 char **linkname, int *err);
90 static gboolean ftpfs_parse_long_list_MLSD (char *line, struct stat *s, char **filename,
91 char **linkname, int *err);
92 static gboolean ftpfs_parse_long_list_AS400 (char *line, struct stat *s, char **filename,
93 char **linkname, int *err);
94 static gboolean ftpfs_parse_long_list_OS2 (char *line, struct stat *s, char **filename,
95 char **linkname, int *err);
96 static gboolean ftpfs_parse_long_list_MacWebStar (char *line, struct stat *s, char **filename,
97 char **linkname, int *err);
99 /*** file scope variables ************************************************************************/
101 static time_t rawnow;
102 static struct tm now;
104 static ftpfs_line_parser line_parsers[number_of_parsers] = {
105 ftpfs_parse_long_list_UNIX,
106 ftpfs_parse_long_list_NT,
107 ftpfs_parse_long_list_EPLF,
108 ftpfs_parse_long_list_MLSD,
109 ftpfs_parse_long_list_AS400,
110 ftpfs_parse_long_list_OS2,
111 ftpfs_parse_long_list_MacWebStar
114 /* --------------------------------------------------------------------------------------------- */
115 /*** file scope functions ************************************************************************/
116 /* --------------------------------------------------------------------------------------------- */
118 static inline uid_t
119 ftpfs_get_uid (const char *s)
121 uid_t u;
123 if (*s < '0' || *s > '9')
124 u = vfs_finduid (s);
125 else
126 u = (uid_t) atol (s);
128 return u;
131 /* --------------------------------------------------------------------------------------------- */
133 static inline gid_t
134 ftpfs_get_gid (const char *s)
136 gid_t g;
138 if (*s < '0' || *s > '9')
139 g = vfs_findgid (s);
140 else
141 g = (gid_t) atol (s);
143 return g;
146 /* --------------------------------------------------------------------------------------------- */
148 static void
149 ftpfs_init_time (void)
151 time (&rawnow);
152 now = *localtime (&rawnow);
155 /* --------------------------------------------------------------------------------------------- */
157 static int
158 guess_year (int month, int day, int hour, int minute)
160 int year;
162 (void) hour;
163 (void) minute;
165 year = now.tm_year + 1900;
167 if (month * 32 + day > now.tm_mon * 32 + now.tm_mday + 6)
168 year--;
170 return year;
173 /* --------------------------------------------------------------------------------------------- */
175 static gboolean
176 parse_year_or_time (const char *year_or_time, int *year, int *hour, int *minute)
178 if (year_or_time[2] == ':')
180 if (sscanf (year_or_time, "%2d:%2d", hour, minute) != 2)
181 return FALSE;
183 *year = -1;
185 else
187 if (sscanf (year_or_time, "%d", year) != 1)
188 return FALSE;
190 *hour = *minute = 0;
193 return TRUE;
196 /* --------------------------------------------------------------------------------------------- */
198 /* Converts struct tm to time_t, assuming the data in tm is UTC rather
199 than local timezone (mktime assumes the latter).
201 Contributed by Roger Beeman <beeman@cisco.com>, with the help of
202 Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO. */
203 static time_t
204 mktime_from_utc (const struct tm *t)
206 struct tm tc;
207 time_t tl, tb;
209 memcpy (&tc, t, sizeof (struct tm));
211 /* UTC times are never DST; if we say -1, we'll introduce odd localtime-
212 * dependent errors. */
214 tc.tm_isdst = 0;
216 tl = mktime (&tc);
217 if (tl == -1)
218 return (-1);
220 tb = mktime (gmtime (&tl));
222 return (tl <= tb ? (tl + (tl - tb)) : (tl - (tb - tl)));
225 /* --------------------------------------------------------------------------------------------- */
227 static time_t
228 ftpfs_convert_date (const char *s)
230 struct tm tm;
231 int year, month, day, hour, minute, second;
232 int skip = 0;
233 int n;
235 memset (&tm, 0, sizeof (tm));
237 n = sscanf (s, "%4d%n", &year, &skip);
239 /* try to workaround server's y2k bug *
240 * I hope in the next 300 years the y2k bug will be finally fixed :) */
241 if (n == 1 && year >= 1910 && year <= 1930)
243 n = sscanf (s, "%5d%n", &year, &skip);
244 year = year - 19100 + 2000;
247 if (n != 1)
248 return NO_DATE;
250 n = sscanf (s + skip, "%2d%2d%2d%2d%2d", &month, &day, &hour, &minute, &second);
252 if (n != 5)
253 return NO_DATE;
255 tm.tm_year = year - 1900;
256 tm.tm_mon = month - 1;
257 tm.tm_mday = day;
258 tm.tm_hour = hour;
259 tm.tm_min = minute;
260 tm.tm_sec = second;
262 return mktime_from_utc (&tm);
265 /* --------------------------------------------------------------------------------------------- */
268 -rwxr-xr-x 1 lav root 4771 Sep 12 1996 install-sh
269 -rw-r--r-- 1 lav root 1349 Feb 2 14:10 lftp.lsm
270 drwxr-xr-x 4 lav root 1024 Feb 22 15:32 lib
271 lrwxrwxrwx 1 lav root 33 Feb 14 17:45 ltconfig -> /usr/share/libtool/ltconfig
273 NOTE: group may be missing.
276 static gboolean
277 parse_ls_line (char *line, struct stat *s, char **filename, char **linkname)
279 char *next = NULL;
280 char *t;
281 mode_t type, mode = 0;
282 char *group_or_size;
283 struct tm date;
284 const char *day_of_month;
285 gboolean year_anomaly = FALSE;
286 char *name;
288 /* parse perms */
289 t = FIRST_TOKEN_R;
290 if (t == NULL)
291 return FALSE;
293 if (!vfs_parse_filetype (t, NULL, &type))
294 return FALSE;
296 if (vfs_parse_fileperms (t + 1, NULL, &mode))
297 mode |= type;
299 s->st_mode = mode;
301 /* link count */
302 t = NEXT_TOKEN_R;
303 if (t == NULL)
304 return FALSE;
305 s->st_nlink = atol (t);
307 /* user */
308 t = NEXT_TOKEN_R;
309 if (t == NULL)
310 return FALSE;
312 s->st_uid = ftpfs_get_uid (t);
314 /* group or size */
315 group_or_size = NEXT_TOKEN_R;
317 /* size or month */
318 t = NEXT_TOKEN_R;
319 if (t == NULL)
320 return FALSE;
321 if (isdigit ((unsigned char) *t))
323 /* it's size, so the previous was group: */
324 long long size;
325 int n;
327 s->st_gid = ftpfs_get_gid (group_or_size);
329 if (sscanf (t, "%lld%n", &size, &n) == 1 && t[n] == '\0')
330 s->st_size = (off_t) size;
331 t = NEXT_TOKEN_R;
332 if (t == NULL)
333 return FALSE;
335 else
337 /* it was month, so the previous was size: */
338 long long size;
339 int n;
341 if (sscanf (group_or_size, "%lld%n", &size, &n) == 1 && group_or_size[n] == '\0')
342 s->st_size = (off_t) size;
345 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
346 s->st_blksize = 512;
347 #endif
348 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
349 s->st_blocks = (s->st_size + 511) / 512;
350 #endif
352 memset (&date, 0, sizeof (date));
354 if (!vfs_parse_month (t, &date))
355 date.tm_mon = 0;
357 day_of_month = NEXT_TOKEN_R;
358 if (day_of_month == NULL)
359 return FALSE;
360 date.tm_mday = atoi (day_of_month);
362 /* time or year */
363 t = NEXT_TOKEN_R;
364 if (t == NULL)
365 return FALSE;
366 date.tm_isdst = -1;
367 date.tm_hour = date.tm_min = 0;
368 date.tm_sec = 30;
370 if (sscanf (t, "%2d:%2d", &date.tm_hour, &date.tm_min) == 2)
371 date.tm_year = guess_year (date.tm_mon, date.tm_mday, date.tm_hour, date.tm_min) - 1900;
372 else
374 if (day_of_month + strlen (day_of_month) + 1 == t)
375 year_anomaly = TRUE;
376 date.tm_year = atoi (t) - 1900;
377 /* We don't know the hour. Set it to something other than 0, or
378 * DST -1 will end up changing the date. */
379 date.tm_hour = 12;
380 date.tm_min = 0;
381 date.tm_sec = 0;
384 s->st_mtime = mktime (&date);
385 /* Use resulting time value */
386 s->st_atime = s->st_ctime = s->st_mtime;
388 name = strtok_r (NULL, "", &next);
389 if (name == NULL)
390 return FALSE;
392 /* there are ls which output extra space after year. */
393 if (year_anomaly && *name == ' ')
394 name++;
396 if (!S_ISLNK (s->st_mode))
397 *linkname = NULL;
398 else
400 char *arrow;
402 for (arrow = name; (arrow = strstr (arrow, " -> ")) != NULL; arrow++)
403 if (arrow != name && arrow[4] != '\0')
405 *arrow = '\0';
406 *linkname = g_strdup (arrow + 4);
407 break;
411 *filename = g_strdup (name);
413 return TRUE;
416 /* --------------------------------------------------------------------------------------------- */
418 static gboolean
419 ftpfs_parse_long_list_UNIX (char *line, struct stat *s, char **filename, char **linkname, int *err)
421 int tmp;
422 gboolean ret;
424 if (sscanf (line, "total %d", &tmp) == 1)
425 return FALSE;
427 if (strncasecmp (line, "Status of ", 10) == 0)
428 return FALSE; /* STAT output. */
430 ret = parse_ls_line (line, s, filename, linkname);
431 if (!ret)
432 (*err)++;
434 return ret;
437 /* --------------------------------------------------------------------------------------------- */
440 07-13-98 09:06PM <DIR> aix
441 07-13-98 09:06PM <DIR> hpux
442 07-13-98 09:06PM <DIR> linux
443 07-13-98 09:06PM <DIR> ncr
444 07-13-98 09:06PM <DIR> solaris
445 03-18-98 06:01AM 2109440 nlxb318e.tar
446 07-02-98 11:17AM 13844 Whatsnew.txt
449 static gboolean
450 ftpfs_parse_long_list_NT (char *line, struct stat *s, char **filename, char **linkname, int *err)
452 char *t;
453 int month, day, year, hour, minute;
454 char am;
455 struct tm tms;
456 long long size;
458 t = FIRST_TOKEN;
459 if (t == NULL)
460 ERR2;
461 if (sscanf (t, "%2d-%2d-%2d", &month, &day, &year) != 3)
462 ERR2;
463 if (year >= 70)
464 year += 1900;
465 else
466 year += 2000;
468 t = NEXT_TOKEN;
469 if (t == NULL)
470 ERR2;
471 am = 'A'; /* AM/PM is optional */
472 if (sscanf (t, "%2d:%2d%c", &hour, &minute, &am) < 2)
473 ERR2;
475 t = NEXT_TOKEN;
476 if (t == NULL)
477 ERR2;
479 if (am == 'P') /* PM - after noon */
481 hour += 12;
482 if (hour == 24)
483 hour = 0;
486 tms.tm_sec = 30; /* seconds after the minute [0, 61] */
487 tms.tm_min = minute; /* minutes after the hour [0, 59] */
488 tms.tm_hour = hour; /* hour since midnight [0, 23] */
489 tms.tm_mday = day; /* day of the month [1, 31] */
490 tms.tm_mon = month - 1; /* months since January [0, 11] */
491 tms.tm_year = year - 1900; /* years since 1900 */
492 tms.tm_isdst = -1;
495 s->st_mtime = mktime (&tms);
496 /* Use resulting time value */
497 s->st_atime = s->st_ctime = s->st_mtime;
499 if (strcmp (t, "<DIR>") == 0)
500 s->st_mode = S_IFDIR;
501 else
503 s->st_mode = S_IFREG;
504 if (sscanf (t, "%lld", &size) != 1)
505 ERR2;
506 s->st_size = (off_t) size;
509 t = strtok (NULL, "");
510 if (t == NULL)
511 ERR2;
512 while (*t == ' ')
513 t++;
514 if (*t == '\0')
515 ERR2;
517 *filename = g_strdup (t);
518 *linkname = NULL;
520 return TRUE;
523 /* --------------------------------------------------------------------------------------------- */
526 +i774.71425,m951188401,/, users
527 +i774.49602,m917883130,r,s79126, jgr_www2.exe
529 starts with +
530 comma separated
531 first character of field is type:
532 i - ?
533 m - modification time
534 / - means directory
535 r - means plain file
536 s - size
537 up - permissions in octal
538 \t - file name follows.
541 static gboolean
542 ftpfs_parse_long_list_EPLF (char *line, struct stat *s, char **filename, char **linkname, int *err)
544 size_t len;
545 const char *b;
546 const char *name = NULL;
547 size_t name_len = 0;
548 off_t size = NO_SIZE;
549 time_t date = NO_DATE;
550 long date_l;
551 long long size_ll;
552 gboolean dir = FALSE;
553 gboolean type_known = FALSE;
554 int perms = -1;
555 const char *scan;
556 ssize_t scan_len;
558 len = strlen (line);
559 b = line;
561 if (len < 2 || b[0] != '+')
562 ERR2;
564 scan = b + 1;
565 scan_len = len - 1;
567 while (scan != NULL && scan_len > 0)
569 const char *comma;
571 switch (*scan)
573 case '\t': /* the rest is file name. */
574 name = scan + 1;
575 name_len = scan_len - 1;
576 scan = NULL;
577 break;
578 case 's':
579 if (sscanf (scan + 1, "%lld", &size_ll) != 1)
580 break;
581 size = size_ll;
582 break;
583 case 'm':
584 if (sscanf (scan + 1, "%ld", &date_l) != 1)
585 break;
586 date = date_l;
587 break;
588 case '/':
589 dir = TRUE;
590 type_known = TRUE;
591 break;
592 case 'r':
593 dir = FALSE;
594 type_known = TRUE;
595 break;
596 case 'i':
597 break;
598 case 'u':
599 if (scan[1] == 'p') /* permissions. */
600 if (sscanf (scan + 2, "%o", (unsigned int *) &perms) != 1)
601 perms = -1;
602 break;
603 default:
604 name = NULL;
605 scan = NULL;
606 break;
608 if (scan == NULL || scan_len == 0)
609 break;
611 comma = (const char *) memchr (scan, ',', scan_len);
612 if (comma == NULL)
613 break;
615 scan_len -= comma + 1 - scan;
616 scan = comma + 1;
619 if (name == NULL || !type_known)
620 ERR2;
622 *filename = g_strndup (name, name_len);
623 *linkname = NULL;
625 if (size != NO_SIZE)
626 s->st_size = size;
627 if (date != NO_DATE)
629 s->st_mtime = date;
630 /* Use resulting time value */
631 s->st_atime = s->st_ctime = s->st_mtime;
633 if (type_known)
634 s->st_mode = dir ? S_IFDIR : S_IFREG;
635 if (perms != -1)
636 s->st_mode |= perms; /* FIXME */
638 return TRUE;
641 /* --------------------------------------------------------------------------------------------- */
643 Type=cdir;Modify=20021029173810;Perm=el;Unique=BP8AAjJufAA; /
644 Type=pdir;Modify=20021029173810;Perm=el;Unique=BP8AAjJufAA; ..
645 Type=dir;Modify=20010118144705;Perm=e;Unique=BP8AAjNufAA; bin
646 Type=dir;Modify=19981021003019;Perm=el;Unique=BP8AAlhufAA; pub
647 Type=file;Size=12303;Modify=19970124132601;Perm=r;Unique=BP8AAo9ufAA; mailserv.FAQ
648 modify=20161215062118;perm=flcdmpe;type=dir;UNIX.group=503;UNIX.mode=0700; directory-name
649 modify=20161213121618;perm=adfrw;size=6369064;type=file;UNIX.group=503;UNIX.mode=0644; file-name
650 modify=20120103123744;perm=adfrw;size=11;type=OS.unix=symlink;UNIX.group=0;UNIX.mode=0777; www
653 static gboolean
654 ftpfs_parse_long_list_MLSD (char *line, struct stat *s, char **filename, char **linkname, int *err)
656 const char *name = NULL;
657 off_t size = NO_SIZE;
658 time_t date = NO_DATE;
659 const char *owner = NULL;
660 const char *group = NULL;
661 filetype type = UNKNOWN;
662 int perms = -1;
663 char *space;
664 char *tok;
666 space = strstr (line, "; ");
667 if (space != NULL)
669 name = space + 2;
670 *space = '\0';
672 else
674 /* NcFTPd does not put a semicolon after last fact, workaround it. */
675 space = strchr (line, ' ');
676 if (space == NULL)
677 ERR2;
678 name = space + 1;
679 *space = '\0';
682 for (tok = strtok (line, ";"); tok != NULL; tok = strtok (NULL, ";"))
684 if (strcasecmp (tok, "Type=cdir") == 0
685 || strcasecmp (tok, "Type=pdir") == 0 || strcasecmp (tok, "Type=dir") == 0)
687 type = DIRECTORY;
688 continue;
690 if (strcasecmp (tok, "Type=file") == 0)
692 type = NORMAL;
693 continue;
695 if (strcasecmp (tok, "Type=OS.unix=symlink") == 0)
697 type = SYMLINK;
698 continue;
700 if (strncasecmp (tok, "Modify=", 7) == 0)
702 date = ftpfs_convert_date (tok + 7);
703 continue;
705 if (strncasecmp (tok, "Size=", 5) == 0)
707 long long size_ll;
709 if (sscanf (tok + 5, "%lld", &size_ll) == 1)
710 size = size_ll;
711 continue;
713 if (strncasecmp (tok, "Perm=", 5) == 0)
715 perms = 0;
716 for (tok += 5; *tok != '\0'; tok++)
718 switch (g_ascii_tolower (*tok))
720 case 'e':
721 perms |= 0111;
722 break;
723 case 'l':
724 perms |= 0444;
725 break;
726 case 'r':
727 perms |= 0444;
728 break;
729 case 'c':
730 perms |= 0200;
731 break;
732 case 'w':
733 perms |= 0200;
734 break;
735 default:
736 break;
739 continue;
741 if (strncasecmp (tok, "UNIX.mode=", 10) == 0)
743 if (sscanf (tok + 10, "%o", (unsigned int *) &perms) != 1)
744 perms = -1;
745 continue;
747 if (strncasecmp (tok, "UNIX.owner=", 11) == 0)
749 owner = tok + 11;
750 continue;
752 if (strncasecmp (tok, "UNIX.group=", 11) == 0)
754 group = tok + 11;
755 continue;
757 if (strncasecmp (tok, "UNIX.uid=", 9) == 0)
759 if (owner == NULL)
760 owner = tok + 9;
761 continue;
763 if (strncasecmp (tok, "UNIX.gid=", 9) == 0)
765 if (group == NULL)
766 group = tok + 9;
767 continue;
770 if (name == NULL || name[0] == '\0' || type == UNKNOWN)
771 ERR2;
773 *filename = g_strdup (name);
774 *linkname = NULL;
776 if (size != NO_SIZE)
777 s->st_size = size;
778 if (date != NO_DATE)
780 s->st_mtime = date;
781 /* Use resulting time value */
782 s->st_atime = s->st_ctime = s->st_mtime;
784 switch (type)
786 case DIRECTORY:
787 s->st_mode = S_IFDIR;
788 break;
789 case SYMLINK:
790 s->st_mode = S_IFLNK;
791 break;
792 case NORMAL:
793 s->st_mode = S_IFREG;
794 break;
795 default:
796 g_assert_not_reached ();
798 if (perms != -1)
799 s->st_mode |= perms; /* FIXME */
800 if (owner != NULL)
801 s->st_uid = ftpfs_get_uid (owner);
802 if (group != NULL)
803 s->st_gid = ftpfs_get_gid (group);
805 return TRUE;
808 /* --------------------------------------------------------------------------------------------- */
811 ASUSER 8192 04/26/05 13:54:16 *DIR dir/
812 ASUSER 8192 04/26/05 13:57:34 *DIR dir1/
813 ASUSER 365255 02/28/01 15:41:40 *STMF readme.txt
814 ASUSER 8489625 03/18/03 09:37:00 *STMF saved.zip
815 ASUSER 365255 02/28/01 15:41:40 *STMF unist.old
818 static gboolean
819 ftpfs_parse_long_list_AS400 (char *line, struct stat *s, char **filename, char **linkname, int *err)
821 char *t;
822 char *user;
823 long long size;
824 int month, day, year, hour, minute, second;
825 struct tm tms;
826 time_t mtime;
827 mode_t type;
828 char *slash;
830 t = FIRST_TOKEN;
831 if (t == NULL)
832 ERR2;
833 user = t;
835 t = NEXT_TOKEN;
836 if (t == NULL)
837 ERR2;
838 if (sscanf (t, "%lld", &size) != 1)
839 ERR2;
841 t = NEXT_TOKEN;
842 if (t == NULL)
843 ERR2;
844 if (sscanf (t, "%2d/%2d/%2d", &month, &day, &year) != 3)
845 ERR2;
846 if (year >= 70)
847 year += 1900;
848 else
849 year += 2000;
851 t = NEXT_TOKEN;
852 if (t == NULL)
853 ERR2;
854 if (sscanf (t, "%2d:%2d:%2d", &hour, &minute, &second) != 3)
855 ERR2;
857 t = NEXT_TOKEN;
858 if (t == NULL)
859 ERR2;
861 tms.tm_sec = second; /* seconds after the minute [0, 61] */
862 tms.tm_min = minute; /* minutes after the hour [0, 59] */
863 tms.tm_hour = hour; /* hour since midnight [0, 23] */
864 tms.tm_mday = day; /* day of the month [1, 31] */
865 tms.tm_mon = month - 1; /* months since January [0, 11] */
866 tms.tm_year = year - 1900; /* years since 1900 */
867 tms.tm_isdst = -1;
868 mtime = mktime (&tms);
870 t = NEXT_TOKEN;
871 if (t == NULL)
872 ERR2;
873 if (strcmp (t, "*DIR") == 0)
874 type = S_IFDIR;
875 else
876 type = S_IFREG;
878 t = strtok (NULL, "");
879 if (t == NULL)
880 ERR2;
881 while (*t == ' ')
882 t++;
883 if (*t == '\0')
884 ERR2;
886 *linkname = NULL;
888 slash = strchr (t, '/');
889 if (slash != NULL)
891 if (slash == t)
892 return FALSE;
894 *slash = '\0';
895 type = S_IFDIR;
896 if (slash[1] != '\0')
898 *filename = g_strdup (t);
899 s->st_mode = type; /* FIXME */
900 return TRUE;
904 *filename = g_strdup (t);
905 s->st_mode = type;
906 s->st_size = (off_t) size;
907 s->st_mtime = mtime;
908 /* Use resulting time value */
909 s->st_atime = s->st_ctime = s->st_mtime;
910 s->st_uid = ftpfs_get_uid (user);
912 return TRUE;
915 /* --------------------------------------------------------------------------------------------- */
918 0 DIR 06-27-96 11:57 PROTOCOL
919 169 11-29-94 09:20 SYSLEVEL.MPT
922 static gboolean
923 ftpfs_parse_long_list_OS2 (char *line, struct stat *s, char **filename, char **linkname, int *err)
925 char *t;
926 long long size;
927 int month, day, year, hour, minute;
928 struct tm tms;
930 t = FIRST_TOKEN;
931 if (t == NULL)
932 ERR2;
934 if (sscanf (t, "%lld", &size) != 1)
935 ERR2;
936 s->st_size = (off_t) size;
938 t = NEXT_TOKEN;
939 if (t == NULL)
940 ERR2;
941 s->st_mode = S_IFREG;
942 if (strcmp (t, "DIR") == 0)
944 s->st_mode = S_IFDIR;
945 t = NEXT_TOKEN;
947 if (t == NULL)
948 ERR2;
951 if (sscanf (t, "%2d-%2d-%2d", &month, &day, &year) != 3)
952 ERR2;
953 if (year >= 70)
954 year += 1900;
955 else
956 year += 2000;
958 t = NEXT_TOKEN;
959 if (t == NULL)
960 ERR2;
961 if (sscanf (t, "%2d:%2d", &hour, &minute) != 3)
962 ERR2;
964 tms.tm_sec = 30; /* seconds after the minute [0, 61] */
965 tms.tm_min = minute; /* minutes after the hour [0, 59] */
966 tms.tm_hour = hour; /* hour since midnight [0, 23] */
967 tms.tm_mday = day; /* day of the month [1, 31] */
968 tms.tm_mon = month - 1; /* months since January [0, 11] */
969 tms.tm_year = year - 1900; /* years since 1900 */
970 tms.tm_isdst = -1;
971 s->st_mtime = mktime (&tms);
972 /* Use resulting time value */
973 s->st_atime = s->st_ctime = s->st_mtime;
975 t = strtok (NULL, "");
976 if (t == NULL)
977 ERR2;
978 while (*t == ' ')
979 t++;
980 if (*t == '\0')
981 ERR2;
982 *filename = g_strdup (t);
983 *linkname = NULL;
985 return TRUE;
988 /* --------------------------------------------------------------------------------------------- */
990 static gboolean
991 ftpfs_parse_long_list_MacWebStar (char *line, struct stat *s, char **filename,
992 char **linkname, int *err)
994 char *t;
995 mode_t type, mode;
996 struct tm date;
997 const char *day_of_month;
998 char *name;
1000 t = FIRST_TOKEN;
1001 if (t == NULL)
1002 ERR2;
1004 if (!vfs_parse_filetype (t, NULL, &type))
1005 ERR2;
1007 s->st_mode = type;
1009 if (!vfs_parse_fileperms (t + 1, NULL, &mode))
1010 ERR2;
1011 /* permissions are meaningless here. */
1013 /* "folder" or 0 */
1014 t = NEXT_TOKEN;
1015 if (t == NULL)
1016 ERR2;
1018 if (strcmp (t, "folder") != 0)
1020 long long size;
1022 /* size? */
1023 t = NEXT_TOKEN;
1024 if (t == NULL)
1025 ERR2;
1026 /* size */
1027 t = NEXT_TOKEN;
1028 if (t == NULL)
1029 ERR2;
1030 if (!isdigit ((unsigned char) *t))
1031 ERR2;
1033 if (sscanf (t, "%lld", &size) == 1)
1034 s->st_size = (off_t) size;
1036 else
1038 /* ?? */
1039 t = NEXT_TOKEN;
1040 if (t == NULL)
1041 ERR2;
1044 /* month */
1045 t = NEXT_TOKEN;
1046 if (t == NULL)
1047 ERR2;
1049 memset (&date, 0, sizeof (date));
1051 if (!vfs_parse_month (t, &date))
1052 ERR2;
1054 day_of_month = NEXT_TOKEN;
1055 if (day_of_month == NULL)
1056 ERR2;
1058 date.tm_mday = atoi (day_of_month);
1060 /* time or year */
1061 t = NEXT_TOKEN;
1062 if (t == NULL)
1063 ERR2;
1064 if (!parse_year_or_time (t, &date.tm_year, &date.tm_hour, &date.tm_min))
1065 ERR2;
1067 date.tm_isdst = -1;
1068 date.tm_sec = 30;
1069 if (date.tm_year == -1)
1070 date.tm_year = guess_year (date.tm_mon, date.tm_mday, date.tm_hour, date.tm_min) - 1900;
1071 else
1072 date.tm_hour = 12;
1074 s->st_mtime = mktime (&date);
1075 /* Use resulting time value */
1076 s->st_atime = s->st_ctime = s->st_mtime;
1078 name = strtok (NULL, "");
1079 if (name == NULL)
1080 ERR2;
1082 /* no symlinks on Mac, but anyway. */
1083 if (!S_ISLNK (s->st_mode))
1084 *linkname = NULL;
1085 else
1087 char *arrow;
1089 for (arrow = name; (arrow = strstr (arrow, " -> ")) != NULL; arrow++)
1090 if (arrow != name && arrow[4] != '\0')
1092 *arrow = '\0';
1093 *linkname = g_strdup (arrow + 4);
1094 break;
1098 *filename = g_strdup (name);
1100 return TRUE;
1103 /* --------------------------------------------------------------------------------------------- */
1104 /*** public functions ****************************************************************************/
1105 /* --------------------------------------------------------------------------------------------- */
1107 GSList *
1108 ftpfs_parse_long_list (struct vfs_class *me, struct vfs_s_inode *dir, GSList *buf, int *err_ret)
1110 int err[number_of_parsers];
1111 GSList *set[number_of_parsers]; /* arrays of struct vfs_s_entry */
1112 size_t i;
1113 GSList *bufp;
1114 ftpfs_line_parser guessed_parser = NULL;
1115 GSList **the_set = NULL;
1116 int *the_err = NULL;
1117 int *best_err1 = &err[0];
1118 int *best_err2 = &err[1];
1120 ftpfs_init_time ();
1122 if (err_ret != NULL)
1123 *err_ret = 0;
1125 memset (&err, 0, sizeof (err));
1126 memset (&set, 0, sizeof (set));
1128 for (bufp = buf; bufp != NULL; bufp = g_slist_next (bufp))
1130 char *b = (char *) bufp->data;
1131 size_t blen;
1133 blen = strlen (b);
1135 if (b[blen - 1] == '\r')
1137 b[blen - 1] = '\0';
1138 blen--;
1141 if (blen == 0)
1142 continue;
1144 if (guessed_parser == NULL)
1146 for (i = 0; i < number_of_parsers; i++)
1148 struct vfs_s_entry *info;
1149 gboolean ok;
1150 char *tmp_line;
1151 int nlink;
1153 /* parser can clobber the line - work on a copy */
1154 tmp_line = g_strndup (b, blen);
1156 info = vfs_s_generate_entry (me, NULL, dir, 0);
1157 nlink = info->ino->st.st_nlink;
1158 ok = (*line_parsers[i]) (tmp_line, &info->ino->st, &info->name,
1159 &info->ino->linkname, &err[i]);
1160 if (ok && strchr (info->name, '/') == NULL)
1162 info->ino->st.st_nlink = nlink; /* Ouch, we need to preserve our counts :-( */
1163 set[i] = g_slist_prepend (set[i], info);
1165 else
1166 vfs_s_free_entry (me, info);
1168 g_free (tmp_line);
1170 if (*best_err1 > err[i])
1171 best_err1 = &err[i];
1172 if (*best_err2 > err[i] && best_err1 != &err[i])
1173 best_err2 = &err[i];
1175 if (*best_err1 > 16)
1176 goto leave; /* too many errors with best parser. */
1179 if (*best_err2 > (*best_err1 + 1) * 16)
1181 i = (size_t) (best_err1 - err);
1182 guessed_parser = line_parsers[i];
1183 the_set = &set[i];
1184 the_err = &err[i];
1187 else
1189 struct vfs_s_entry *info;
1190 gboolean ok;
1191 char *tmp_line;
1192 int nlink;
1194 /* parser can clobber the line - work on a copy */
1195 tmp_line = g_strndup (b, blen);
1197 info = vfs_s_generate_entry (me, NULL, dir, 0);
1198 nlink = info->ino->st.st_nlink;
1199 ok = guessed_parser (tmp_line, &info->ino->st, &info->name, &info->ino->linkname,
1200 the_err);
1201 if (ok && strchr (info->name, '/') == NULL)
1203 info->ino->st.st_nlink = nlink; /* Ouch, we need to preserve our counts :-( */
1204 *the_set = g_slist_prepend (*the_set, info);
1206 else
1207 vfs_s_free_entry (me, info);
1209 g_free (tmp_line);
1213 if (the_set == NULL)
1215 i = best_err1 - err;
1216 the_set = &set[i];
1217 the_err = &err[i];
1220 leave:
1221 for (i = 0; i < number_of_parsers; i++)
1222 if (&set[i] != the_set)
1224 for (bufp = set[i]; bufp != NULL; bufp = g_slist_next (bufp))
1225 vfs_s_free_entry (me, VFS_ENTRY (bufp->data));
1227 g_slist_free (set[i]);
1230 if (err_ret != NULL && the_err != NULL)
1231 *err_ret = *the_err;
1233 return the_set != NULL ? g_slist_reverse (*the_set) : NULL;
1236 /* --------------------------------------------------------------------------------------------- */