root/src/vfs/ftpfs/ftpfs_parse_ls.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. ftpfs_get_uid
  2. ftpfs_get_gid
  3. ftpfs_init_time
  4. guess_year
  5. parse_year_or_time
  6. mktime_from_utc
  7. ftpfs_convert_date
  8. parse_ls_line
  9. ftpfs_parse_long_list_UNIX
  10. ftpfs_parse_long_list_NT
  11. ftpfs_parse_long_list_EPLF
  12. ftpfs_parse_long_list_MLSD
  13. ftpfs_parse_long_list_AS400
  14. ftpfs_parse_long_list_OS2
  15. ftpfs_parse_long_list_MacWebStar
  16. ftpfs_parse_long_list

   1 /*
   2    Virtual File System: FTP file system
   3 
   4    Copyright (C) 2015-2022
   5    The Free Software Foundation, Inc.
   6 
   7    Written by: Andrew Borodin <aborodin@vmail.ru>, 2013
   8 
   9    This file is part of the Midnight Commander.
  10 
  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.
  15 
  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.
  20 
  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/>.
  23  */
  24 
  25 /** \file
  26  *  \brief Source: Virtual File System: FTP file system
  27  *  \author Andrew Borodin
  28  *  \date 2015
  29  *
  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)
  33  */
  34 
  35 #include <config.h>
  36 
  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>
  45 
  46 #include "lib/global.h"
  47 
  48 #include "lib/vfs/vfs.h"
  49 #include "lib/vfs/utilvfs.h"
  50 
  51 #include "ftpfs.h"
  52 
  53 /*** global variables ****************************************************************************/
  54 
  55 /*** file scope macro definitions ****************************************************************/
  56 
  57 #define number_of_parsers 7
  58 
  59 #define NO_SIZE     ((off_t) (-1L))
  60 #define NO_DATE     ((time_t) (-1L))
  61 
  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)
  66 
  67 #define ERR2 do { (*err)++; return FALSE; } while (FALSE)
  68 
  69 /*** file scope type declarations ****************************************************************/
  70 
  71 typedef enum
  72 {
  73     UNKNOWN = 0,
  74     DIRECTORY,
  75     SYMLINK,
  76     NORMAL
  77 } filetype;
  78 
  79 typedef gboolean (*ftpfs_line_parser) (char *line, struct stat * s, char **filename,
  80                                        char **linkname, int *err);
  81 
  82 /* formard declarations */
  83 static gboolean ftpfs_parse_long_list_UNIX (char *line, struct stat *s, char **filename,
  84                                             char **linkname, int *err);
  85 static gboolean ftpfs_parse_long_list_NT (char *line, struct stat *s, char **filename,
  86                                           char **linkname, int *err);
  87 static gboolean ftpfs_parse_long_list_EPLF (char *line, struct stat *s, char **filename,
  88                                             char **linkname, int *err);
  89 static gboolean ftpfs_parse_long_list_MLSD (char *line, struct stat *s, char **filename,
  90                                             char **linkname, int *err);
  91 static gboolean ftpfs_parse_long_list_AS400 (char *line, struct stat *s, char **filename,
  92                                              char **linkname, int *err);
  93 static gboolean ftpfs_parse_long_list_OS2 (char *line, struct stat *s, char **filename,
  94                                            char **linkname, int *err);
  95 static gboolean ftpfs_parse_long_list_MacWebStar (char *line, struct stat *s, char **filename,
  96                                                   char **linkname, int *err);
  97 
  98 /*** file scope variables ************************************************************************/
  99 
 100 static time_t rawnow;
 101 static struct tm now;
 102 
 103 static ftpfs_line_parser line_parsers[number_of_parsers] = {
 104     ftpfs_parse_long_list_UNIX,
 105     ftpfs_parse_long_list_NT,
 106     ftpfs_parse_long_list_EPLF,
 107     ftpfs_parse_long_list_MLSD,
 108     ftpfs_parse_long_list_AS400,
 109     ftpfs_parse_long_list_OS2,
 110     ftpfs_parse_long_list_MacWebStar
 111 };
 112 
 113 /* --------------------------------------------------------------------------------------------- */
 114 /*** file scope functions ************************************************************************/
 115 /* --------------------------------------------------------------------------------------------- */
 116 
 117 static inline uid_t
 118 ftpfs_get_uid (const char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 119 {
 120     uid_t u;
 121 
 122     if (*s < '0' || *s > '9')
 123         u = vfs_finduid (s);
 124     else
 125         u = (uid_t) atol (s);
 126 
 127     return u;
 128 }
 129 
 130 /* --------------------------------------------------------------------------------------------- */
 131 
 132 static inline gid_t
 133 ftpfs_get_gid (const char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 134 {
 135     gid_t g;
 136 
 137     if (*s < '0' || *s > '9')
 138         g = vfs_findgid (s);
 139     else
 140         g = (gid_t) atol (s);
 141 
 142     return g;
 143 }
 144 
 145 /* --------------------------------------------------------------------------------------------- */
 146 
 147 static void
 148 ftpfs_init_time (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 149 {
 150     time (&rawnow);
 151     now = *localtime (&rawnow);
 152 }
 153 
 154 /* --------------------------------------------------------------------------------------------- */
 155 
 156 static int
 157 guess_year (int month, int day, int hour, int minute)
     /* [previous][next][first][last][top][bottom][index][help]  */
 158 {
 159     int year;
 160 
 161     (void) hour;
 162     (void) minute;
 163 
 164     year = now.tm_year + 1900;
 165 
 166     if (month * 32 + day > now.tm_mon * 32 + now.tm_mday + 6)
 167         year--;
 168 
 169     return year;
 170 }
 171 
 172 /* --------------------------------------------------------------------------------------------- */
 173 
 174 static gboolean
 175 parse_year_or_time (const char *year_or_time, int *year, int *hour, int *minute)
     /* [previous][next][first][last][top][bottom][index][help]  */
 176 {
 177     if (year_or_time[2] == ':')
 178     {
 179         if (sscanf (year_or_time, "%2d:%2d", hour, minute) != 2)
 180             return FALSE;
 181 
 182         *year = -1;
 183     }
 184     else
 185     {
 186         if (sscanf (year_or_time, "%d", year) != 1)
 187             return FALSE;
 188 
 189         *hour = *minute = 0;
 190     }
 191 
 192     return TRUE;
 193 }
 194 
 195 /* --------------------------------------------------------------------------------------------- */
 196 
 197 /* Converts struct tm to time_t, assuming the data in tm is UTC rather
 198    than local timezone (mktime assumes the latter).
 199 
 200    Contributed by Roger Beeman <beeman@cisco.com>, with the help of
 201    Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO.  */
 202 static time_t
 203 mktime_from_utc (const struct tm *t)
     /* [previous][next][first][last][top][bottom][index][help]  */
 204 {
 205     struct tm tc;
 206     time_t tl, tb;
 207 
 208     memcpy (&tc, t, sizeof (struct tm));
 209 
 210     /* UTC times are never DST; if we say -1, we'll introduce odd localtime-
 211      * dependant errors. */
 212 
 213     tc.tm_isdst = 0;
 214 
 215     tl = mktime (&tc);
 216     if (tl == -1)
 217         return (-1);
 218 
 219     tb = mktime (gmtime (&tl));
 220 
 221     return (tl <= tb ? (tl + (tl - tb)) : (tl - (tb - tl)));
 222 }
 223 
 224 /* --------------------------------------------------------------------------------------------- */
 225 
 226 static time_t
 227 ftpfs_convert_date (const char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 228 {
 229     struct tm tm;
 230     int year, month, day, hour, minute, second;
 231     int skip = 0;
 232     int n;
 233 
 234     memset (&tm, 0, sizeof (tm));
 235 
 236     n = sscanf (s, "%4d%n", &year, &skip);
 237 
 238     /* try to workaround server's y2k bug *
 239      * I hope in the next 300 years the y2k bug will be finally fixed :) */
 240     if (n == 1 && year >= 1910 && year <= 1930)
 241     {
 242         n = sscanf (s, "%5d%n", &year, &skip);
 243         year = year - 19100 + 2000;
 244     }
 245 
 246     if (n != 1)
 247         return NO_DATE;
 248 
 249     n = sscanf (s + skip, "%2d%2d%2d%2d%2d", &month, &day, &hour, &minute, &second);
 250 
 251     if (n != 5)
 252         return NO_DATE;
 253 
 254     tm.tm_year = year - 1900;
 255     tm.tm_mon = month - 1;
 256     tm.tm_mday = day;
 257     tm.tm_hour = hour;
 258     tm.tm_min = minute;
 259     tm.tm_sec = second;
 260 
 261     return mktime_from_utc (&tm);
 262 }
 263 
 264 /* --------------------------------------------------------------------------------------------- */
 265 
 266 /*
 267    -rwxr-xr-x   1 lav      root         4771 Sep 12  1996 install-sh
 268    -rw-r--r--   1 lav      root         1349 Feb  2 14:10 lftp.lsm
 269    drwxr-xr-x   4 lav      root         1024 Feb 22 15:32 lib
 270    lrwxrwxrwx   1 lav      root           33 Feb 14 17:45 ltconfig -> /usr/share/libtool/ltconfig
 271 
 272    NOTE: group may be missing.
 273  */
 274 
 275 static gboolean
 276 parse_ls_line (char *line, struct stat *s, char **filename, char **linkname)
     /* [previous][next][first][last][top][bottom][index][help]  */
 277 {
 278     char *next = NULL;
 279     char *t;
 280     mode_t type, mode = 0;
 281     char *group_or_size;
 282     struct tm date;
 283     const char *day_of_month;
 284     gboolean year_anomaly = FALSE;
 285     char *name;
 286 
 287     /* parse perms */
 288     t = FIRST_TOKEN_R;
 289     if (t == NULL)
 290         return FALSE;
 291 
 292     if (!vfs_parse_filetype (t, NULL, &type))
 293         return FALSE;
 294 
 295     if (vfs_parse_fileperms (t + 1, NULL, &mode))
 296         mode |= type;
 297 
 298     s->st_mode = mode;
 299 
 300     /* link count */
 301     t = NEXT_TOKEN_R;
 302     if (t == NULL)
 303         return FALSE;
 304     s->st_nlink = atol (t);
 305 
 306     /* user */
 307     t = NEXT_TOKEN_R;
 308     if (t == NULL)
 309         return FALSE;
 310 
 311     s->st_uid = ftpfs_get_uid (t);
 312 
 313     /* group or size */
 314     group_or_size = NEXT_TOKEN_R;
 315 
 316     /* size or month */
 317     t = NEXT_TOKEN_R;
 318     if (t == NULL)
 319         return FALSE;
 320     if (isdigit ((unsigned char) *t))
 321     {
 322         /* it's size, so the previous was group: */
 323         long long size;
 324         int n;
 325 
 326         s->st_gid = ftpfs_get_gid (t);
 327 
 328         if (sscanf (t, "%lld%n", &size, &n) == 1 && t[n] == '\0')
 329             s->st_size = (off_t) size;
 330         t = NEXT_TOKEN_R;
 331         if (t == NULL)
 332             return FALSE;
 333     }
 334     else
 335     {
 336         /*  it was month, so the previous was size: */
 337         long long size;
 338         int n;
 339 
 340         if (sscanf (group_or_size, "%lld%n", &size, &n) == 1 && group_or_size[n] == '\0')
 341             s->st_size = (off_t) size;
 342     }
 343 
 344 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
 345     s->st_blksize = 512;
 346 #endif
 347 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
 348     s->st_blocks = (s->st_size + 511) / 512;
 349 #endif
 350 
 351     memset (&date, 0, sizeof (date));
 352 
 353     if (!vfs_parse_month (t, &date))
 354         date.tm_mon = 0;
 355 
 356     day_of_month = NEXT_TOKEN_R;
 357     if (day_of_month == NULL)
 358         return FALSE;
 359     date.tm_mday = atoi (day_of_month);
 360 
 361     /* time or year */
 362     t = NEXT_TOKEN_R;
 363     if (t == NULL)
 364         return FALSE;
 365     date.tm_isdst = -1;
 366     date.tm_hour = date.tm_min = 0;
 367     date.tm_sec = 30;
 368 
 369     if (sscanf (t, "%2d:%2d", &date.tm_hour, &date.tm_min) == 2)
 370         date.tm_year = guess_year (date.tm_mon, date.tm_mday, date.tm_hour, date.tm_min) - 1900;
 371     else
 372     {
 373         if (day_of_month + strlen (day_of_month) + 1 == t)
 374             year_anomaly = TRUE;
 375         date.tm_year = atoi (t) - 1900;
 376         /* We don't know the hour.  Set it to something other than 0, or
 377          * DST -1 will end up changing the date. */
 378         date.tm_hour = 12;
 379         date.tm_min = 0;
 380         date.tm_sec = 0;
 381     }
 382 
 383     s->st_mtime = mktime (&date);
 384     /* Use resulting time value */
 385     s->st_atime = s->st_ctime = s->st_mtime;
 386 
 387     name = strtok_r (NULL, "", &next);
 388     if (name == NULL)
 389         return FALSE;
 390 
 391     /* there are ls which output extra space after year. */
 392     if (year_anomaly && *name == ' ')
 393         name++;
 394 
 395     if (!S_ISLNK (s->st_mode))
 396         *linkname = NULL;
 397     else
 398     {
 399         char *arrow;
 400 
 401         for (arrow = name; (arrow = strstr (arrow, " -> ")) != NULL; arrow++)
 402             if (arrow != name && arrow[4] != '\0')
 403             {
 404                 *arrow = '\0';
 405                 *linkname = g_strdup (arrow + 4);
 406                 break;
 407             }
 408     }
 409 
 410     *filename = g_strdup (name);
 411 
 412     return TRUE;
 413 }
 414 
 415 /* --------------------------------------------------------------------------------------------- */
 416 
 417 static gboolean
 418 ftpfs_parse_long_list_UNIX (char *line, struct stat *s, char **filename, char **linkname, int *err)
     /* [previous][next][first][last][top][bottom][index][help]  */
 419 {
 420     int tmp;
 421     gboolean ret;
 422 
 423     if (sscanf (line, "total %d", &tmp) == 1)
 424         return FALSE;
 425 
 426     if (strncasecmp (line, "Status of ", 10) == 0)
 427         return FALSE;           /* STAT output. */
 428     if (strchr ("bcpsD", line[0]) != NULL)      /* block, char, pipe, socket, Door. */
 429         return FALSE;
 430 
 431     ret = parse_ls_line (line, s, filename, linkname);
 432     if (!ret)
 433         (*err)++;
 434 
 435     return ret;
 436 }
 437 
 438 /* --------------------------------------------------------------------------------------------- */
 439 
 440 /*
 441    07-13-98  09:06PM       <DIR>          aix
 442    07-13-98  09:06PM       <DIR>          hpux
 443    07-13-98  09:06PM       <DIR>          linux
 444    07-13-98  09:06PM       <DIR>          ncr
 445    07-13-98  09:06PM       <DIR>          solaris
 446    03-18-98  06:01AM              2109440 nlxb318e.tar
 447    07-02-98  11:17AM                13844 Whatsnew.txt
 448  */
 449 
 450 static gboolean
 451 ftpfs_parse_long_list_NT (char *line, struct stat *s, char **filename, char **linkname, int *err)
     /* [previous][next][first][last][top][bottom][index][help]  */
 452 {
 453     char *t;
 454     int month, day, year, hour, minute;
 455     char am;
 456     struct tm tms;
 457     long long size;
 458 
 459     t = FIRST_TOKEN;
 460     if (t == NULL)
 461         ERR2;
 462     if (sscanf (t, "%2d-%2d-%2d", &month, &day, &year) != 3)
 463         ERR2;
 464     if (year >= 70)
 465         year += 1900;
 466     else
 467         year += 2000;
 468 
 469     t = NEXT_TOKEN;
 470     if (t == NULL)
 471         ERR2;
 472     am = 'A';                   /* AM/PM is optional */
 473     if (sscanf (t, "%2d:%2d%c", &hour, &minute, &am) < 2)
 474         ERR2;
 475 
 476     t = NEXT_TOKEN;
 477     if (t == NULL)
 478         ERR2;
 479 
 480     if (am == 'P')              /* PM - after noon */
 481     {
 482         hour += 12;
 483         if (hour == 24)
 484             hour = 0;
 485     }
 486 
 487     tms.tm_sec = 30;            /* seconds after the minute [0, 61]  */
 488     tms.tm_min = minute;        /* minutes after the hour [0, 59] */
 489     tms.tm_hour = hour;         /* hour since midnight [0, 23] */
 490     tms.tm_mday = day;          /* day of the month [1, 31] */
 491     tms.tm_mon = month - 1;     /* months since January [0, 11] */
 492     tms.tm_year = year - 1900;  /* years since 1900 */
 493     tms.tm_isdst = -1;
 494 
 495 
 496     s->st_mtime = mktime (&tms);
 497     /* Use resulting time value */
 498     s->st_atime = s->st_ctime = s->st_mtime;
 499 
 500     if (strcmp (t, "<DIR>") == 0)
 501         s->st_mode = S_IFDIR;
 502     else
 503     {
 504         s->st_mode = S_IFREG;
 505         if (sscanf (t, "%lld", &size) != 1)
 506             ERR2;
 507         s->st_size = (off_t) size;
 508     }
 509 
 510     t = strtok (NULL, "");
 511     if (t == NULL)
 512         ERR2;
 513     while (*t == ' ')
 514         t++;
 515     if (*t == '\0')
 516         ERR2;
 517 
 518     *filename = g_strdup (t);
 519     *linkname = NULL;
 520 
 521     return TRUE;
 522 }
 523 
 524 /* --------------------------------------------------------------------------------------------- */
 525 
 526 /*
 527    +i774.71425,m951188401,/,       users
 528    +i774.49602,m917883130,r,s79126,        jgr_www2.exe
 529 
 530    starts with +
 531    comma separated
 532    first character of field is type:
 533    i - ?
 534    m - modification time
 535    / - means directory
 536    r - means plain file
 537    s - size
 538    up - permissions in octal
 539    \t - file name follows.
 540  */
 541 
 542 static gboolean
 543 ftpfs_parse_long_list_EPLF (char *line, struct stat *s, char **filename, char **linkname, int *err)
     /* [previous][next][first][last][top][bottom][index][help]  */
 544 {
 545     size_t len;
 546     const char *b;
 547     const char *name = NULL;
 548     size_t name_len = 0;
 549     off_t size = NO_SIZE;
 550     time_t date = NO_DATE;
 551     long date_l;
 552     long long size_ll;
 553     gboolean dir = FALSE;
 554     gboolean type_known = FALSE;
 555     int perms = -1;
 556     const char *scan;
 557     ssize_t scan_len;
 558 
 559     len = strlen (line);
 560     b = line;
 561 
 562     if (len < 2 || b[0] != '+')
 563         ERR2;
 564 
 565     scan = b + 1;
 566     scan_len = len - 1;
 567 
 568     while (scan != NULL && scan_len > 0)
 569     {
 570         const char *comma;
 571 
 572         switch (*scan)
 573         {
 574         case '\t':             /* the rest is file name. */
 575             name = scan + 1;
 576             name_len = scan_len - 1;
 577             scan = NULL;
 578             break;
 579         case 's':
 580             if (sscanf (scan + 1, "%lld", &size_ll) != 1)
 581                 break;
 582             size = size_ll;
 583             break;
 584         case 'm':
 585             if (sscanf (scan + 1, "%ld", &date_l) != 1)
 586                 break;
 587             date = date_l;
 588             break;
 589         case '/':
 590             dir = TRUE;
 591             type_known = TRUE;
 592             break;
 593         case 'r':
 594             dir = FALSE;
 595             type_known = TRUE;
 596             break;
 597         case 'i':
 598             break;
 599         case 'u':
 600             if (scan[1] == 'p') /* permissions. */
 601                 if (sscanf (scan + 2, "%o", (unsigned int *) &perms) != 1)
 602                     perms = -1;
 603             break;
 604         default:
 605             name = NULL;
 606             scan = NULL;
 607             break;
 608         }
 609         if (scan == NULL || scan_len == 0)
 610             break;
 611 
 612         comma = (const char *) memchr (scan, ',', scan_len);
 613         if (comma == NULL)
 614             break;
 615 
 616         scan_len -= comma + 1 - scan;
 617         scan = comma + 1;
 618     }
 619 
 620     if (name == NULL || !type_known)
 621         ERR2;
 622 
 623     *filename = g_strndup (name, name_len);
 624     *linkname = NULL;
 625 
 626     if (size != NO_SIZE)
 627         s->st_size = size;
 628     if (date != NO_DATE)
 629     {
 630         s->st_mtime = date;
 631         /* Use resulting time value */
 632         s->st_atime = s->st_ctime = s->st_mtime;
 633     }
 634     if (type_known)
 635         s->st_mode = dir ? S_IFDIR : S_IFREG;
 636     if (perms != -1)
 637         s->st_mode |= perms;    /* FIXME */
 638 
 639     return TRUE;
 640 }
 641 
 642 /* --------------------------------------------------------------------------------------------- */
 643 /*
 644    Type=cdir;Modify=20021029173810;Perm=el;Unique=BP8AAjJufAA; /
 645    Type=pdir;Modify=20021029173810;Perm=el;Unique=BP8AAjJufAA; ..
 646    Type=dir;Modify=20010118144705;Perm=e;Unique=BP8AAjNufAA; bin
 647    Type=dir;Modify=19981021003019;Perm=el;Unique=BP8AAlhufAA; pub
 648    Type=file;Size=12303;Modify=19970124132601;Perm=r;Unique=BP8AAo9ufAA; mailserv.FAQ
 649    modify=20161215062118;perm=flcdmpe;type=dir;UNIX.group=503;UNIX.mode=0700; directory-name
 650    modify=20161213121618;perm=adfrw;size=6369064;type=file;UNIX.group=503;UNIX.mode=0644; file-name
 651    modify=20120103123744;perm=adfrw;size=11;type=OS.unix=symlink;UNIX.group=0;UNIX.mode=0777; www
 652  */
 653 
 654 static gboolean
 655 ftpfs_parse_long_list_MLSD (char *line, struct stat *s, char **filename, char **linkname, int *err)
     /* [previous][next][first][last][top][bottom][index][help]  */
 656 {
 657     const char *name = NULL;
 658     off_t size = NO_SIZE;
 659     time_t date = NO_DATE;
 660     const char *owner = NULL;
 661     const char *group = NULL;
 662     filetype type = UNKNOWN;
 663     int perms = -1;
 664     char *space;
 665     char *tok;
 666 
 667     space = strstr (line, "; ");
 668     if (space != NULL)
 669     {
 670         name = space + 2;
 671         *space = '\0';
 672     }
 673     else
 674     {
 675         /* NcFTPd does not put a semicolon after last fact, workaround it. */
 676         space = strchr (line, ' ');
 677         if (space == NULL)
 678             ERR2;
 679         name = space + 1;
 680         *space = '\0';
 681     }
 682 
 683     for (tok = strtok (line, ";"); tok != NULL; tok = strtok (NULL, ";"))
 684     {
 685         if (strcasecmp (tok, "Type=cdir") == 0
 686             || strcasecmp (tok, "Type=pdir") == 0 || strcasecmp (tok, "Type=dir") == 0)
 687         {
 688             type = DIRECTORY;
 689             continue;
 690         }
 691         if (strcasecmp (tok, "Type=file") == 0)
 692         {
 693             type = NORMAL;
 694             continue;
 695         }
 696         if (strcasecmp (tok, "Type=OS.unix=symlink") == 0)
 697         {
 698             type = SYMLINK;
 699             continue;
 700         }
 701         if (strncasecmp (tok, "Modify=", 7) == 0)
 702         {
 703             date = ftpfs_convert_date (tok + 7);
 704             continue;
 705         }
 706         if (strncasecmp (tok, "Size=", 5) == 0)
 707         {
 708             long long size_ll;
 709 
 710             if (sscanf (tok + 5, "%lld", &size_ll) == 1)
 711                 size = size_ll;
 712             continue;
 713         }
 714         if (strncasecmp (tok, "Perm=", 5) == 0)
 715         {
 716             perms = 0;
 717             for (tok += 5; *tok != '\0'; tok++)
 718             {
 719                 switch (g_ascii_tolower (*tok))
 720                 {
 721                 case 'e':
 722                     perms |= 0111;
 723                     break;
 724                 case 'l':
 725                     perms |= 0444;
 726                     break;
 727                 case 'r':
 728                     perms |= 0444;
 729                     break;
 730                 case 'c':
 731                     perms |= 0200;
 732                     break;
 733                 case 'w':
 734                     perms |= 0200;
 735                     break;
 736                 default:
 737                     break;
 738                 }
 739             }
 740             continue;
 741         }
 742         if (strncasecmp (tok, "UNIX.mode=", 10) == 0)
 743         {
 744             if (sscanf (tok + 10, "%o", (unsigned int *) &perms) != 1)
 745                 perms = -1;
 746             continue;
 747         }
 748         if (strncasecmp (tok, "UNIX.owner=", 11) == 0)
 749         {
 750             owner = tok + 11;
 751             continue;
 752         }
 753         if (strncasecmp (tok, "UNIX.group=", 11) == 0)
 754         {
 755             group = tok + 11;
 756             continue;
 757         }
 758         if (strncasecmp (tok, "UNIX.uid=", 9) == 0)
 759         {
 760             if (owner == NULL)
 761                 owner = tok + 9;
 762             continue;
 763         }
 764         if (strncasecmp (tok, "UNIX.gid=", 9) == 0)
 765         {
 766             if (group == NULL)
 767                 group = tok + 9;
 768             continue;
 769         }
 770     }
 771     if (name == NULL || name[0] == '\0' || type == UNKNOWN)
 772         ERR2;
 773 
 774     *filename = g_strdup (name);
 775     *linkname = NULL;
 776 
 777     if (size != NO_SIZE)
 778         s->st_size = size;
 779     if (date != NO_DATE)
 780     {
 781         s->st_mtime = date;
 782         /* Use resulting time value */
 783         s->st_atime = s->st_ctime = s->st_mtime;
 784     }
 785     switch (type)
 786     {
 787     case DIRECTORY:
 788         s->st_mode = S_IFDIR;
 789         break;
 790     case SYMLINK:
 791         s->st_mode = S_IFLNK;
 792         break;
 793     case NORMAL:
 794         s->st_mode = S_IFREG;
 795         break;
 796     default:
 797         g_assert_not_reached ();
 798     }
 799     if (perms != -1)
 800         s->st_mode |= perms;    /* FIXME */
 801     if (owner != NULL)
 802         s->st_uid = ftpfs_get_uid (owner);
 803     if (group != NULL)
 804         s->st_uid = ftpfs_get_gid (group);
 805 
 806     return TRUE;
 807 }
 808 
 809 /* --------------------------------------------------------------------------------------------- */
 810 
 811 /*
 812    ASUSER          8192 04/26/05 13:54:16 *DIR       dir/
 813    ASUSER          8192 04/26/05 13:57:34 *DIR       dir1/
 814    ASUSER        365255 02/28/01 15:41:40 *STMF      readme.txt
 815    ASUSER       8489625 03/18/03 09:37:00 *STMF      saved.zip
 816    ASUSER        365255 02/28/01 15:41:40 *STMF      unist.old
 817  */
 818 
 819 static gboolean
 820 ftpfs_parse_long_list_AS400 (char *line, struct stat *s, char **filename, char **linkname, int *err)
     /* [previous][next][first][last][top][bottom][index][help]  */
 821 {
 822     char *t;
 823     char *user;
 824     long long size;
 825     int month, day, year, hour, minute, second;
 826     struct tm tms;
 827     time_t mtime;
 828     mode_t type;
 829     char *slash;
 830 
 831     t = FIRST_TOKEN;
 832     if (t == NULL)
 833         ERR2;
 834     user = t;
 835 
 836     t = NEXT_TOKEN;
 837     if (t == NULL)
 838         ERR2;
 839     if (sscanf (t, "%lld", &size) != 1)
 840         ERR2;
 841 
 842     t = NEXT_TOKEN;
 843     if (t == NULL)
 844         ERR2;
 845     if (sscanf (t, "%2d/%2d/%2d", &month, &day, &year) != 3)
 846         ERR2;
 847     if (year >= 70)
 848         year += 1900;
 849     else
 850         year += 2000;
 851 
 852     t = NEXT_TOKEN;
 853     if (t == NULL)
 854         ERR2;
 855     if (sscanf (t, "%2d:%2d:%2d", &hour, &minute, &second) != 3)
 856         ERR2;
 857 
 858     t = NEXT_TOKEN;
 859     if (t == NULL)
 860         ERR2;
 861 
 862     tms.tm_sec = second;        /* seconds after the minute [0, 61]  */
 863     tms.tm_min = minute;        /* minutes after the hour [0, 59] */
 864     tms.tm_hour = hour;         /* hour since midnight [0, 23] */
 865     tms.tm_mday = day;          /* day of the month [1, 31] */
 866     tms.tm_mon = month - 1;     /* months since January [0, 11] */
 867     tms.tm_year = year - 1900;  /* years since 1900 */
 868     tms.tm_isdst = -1;
 869     mtime = mktime (&tms);
 870 
 871     t = NEXT_TOKEN;
 872     if (t == NULL)
 873         ERR2;
 874     if (strcmp (t, "*DIR") == 0)
 875         type = S_IFDIR;
 876     else
 877         type = S_IFREG;
 878 
 879     t = strtok (NULL, "");
 880     if (t == NULL)
 881         ERR2;
 882     while (*t == ' ')
 883         t++;
 884     if (*t == '\0')
 885         ERR2;
 886 
 887     *linkname = NULL;
 888 
 889     slash = strchr (t, '/');
 890     if (slash != NULL)
 891     {
 892         if (slash == t)
 893             return FALSE;
 894 
 895         *slash = '\0';
 896         type = S_IFDIR;
 897         if (slash[1] != '\0')
 898         {
 899             *filename = g_strdup (t);
 900             s->st_mode = type;  /* FIXME */
 901             return TRUE;
 902         }
 903     }
 904 
 905     *filename = g_strdup (t);
 906     s->st_mode = type;
 907     s->st_size = (off_t) size;
 908     s->st_mtime = mtime;
 909     /* Use resulting time value */
 910     s->st_atime = s->st_ctime = s->st_mtime;
 911     s->st_uid = ftpfs_get_uid (user);
 912 
 913     return TRUE;
 914 }
 915 
 916 /* --------------------------------------------------------------------------------------------- */
 917 
 918 /*
 919    0          DIR  06-27-96  11:57  PROTOCOL
 920    169               11-29-94  09:20  SYSLEVEL.MPT
 921  */
 922 
 923 static gboolean
 924 ftpfs_parse_long_list_OS2 (char *line, struct stat *s, char **filename, char **linkname, int *err)
     /* [previous][next][first][last][top][bottom][index][help]  */
 925 {
 926     char *t;
 927     long long size;
 928     int month, day, year, hour, minute;
 929     struct tm tms;
 930 
 931     t = FIRST_TOKEN;
 932     if (t == NULL)
 933         ERR2;
 934 
 935     if (sscanf (t, "%lld", &size) != 1)
 936         ERR2;
 937     s->st_size = (off_t) size;
 938 
 939     t = NEXT_TOKEN;
 940     if (t == NULL)
 941         ERR2;
 942     s->st_mode = S_IFREG;
 943     if (strcmp (t, "DIR") == 0)
 944     {
 945         s->st_mode = S_IFDIR;
 946         t = NEXT_TOKEN;
 947 
 948         if (t == NULL)
 949             ERR2;
 950     }
 951 
 952     if (sscanf (t, "%2d-%2d-%2d", &month, &day, &year) != 3)
 953         ERR2;
 954     if (year >= 70)
 955         year += 1900;
 956     else
 957         year += 2000;
 958 
 959     t = NEXT_TOKEN;
 960     if (t == NULL)
 961         ERR2;
 962     if (sscanf (t, "%2d:%2d", &hour, &minute) != 3)
 963         ERR2;
 964 
 965     tms.tm_sec = 30;            /* seconds after the minute [0, 61]  */
 966     tms.tm_min = minute;        /* minutes after the hour [0, 59] */
 967     tms.tm_hour = hour;         /* hour since midnight [0, 23] */
 968     tms.tm_mday = day;          /* day of the month [1, 31] */
 969     tms.tm_mon = month - 1;     /* months since January [0, 11] */
 970     tms.tm_year = year - 1900;  /* years since 1900 */
 971     tms.tm_isdst = -1;
 972     s->st_mtime = mktime (&tms);
 973     /* Use resulting time value */
 974     s->st_atime = s->st_ctime = s->st_mtime;
 975 
 976     t = strtok (NULL, "");
 977     if (t == NULL)
 978         ERR2;
 979     while (*t == ' ')
 980         t++;
 981     if (*t == '\0')
 982         ERR2;
 983     *filename = g_strdup (t);
 984     *linkname = NULL;
 985 
 986     return TRUE;
 987 }
 988 
 989 /* --------------------------------------------------------------------------------------------- */
 990 
 991 static gboolean
 992 ftpfs_parse_long_list_MacWebStar (char *line, struct stat *s, char **filename,
     /* [previous][next][first][last][top][bottom][index][help]  */
 993                                   char **linkname, int *err)
 994 {
 995     char *t;
 996     mode_t type, mode;
 997     struct tm date;
 998     const char *day_of_month;
 999     char *name;
1000 
1001     t = FIRST_TOKEN;
1002     if (t == NULL)
1003         ERR2;
1004 
1005     if (!vfs_parse_filetype (t, NULL, &type))
1006         ERR2;
1007 
1008     s->st_mode = type;
1009 
1010     if (!vfs_parse_fileperms (t + 1, NULL, &mode))
1011         ERR2;
1012     /* permissions are meaningless here. */
1013 
1014     /* "folder" or 0 */
1015     t = NEXT_TOKEN;
1016     if (t == NULL)
1017         ERR2;
1018 
1019     if (strcmp (t, "folder") != 0)
1020     {
1021         long long size;
1022 
1023         /* size? */
1024         t = NEXT_TOKEN;
1025         if (t == NULL)
1026             ERR2;
1027         /* size */
1028         t = NEXT_TOKEN;
1029         if (t == NULL)
1030             ERR2;
1031         if (!isdigit ((unsigned char) *t))
1032             ERR2;
1033 
1034         if (sscanf (t, "%lld", &size) == 1)
1035             s->st_size = (off_t) size;
1036     }
1037     else
1038     {
1039         /* ?? */
1040         t = NEXT_TOKEN;
1041         if (t == NULL)
1042             ERR2;
1043     }
1044 
1045     /* month */
1046     t = NEXT_TOKEN;
1047     if (t == NULL)
1048         ERR2;
1049 
1050     memset (&date, 0, sizeof (date));
1051 
1052     if (!vfs_parse_month (t, &date))
1053         ERR2;
1054 
1055     day_of_month = NEXT_TOKEN;
1056     if (day_of_month == NULL)
1057         ERR2;
1058 
1059     date.tm_mday = atoi (day_of_month);
1060 
1061     /* time or year */
1062     t = NEXT_TOKEN;
1063     if (t == NULL)
1064         ERR2;
1065     if (!parse_year_or_time (t, &date.tm_year, &date.tm_hour, &date.tm_min))
1066         ERR2;
1067 
1068     date.tm_isdst = -1;
1069     date.tm_sec = 30;
1070     if (date.tm_year == -1)
1071         date.tm_year = guess_year (date.tm_mon, date.tm_mday, date.tm_hour, date.tm_min) - 1900;
1072     else
1073         date.tm_hour = 12;
1074 
1075     s->st_mtime = mktime (&date);
1076     /* Use resulting time value */
1077     s->st_atime = s->st_ctime = s->st_mtime;
1078 
1079     name = strtok (NULL, "");
1080     if (name == NULL)
1081         ERR2;
1082 
1083     /* no symlinks on Mac, but anyway. */
1084     if (!S_ISLNK (s->st_mode))
1085         *linkname = NULL;
1086     else
1087     {
1088         char *arrow;
1089 
1090         for (arrow = name; (arrow = strstr (arrow, " -> ")) != NULL; arrow++)
1091             if (arrow != name && arrow[4] != '\0')
1092             {
1093                 *arrow = '\0';
1094                 *linkname = g_strdup (arrow + 4);
1095                 break;
1096             }
1097     }
1098 
1099     *filename = g_strdup (name);
1100 
1101     return TRUE;
1102 }
1103 
1104 /* --------------------------------------------------------------------------------------------- */
1105 /*** public functions ****************************************************************************/
1106 /* --------------------------------------------------------------------------------------------- */
1107 
1108 GSList *
1109 ftpfs_parse_long_list (struct vfs_class * me, struct vfs_s_inode * dir, GSList * buf, int *err_ret)
     /* [previous][next][first][last][top][bottom][index][help]  */
1110 {
1111     int err[number_of_parsers];
1112     GSList *set[number_of_parsers];     /* arrays of struct vfs_s_entry */
1113     size_t i;
1114     GSList *bufp;
1115     ftpfs_line_parser guessed_parser = NULL;
1116     GSList **the_set = NULL;
1117     int *the_err = NULL;
1118     int *best_err1 = &err[0];
1119     int *best_err2 = &err[1];
1120 
1121     ftpfs_init_time ();
1122 
1123     if (err_ret != NULL)
1124         *err_ret = 0;
1125 
1126     memset (&err, 0, sizeof (err));
1127     memset (&set, 0, sizeof (set));
1128 
1129     for (bufp = buf; bufp != NULL; bufp = g_slist_next (bufp))
1130     {
1131         char *b = (char *) bufp->data;
1132         size_t blen;
1133 
1134         blen = strlen (b);
1135 
1136         if (b[blen - 1] == '\r')
1137         {
1138             b[blen - 1] = '\0';
1139             blen--;
1140         }
1141 
1142         if (blen == 0)
1143             continue;
1144 
1145         if (guessed_parser == NULL)
1146         {
1147             for (i = 0; i < number_of_parsers; i++)
1148             {
1149                 struct vfs_s_entry *info;
1150                 gboolean ok;
1151                 char *tmp_line;
1152                 int nlink;
1153 
1154                 /* parser can clobber the line - work on a copy */
1155                 tmp_line = g_strndup (b, blen);
1156 
1157                 info = vfs_s_generate_entry (me, NULL, dir, 0);
1158                 nlink = info->ino->st.st_nlink;
1159                 ok = (*line_parsers[i]) (tmp_line, &info->ino->st, &info->name,
1160                                          &info->ino->linkname, &err[i]);
1161                 if (ok && strchr (info->name, '/') == NULL)
1162                 {
1163                     info->ino->st.st_nlink = nlink;     /* Ouch, we need to preserve our counts :-( */
1164                     set[i] = g_slist_prepend (set[i], info);
1165                 }
1166                 else
1167                     vfs_s_free_entry (me, info);
1168 
1169                 g_free (tmp_line);
1170 
1171                 if (*best_err1 > err[i])
1172                     best_err1 = &err[i];
1173                 if (*best_err2 > err[i] && best_err1 != &err[i])
1174                     best_err2 = &err[i];
1175 
1176                 if (*best_err1 > 16)
1177                     goto leave; /* too many errors with best parser. */
1178             }
1179 
1180             if (*best_err2 > (*best_err1 + 1) * 16)
1181             {
1182                 i = (size_t) (best_err1 - err);
1183                 guessed_parser = line_parsers[i];
1184                 the_set = &set[i];
1185                 the_err = &err[i];
1186             }
1187         }
1188         else
1189         {
1190             struct vfs_s_entry *info;
1191             gboolean ok;
1192             char *tmp_line;
1193             int nlink;
1194 
1195             /* parser can clobber the line - work on a copy */
1196             tmp_line = g_strndup (b, blen);
1197 
1198             info = vfs_s_generate_entry (me, NULL, dir, 0);
1199             nlink = info->ino->st.st_nlink;
1200             ok = guessed_parser (tmp_line, &info->ino->st, &info->name, &info->ino->linkname,
1201                                  the_err);
1202             if (ok && strchr (info->name, '/') == NULL)
1203             {
1204                 info->ino->st.st_nlink = nlink; /* Ouch, we need to preserve our counts :-( */
1205                 *the_set = g_slist_prepend (*the_set, info);
1206             }
1207             else
1208                 vfs_s_free_entry (me, info);
1209 
1210             g_free (tmp_line);
1211         }
1212     }
1213 
1214     if (the_set == NULL)
1215     {
1216         i = best_err1 - err;
1217         the_set = &set[i];
1218         the_err = &err[i];
1219     }
1220 
1221   leave:
1222     for (i = 0; i < number_of_parsers; i++)
1223         if (&set[i] != the_set)
1224         {
1225             for (bufp = set[i]; bufp != NULL; bufp = g_slist_next (bufp))
1226                 vfs_s_free_entry (me, VFS_ENTRY (bufp->data));
1227 
1228             g_slist_free (set[i]);
1229         }
1230 
1231     if (err_ret != NULL && the_err != NULL)
1232         *err_ret = *the_err;
1233 
1234     return the_set != NULL ? g_slist_reverse (*the_set) : NULL;
1235 }
1236 
1237 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */