LCOV - code coverage report
Current view: top level - netwerk/streamconv/converters - ParseFTPList.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 808 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 2 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "ParseFTPList.h"
       7             : #include <stdlib.h>
       8             : #include <string.h>
       9             : #include <ctype.h>
      10             : #include "plstr.h"
      11             : #include "nsDebug.h"
      12             : #include "prprf.h"
      13             : #include "mozilla/IntegerPrintfMacros.h"
      14             : #include "mozilla/Sprintf.h"
      15             : 
      16             : /* ==================================================================== */
      17             : 
      18           0 : static inline int ParsingFailed(struct list_state *state)
      19             : {
      20           0 :   if (state->parsed_one || state->lstyle) /* junk if we fail to parse */
      21           0 :     return '?';      /* this time but had previously parsed successfully */
      22           0 :   return '"';        /* its part of a comment or error message */
      23             : }
      24             : 
      25           0 : int ParseFTPList(const char *line, struct list_state *state,
      26             :                  struct list_result *result )
      27             : {
      28             :   unsigned int carry_buf_len; /* copy of state->carry_buf_len */
      29             :   unsigned int pos;
      30             :   const char *p;
      31             : 
      32           0 :   if (!line || !state || !result)
      33           0 :     return 0;
      34             : 
      35           0 :   memset( result, 0, sizeof(*result) );
      36           0 :   state->numlines++;
      37             : 
      38             :   /* carry buffer is only valid from one line to the next */
      39           0 :   carry_buf_len = state->carry_buf_len;
      40           0 :   state->carry_buf_len = 0;
      41             : 
      42             :   /* strip leading whitespace */
      43           0 :   while (*line == ' ' || *line == '\t')
      44           0 :     line++;
      45             : 
      46             :   /* line is terminated at first '\0' or '\n' */
      47           0 :   p = line;
      48           0 :   while (*p && *p != '\n')
      49           0 :     p++;
      50           0 :   unsigned int linelen = p - line;
      51             : 
      52           0 :   if (linelen > 0 && *p == '\n' && *(p-1) == '\r')
      53           0 :     linelen--;
      54             : 
      55             :   /* DON'T strip trailing whitespace. */
      56             : 
      57           0 :   if (linelen > 0)
      58             :   {
      59             :     static const char *month_names = "JanFebMarAprMayJunJulAugSepOctNovDec";
      60             :     const char *tokens[16]; /* 16 is more than enough */
      61             :     unsigned int toklen[(sizeof(tokens)/sizeof(tokens[0]))];
      62             :     unsigned int linelen_sans_wsp;  // line length sans whitespace
      63           0 :     unsigned int numtoks = 0;
      64           0 :     unsigned int tokmarker = 0; /* extra info for lstyle handler */
      65           0 :     unsigned int month_num = 0;
      66             :     char tbuf[4];
      67           0 :     int lstyle = 0;
      68             : 
      69           0 :     if (carry_buf_len) /* VMS long filename carryover buffer */
      70             :     {
      71           0 :       tokens[0] = state->carry_buf;
      72           0 :       toklen[0] = carry_buf_len;
      73           0 :       numtoks++;
      74             :     }
      75             : 
      76           0 :     pos = 0;
      77           0 :     while (pos < linelen && numtoks < (sizeof(tokens)/sizeof(tokens[0])) )
      78             :     {
      79           0 :       while (pos < linelen &&
      80           0 :             (line[pos] == ' ' || line[pos] == '\t' || line[pos] == '\r'))
      81           0 :         pos++;
      82           0 :       if (pos < linelen)
      83             :       {
      84           0 :         tokens[numtoks] = &line[pos];
      85           0 :         while (pos < linelen &&
      86           0 :            (line[pos] != ' ' && line[pos] != '\t' && line[pos] != '\r'))
      87           0 :           pos++;
      88           0 :         if (tokens[numtoks] != &line[pos])
      89             :         {
      90           0 :           toklen[numtoks] = (&line[pos] - tokens[numtoks]);
      91           0 :           numtoks++;
      92             :         }
      93             :       }
      94             :     }
      95             : 
      96           0 :     if (!numtoks)
      97           0 :       return ParsingFailed(state);
      98             : 
      99           0 :     linelen_sans_wsp = &(tokens[numtoks-1][toklen[numtoks-1]]) - tokens[0];
     100           0 :     if (numtoks == (sizeof(tokens)/sizeof(tokens[0])) )
     101             :     {
     102           0 :       pos = linelen;
     103           0 :       while (pos > 0 && (line[pos-1] == ' ' || line[pos-1] == '\t'))
     104           0 :         pos--;
     105           0 :       linelen_sans_wsp = pos;
     106             :     }
     107             : 
     108             :     /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
     109             : 
     110             : #if defined(SUPPORT_EPLF)
     111             :     /* EPLF handling must come somewhere before /bin/dls handling. */
     112           0 :     if (!lstyle && (!state->lstyle || state->lstyle == 'E'))
     113             :     {
     114           0 :       if (*line == '+' && linelen > 4 && numtoks >= 2)
     115             :       {
     116           0 :         pos = 1;
     117           0 :         while (pos < (linelen-1))
     118             :         {
     119           0 :           p = &line[pos++];
     120           0 :           if (*p == '/')
     121           0 :             result->fe_type = 'd'; /* its a dir */
     122           0 :           else if (*p == 'r')
     123           0 :             result->fe_type = 'f'; /* its a file */
     124           0 :           else if (*p == 'm')
     125             :           {
     126           0 :             if (isdigit(line[pos]))
     127             :             {
     128           0 :               while (pos < linelen && isdigit(line[pos]))
     129           0 :                 pos++;
     130           0 :               if (pos < linelen && line[pos] == ',')
     131             :               {
     132             :                 PRTime t;
     133             :                 PRTime seconds;
     134           0 :                 PR_sscanf(p+1, "%llu", &seconds);
     135           0 :                 t = seconds * PR_USEC_PER_SEC;
     136           0 :                 PR_ExplodeTime(t, PR_LocalTimeParameters, &(result->fe_time) );
     137             :               }
     138             :             }
     139             :           }
     140           0 :           else if (*p == 's')
     141             :           {
     142           0 :             if (isdigit(line[pos]))
     143             :             {
     144           0 :               while (pos < linelen && isdigit(line[pos]))
     145           0 :                 pos++;
     146           0 :               if (pos < linelen && line[pos] == ',' &&
     147           0 :                  ((&line[pos]) - (p+1)) < int(sizeof(result->fe_size)-1) )
     148             :               {
     149           0 :                 memcpy( result->fe_size, p+1, (unsigned)(&line[pos] - (p+1)) );
     150           0 :                 result->fe_size[(&line[pos] - (p+1))] = '\0';
     151             :               }
     152             :             }
     153             :           }
     154           0 :           else if (isalpha(*p)) /* 'i'/'up' or unknown "fact" (property) */
     155             :           {
     156           0 :             while (pos < linelen && *++p != ',')
     157           0 :               pos++;
     158             :           }
     159           0 :           else if (*p != '\t' || (p+1) != tokens[1])
     160             :           {
     161             :             break; /* its not EPLF after all */
     162             :           }
     163             :           else
     164             :           {
     165           0 :             state->parsed_one = 1;
     166           0 :             state->lstyle = lstyle = 'E';
     167             : 
     168           0 :             p = &(line[linelen_sans_wsp]);
     169           0 :             result->fe_fname = tokens[1];
     170           0 :             result->fe_fnlen = p - tokens[1];
     171             : 
     172           0 :             if (!result->fe_type) /* access denied */
     173             :             {
     174           0 :               result->fe_type = 'f'; /* is assuming 'f'ile correct? */
     175           0 :               return '?';            /* NO! junk it. */
     176             :             }
     177           0 :             return result->fe_type;
     178             :           }
     179           0 :           if (pos >= (linelen-1) || line[pos] != ',')
     180             :             break;
     181           0 :           pos++;
     182             :         } /* while (pos < linelen) */
     183           0 :         memset( result, 0, sizeof(*result) );
     184             :       } /* if (*line == '+' && linelen > 4 && numtoks >= 2) */
     185             :     } /* if (!lstyle && (!state->lstyle || state->lstyle == 'E')) */
     186             : #endif /* SUPPORT_EPLF */
     187             : 
     188             :     /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
     189             : 
     190             : #if defined(SUPPORT_VMS)
     191           0 :     if (!lstyle && (!state->lstyle || state->lstyle == 'V'))
     192             :     {                          /* try VMS Multinet/UCX/CMS server */
     193             :       /*
     194             :        * Legal characters in a VMS file/dir spec are [A-Z0-9$.-_~].
     195             :        * '$' cannot begin a filename and `-' cannot be used as the first
     196             :        * or last character. '.' is only valid as a directory separator
     197             :        * and <file>.<type> separator. A canonical filename spec might look
     198             :        * like this: DISK$VOL:[DIR1.DIR2.DIR3]FILE.TYPE;123
     199             :        * All VMS FTP servers LIST in uppercase.
     200             :        *
     201             :        * We need to be picky about this in order to support
     202             :        * multi-line listings correctly.
     203             :       */
     204           0 :       if (!state->parsed_one &&
     205           0 :           (numtoks == 1 || (numtoks == 2 && toklen[0] == 9 &&
     206           0 :                             memcmp(tokens[0], "Directory", 9)==0 )))
     207             :       {
     208             :         /* If no dirstyle has been detected yet, and this line is a
     209             :          * VMS list's dirname, then turn on VMS dirstyle.
     210             :          * eg "ACA:[ANONYMOUS]", "DISK$FTP:[ANONYMOUS]", "SYS$ANONFTP:"
     211             :         */
     212           0 :         p = tokens[0];
     213           0 :         pos = toklen[0];
     214           0 :         if (numtoks == 2)
     215             :         {
     216           0 :           p = tokens[1];
     217           0 :           pos = toklen[1];
     218             :         }
     219           0 :         pos--;
     220           0 :         if (pos >= 3)
     221             :         {
     222           0 :           while (pos > 0 && p[pos] != '[')
     223             :           {
     224           0 :             pos--;
     225           0 :             if (p[pos] == '-' || p[pos] == '$')
     226             :             {
     227           0 :               if (pos == 0 || p[pos-1] == '[' || p[pos-1] == '.' ||
     228           0 :                   (p[pos] == '-' && (p[pos+1] == ']' || p[pos+1] == '.')))
     229             :                 break;
     230             :             }
     231           0 :             else if (p[pos] != '.' && p[pos] != '~' &&
     232           0 :                      !isdigit(p[pos]) && !isalpha(p[pos]))
     233             :               break;
     234           0 :             else if (isalpha(p[pos]) && p[pos] != toupper(p[pos]))
     235           0 :               break;
     236             :           }
     237           0 :           if (pos > 0)
     238             :           {
     239           0 :             pos--;
     240           0 :             if (p[pos] != ':' || p[pos+1] != '[')
     241           0 :               pos = 0;
     242             :           }
     243             :         }
     244           0 :         if (pos > 0 && p[pos] == ':')
     245             :         {
     246           0 :           while (pos > 0)
     247             :           {
     248           0 :             pos--;
     249           0 :             if (p[pos] != '$' && p[pos] != '_' && p[pos] != '-' &&
     250           0 :                 p[pos] != '~' && !isdigit(p[pos]) && !isalpha(p[pos]))
     251             :               break;
     252           0 :             else if (isalpha(p[pos]) && p[pos] != toupper(p[pos]))
     253           0 :               break;
     254             :           }
     255           0 :           if (pos == 0)
     256             :           {
     257           0 :             state->lstyle = 'V';
     258           0 :             return '?'; /* its junk */
     259             :           }
     260             :         }
     261             :         /* fallthrough */
     262             :       }
     263           0 :       else if ((tokens[0][toklen[0]-1]) != ';')
     264             :       {
     265           0 :         if (numtoks == 1 && (state->lstyle == 'V' && !carry_buf_len))
     266           0 :           lstyle = 'V';
     267           0 :         else if (numtoks < 4)
     268             :           ;
     269           0 :         else if (toklen[1] >= 10 && memcmp(tokens[1], "%RMS-E-PRV", 10) == 0)
     270           0 :           lstyle = 'V';
     271           0 :         else if ((&line[linelen] - tokens[1]) >= 22 &&
     272           0 :                   memcmp(tokens[1], "insufficient privilege", 22) == 0)
     273           0 :           lstyle = 'V';
     274           0 :         else if (numtoks != 4 && numtoks != 6)
     275             :           ;
     276           0 :         else if (numtoks == 6 && (
     277           0 :                  toklen[5] < 4 || *tokens[5] != '(' ||        /* perms */
     278           0 :                            (tokens[5][toklen[5]-1]) != ')'  ))
     279             :           ;
     280           0 :         else if (  (toklen[2] == 10 || toklen[2] == 11) &&
     281           0 :                         (tokens[2][toklen[2]-5]) == '-' &&
     282           0 :                         (tokens[2][toklen[2]-9]) == '-' &&
     283           0 :         (((toklen[3]==4 || toklen[3]==5 || toklen[3]==7 || toklen[3]==8) &&
     284           0 :                         (tokens[3][toklen[3]-3]) == ':' ) ||
     285           0 :          ((toklen[3]==10 || toklen[3]==11 ) &&
     286           0 :                         (tokens[3][toklen[3]-3]) == '.' )
     287           0 :         ) &&  /* time in [H]H:MM[:SS[.CC]] format */
     288           0 :                                     isdigit(*tokens[1]) && /* size */
     289           0 :                                     isdigit(*tokens[2]) && /* date */
     290           0 :                                     isdigit(*tokens[3])    /* time */
     291             :                 )
     292             :         {
     293           0 :           lstyle = 'V';
     294             :         }
     295           0 :         if (lstyle == 'V')
     296             :         {
     297             :           /*
     298             :           * MultiNet FTP:
     299             :           *   LOGIN.COM;2                 1   4-NOV-1994 04:09 [ANONYMOUS] (RWE,RWE,,)
     300             :           *   PUB.DIR;1                   1  27-JAN-1994 14:46 [ANONYMOUS] (RWE,RWE,RE,RWE)
     301             :           *   README.FTP;1        %RMS-E-PRV, insufficient privilege or file protection violation
     302             :           *   ROUSSOS.DIR;1               1  27-JAN-1994 14:48 [CS,ROUSSOS] (RWE,RWE,RE,R)
     303             :           *   S67-50903.JPG;1           328  22-SEP-1998 16:19 [ANONYMOUS] (RWED,RWED,,)
     304             :           * UCX FTP:
     305             :           *   CII-MANUAL.TEX;1  213/216  29-JAN-1996 03:33:12  [ANONYMOU,ANONYMOUS] (RWED,RWED,,)
     306             :           * CMU/VMS-IP FTP
     307             :           *   [VMSSERV.FILES]ALARM.DIR;1 1/3 5-MAR-1993 18:09
     308             :           * TCPware FTP
     309             :           *   FOO.BAR;1 4 5-MAR-1993 18:09:01.12
     310             :           * Long filename example:
     311             :           *   THIS-IS-A-LONG-VMS-FILENAME.AND-THIS-IS-A-LONG-VMS-FILETYPE\r\n
     312             :           *                    213[/nnn]  29-JAN-1996 03:33[:nn]  [ANONYMOU,ANONYMOUS] (RWED,RWED,,)
     313             :           */
     314           0 :           tokmarker = 0;
     315           0 :           p = tokens[0];
     316           0 :           pos = 0;
     317           0 :           if (*p == '[' && toklen[0] >= 4) /* CMU style */
     318             :           {
     319           0 :             if (p[1] != ']')
     320             :             {
     321           0 :               p++;
     322           0 :               pos++;
     323             :             }
     324           0 :             while (lstyle && pos < toklen[0] && *p != ']')
     325             :             {
     326           0 :               if (*p != '$' && *p != '.' && *p != '_' && *p != '-' &&
     327           0 :                   *p != '~' && !isdigit(*p) && !isalpha(*p))
     328           0 :                 lstyle = 0;
     329           0 :               pos++;
     330           0 :               p++;
     331             :             }
     332           0 :             if (lstyle && pos < (toklen[0]-1))
     333             :             {
     334             :               /* ']' was found and there is at least one character after it */
     335           0 :               NS_ASSERTION(*p == ']', "unexpected state");
     336           0 :               pos++;
     337           0 :               p++;
     338           0 :               tokmarker = pos; /* length of leading "[DIR1.DIR2.etc]" */
     339             :             } else {
     340             :               /* not a CMU style listing */
     341           0 :               lstyle = 0;
     342             :             }
     343             :           }
     344           0 :           while (lstyle && pos < toklen[0] && *p != ';')
     345             :           {
     346           0 :             if (*p != '$' && *p != '.' && *p != '_' && *p != '-' &&
     347           0 :                 *p != '~' && !isdigit(*p) && !isalpha(*p))
     348           0 :               lstyle = 0;
     349           0 :             else if (isalpha(*p) && *p != toupper(*p))
     350           0 :               lstyle = 0;
     351           0 :             p++;
     352           0 :             pos++;
     353             :           }
     354           0 :           if (lstyle && *p == ';')
     355             :           {
     356           0 :             if (pos == 0 || pos == (toklen[0]-1))
     357           0 :               lstyle = 0;
     358           0 :             for (pos++;lstyle && pos < toklen[0];pos++)
     359             :             {
     360           0 :               if (!isdigit(tokens[0][pos]))
     361           0 :                 lstyle = 0;
     362             :             }
     363             :           }
     364           0 :           pos = (p - tokens[0]); /* => fnlength sans ";####" */
     365           0 :           pos -= tokmarker;      /* => fnlength sans "[DIR1.DIR2.etc]" */
     366           0 :           p = &(tokens[0][tokmarker]); /* offset of basename */
     367             : 
     368           0 :           if (!lstyle || pos == 0 || pos > 80) /* VMS filenames can't be longer than that */
     369             :           {
     370           0 :             lstyle = 0;
     371             :           }
     372           0 :           else if (numtoks == 1)
     373             :           {
     374             :             /* if VMS has been detected and there is only one token and that
     375             :              * token was a VMS filename then this is a multiline VMS LIST entry.
     376             :             */
     377           0 :             if (pos >= (sizeof(state->carry_buf)-1))
     378           0 :               pos = (sizeof(state->carry_buf)-1); /* shouldn't happen */
     379           0 :             memcpy( state->carry_buf, p, pos );
     380           0 :             state->carry_buf_len = pos;
     381           0 :             return '?'; /* tell caller to treat as junk */
     382             :           }
     383           0 :           else if (isdigit(*tokens[1])) /* not no-privs message */
     384             :           {
     385           0 :             for (pos = 0; lstyle && pos < (toklen[1]); pos++)
     386             :             {
     387           0 :               if (!isdigit((tokens[1][pos])) && (tokens[1][pos]) != '/')
     388           0 :                 lstyle = 0;
     389             :             }
     390           0 :             if (lstyle && numtoks > 4) /* Multinet or UCX but not CMU */
     391             :             {
     392           0 :               for (pos = 1; lstyle && pos < (toklen[5]-1); pos++)
     393             :               {
     394           0 :                 p = &(tokens[5][pos]);
     395           0 :                 if (*p!='R' && *p!='W' && *p!='E' && *p!='D' && *p!=',')
     396           0 :                   lstyle = 0;
     397             :               }
     398             :             }
     399             :           }
     400             :         } /* passed initial tests */
     401             :       } /* else if ((tokens[0][toklen[0]-1]) != ';') */
     402             : 
     403           0 :       if (lstyle == 'V')
     404             :       {
     405           0 :         state->parsed_one = 1;
     406           0 :         state->lstyle = lstyle;
     407             : 
     408           0 :         if (isdigit(*tokens[1]))  /* not permission denied etc */
     409             :         {
     410             :           /* strip leading directory name */
     411           0 :           if (*tokens[0] == '[') /* CMU server */
     412             :           {
     413           0 :             pos = toklen[0]-1;
     414           0 :             p = tokens[0]+1;
     415           0 :             while (*p != ']')
     416             :             {
     417           0 :               p++;
     418           0 :               pos--;
     419             :             }
     420           0 :             toklen[0] = --pos;
     421           0 :             tokens[0] = ++p;
     422             :           }
     423           0 :           pos = 0;
     424           0 :           while (pos < toklen[0] && (tokens[0][pos]) != ';')
     425           0 :             pos++;
     426             : 
     427           0 :           result->fe_cinfs = 1;
     428           0 :           result->fe_type = 'f';
     429           0 :           result->fe_fname = tokens[0];
     430           0 :           result->fe_fnlen = pos;
     431             : 
     432           0 :           if (pos > 4)
     433             :           {
     434           0 :             p = &(tokens[0][pos-4]);
     435           0 :             if (p[0] == '.' && p[1] == 'D' && p[2] == 'I' && p[3] == 'R')
     436             :             {
     437           0 :               result->fe_fnlen -= 4;
     438           0 :               result->fe_type = 'd';
     439             :             }
     440             :           }
     441             : 
     442           0 :           if (result->fe_type != 'd')
     443             :           {
     444             :             /* #### or used/allocated form. If used/allocated form, then
     445             :              * 'used' is the size in bytes if and only if 'used'<=allocated.
     446             :              * If 'used' is size in bytes then it can be > 2^32
     447             :              * If 'used' is not size in bytes then it is size in blocks.
     448             :             */
     449           0 :             pos = 0;
     450           0 :             while (pos < toklen[1] && (tokens[1][pos]) != '/')
     451           0 :               pos++;
     452             : 
     453             : /*
     454             :  * I've never seen size come back in bytes, its always in blocks, and
     455             :  * the following test fails. So, always perform the "size in blocks".
     456             :  * I'm leaving the "size in bytes" code if'd out in case we ever need
     457             :  * to re-instate it.
     458             : */
     459             : #if 0
     460             :             if (pos < toklen[1] && ( (pos<<1) > (toklen[1]-1) ||
     461             :                  (strtoul(tokens[1], (char **)0, 10) >
     462             :                   strtoul(tokens[1]+pos+1, (char **)0, 10))        ))
     463             :             {                                   /* size is in bytes */
     464             :               if (pos > (sizeof(result->fe_size)-1))
     465             :                 pos = sizeof(result->fe_size)-1;
     466             :               memcpy( result->fe_size, tokens[1], pos );
     467             :               result->fe_size[pos] = '\0';
     468             :             }
     469             :             else /* size is in blocks */
     470             : #endif
     471             :             {
     472             :               /* size requires multiplication by blocksize.
     473             :                *
     474             :                * We could assume blocksize is 512 (like Lynx does) and
     475             :                * shift by 9, but that might not be right. Even if it
     476             :                * were, doing that wouldn't reflect what the file's
     477             :                * real size was. The sanest thing to do is not use the
     478             :                * LISTing's filesize, so we won't (like ftpmirror).
     479             :                *
     480             :                * ulltoa(((unsigned long long)fsz)<<9, result->fe_size, 10);
     481             :                *
     482             :                * A block is always 512 bytes on OpenVMS, compute size.
     483             :                * So its rounded up to the next block, so what, its better
     484             :                * than not showing the size at all.
     485             :                * A block is always 512 bytes on OpenVMS, compute size.
     486             :                * So its rounded up to the next block, so what, its better
     487             :                * than not showing the size at all.
     488             :               */
     489           0 :               uint64_t fsz = uint64_t(strtoul(tokens[1], (char **)0, 10) * 512);
     490           0 :               SprintfLiteral(result->fe_size, "%" PRId64, fsz);
     491             :             }
     492             : 
     493             :           } /* if (result->fe_type != 'd') */
     494             : 
     495           0 :           p = tokens[2] + 2;
     496           0 :           if (*p == '-')
     497           0 :             p++;
     498           0 :           tbuf[0] = p[0];
     499           0 :           tbuf[1] = tolower(p[1]);
     500           0 :           tbuf[2] = tolower(p[2]);
     501           0 :           month_num = 0;
     502           0 :           for (pos = 0; pos < (12*3); pos+=3)
     503             :           {
     504           0 :             if (tbuf[0] == month_names[pos+0] &&
     505           0 :                 tbuf[1] == month_names[pos+1] &&
     506           0 :                 tbuf[2] == month_names[pos+2])
     507           0 :               break;
     508           0 :             month_num++;
     509             :           }
     510           0 :           if (month_num >= 12)
     511           0 :             month_num = 0;
     512           0 :           result->fe_time.tm_month = month_num;
     513           0 :           result->fe_time.tm_mday = atoi(tokens[2]);
     514           0 :           result->fe_time.tm_year = atoi(p+4); // NSPR wants year as XXXX
     515             : 
     516           0 :           p = tokens[3] + 2;
     517           0 :           if (*p == ':')
     518           0 :             p++;
     519           0 :           if (p[2] == ':')
     520           0 :             result->fe_time.tm_sec = atoi(p+3);
     521           0 :           result->fe_time.tm_hour = atoi(tokens[3]);
     522           0 :           result->fe_time.tm_min  = atoi(p);
     523             : 
     524           0 :           return result->fe_type;
     525             : 
     526             :         } /* if (isdigit(*tokens[1])) */
     527             : 
     528           0 :         return '?'; /* junk */
     529             : 
     530             :       } /* if (lstyle == 'V') */
     531             :     } /* if (!lstyle && (!state->lstyle || state->lstyle == 'V')) */
     532             : #endif
     533             : 
     534             :     /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
     535             : 
     536             : #if defined(SUPPORT_CMS)
     537             :     /* Virtual Machine/Conversational Monitor System (IBM Mainframe) */
     538           0 :     if (!lstyle && (!state->lstyle || state->lstyle == 'C'))  /* VM/CMS */
     539             :     {
     540             :       /* LISTing according to mirror.pl
     541             :        * Filename FileType  Fm Format Lrecl  Records Blocks Date      Time
     542             :        * LASTING  GLOBALV   A1 V      41     21     1       9/16/91   15:10:32
     543             :        * J43401   NETLOG    A0 V      77     1      1       9/12/91   12:36:04
     544             :        * PROFILE  EXEC      A1 V      17     3      1       9/12/91   12:39:07
     545             :        * DIRUNIX  SCRIPT    A1 V      77     1216   17      1/04/93   20:30:47
     546             :        * MAIL     PROFILE   A2 F      80     1      1       10/14/92  16:12:27
     547             :        * BADY2K   TEXT      A0 V      1      1      1       1/03/102  10:11:12
     548             :        * AUTHORS            A1 DIR    -      -      -       9/20/99   10:31:11
     549             :        *
     550             :        * LISTing from vm.marist.edu and vm.sc.edu
     551             :        * 220-FTPSERVE IBM VM Level 420 at VM.MARIST.EDU, 04:58:12 EDT WEDNESDAY 2002-07-10
     552             :        * AUTHORS           DIR        -          -          - 1999-09-20 10:31:11 -
     553             :        * HARRINGTON        DIR        -          -          - 1997-02-12 15:33:28 -
     554             :        * PICS              DIR        -          -          - 2000-10-12 15:43:23 -
     555             :        * SYSFILE           DIR        -          -          - 2000-07-20 17:48:01 -
     556             :        * WELCNVT  EXEC     V         72          9          1 1999-09-20 17:16:18 -
     557             :        * WELCOME  EREADME  F         80         21          1 1999-12-27 16:19:00 -
     558             :        * WELCOME  README   V         82         21          1 1999-12-27 16:19:04 -
     559             :        * README   ANONYMOU V         71         26          1 1997-04-02 12:33:20 TCP291
     560             :        * README   ANONYOLD V         71         15          1 1995-08-25 16:04:27 TCP291
     561             :       */
     562           0 :       if (numtoks >= 7 && (toklen[0]+toklen[1]) <= 16)
     563             :       {
     564           0 :         for (pos = 1; !lstyle && (pos+5) < numtoks; pos++)
     565             :         {
     566           0 :           p = tokens[pos];
     567           0 :           if ((toklen[pos] == 1 && (*p == 'F' || *p == 'V')) ||
     568           0 :               (toklen[pos] == 3 && *p == 'D' && p[1] == 'I' && p[2] == 'R'))
     569             :           {
     570           0 :             if (toklen[pos+5] == 8 && (tokens[pos+5][2]) == ':' &&
     571           0 :                                       (tokens[pos+5][5]) == ':'   )
     572             :             {
     573           0 :               p = tokens[pos+4];
     574           0 :               if ((toklen[pos+4] == 10 && p[4] == '-' && p[7] == '-') ||
     575           0 :                   (toklen[pos+4] >= 7 && toklen[pos+4] <= 9 &&
     576           0 :                             p[((p[1]!='/')?(2):(1))] == '/' &&
     577           0 :                             p[((p[1]!='/')?(5):(4))] == '/'))
     578             :                /* Y2K bugs possible ("7/06/102" or "13/02/101") */
     579             :               {
     580           0 :                 if ( (*tokens[pos+1] == '-' &&
     581           0 :                       *tokens[pos+2] == '-' &&
     582           0 :                       *tokens[pos+3] == '-')  ||
     583           0 :                       (isdigit(*tokens[pos+1]) &&
     584           0 :                        isdigit(*tokens[pos+2]) &&
     585           0 :                        isdigit(*tokens[pos+3])) )
     586             :                 {
     587           0 :                   lstyle = 'C';
     588           0 :                   tokmarker = pos;
     589             :                 }
     590             :               }
     591             :             }
     592             :           }
     593             :         } /* for (pos = 1; !lstyle && (pos+5) < numtoks; pos++) */
     594             :       } /* if (numtoks >= 7) */
     595             : 
     596             :       /* extra checking if first pass */
     597           0 :       if (lstyle && !state->lstyle)
     598             :       {
     599           0 :         for (pos = 0, p = tokens[0]; lstyle && pos < toklen[0]; pos++, p++)
     600             :         {
     601           0 :           if (isalpha(*p) && toupper(*p) != *p)
     602           0 :             lstyle = 0;
     603             :         }
     604           0 :         for (pos = tokmarker+1; pos <= tokmarker+3; pos++)
     605             :         {
     606           0 :           if (!(toklen[pos] == 1 && *tokens[pos] == '-'))
     607             :           {
     608           0 :             for (p = tokens[pos]; lstyle && p<(tokens[pos]+toklen[pos]); p++)
     609             :             {
     610           0 :               if (!isdigit(*p))
     611           0 :                 lstyle = 0;
     612             :             }
     613             :           }
     614             :         }
     615           0 :         for (pos = 0, p = tokens[tokmarker+4];
     616           0 :              lstyle && pos < toklen[tokmarker+4]; pos++, p++)
     617             :         {
     618           0 :           if (*p == '/')
     619             :           {
     620             :             /* There may be Y2K bugs in the date. Don't simplify to
     621             :              * pos != (len-3) && pos != (len-6) like time is done.
     622             :             */
     623           0 :             if ((tokens[tokmarker+4][1]) == '/')
     624             :             {
     625           0 :               if (pos != 1 && pos != 4)
     626           0 :                 lstyle = 0;
     627             :             }
     628           0 :             else if (pos != 2 && pos != 5)
     629           0 :               lstyle = 0;
     630             :           }
     631           0 :           else if (*p != '-' && !isdigit(*p))
     632           0 :             lstyle = 0;
     633           0 :           else if (*p == '-' && pos != 4 && pos != 7)
     634           0 :             lstyle = 0;
     635             :         }
     636           0 :         for (pos = 0, p = tokens[tokmarker+5];
     637           0 :              lstyle && pos < toklen[tokmarker+5]; pos++, p++)
     638             :         {
     639           0 :           if (*p != ':' && !isdigit(*p))
     640           0 :             lstyle = 0;
     641           0 :           else if (*p == ':' && pos != (toklen[tokmarker+5]-3)
     642           0 :                              && pos != (toklen[tokmarker+5]-6))
     643           0 :             lstyle = 0;
     644             :         }
     645             :       } /* initial if() */
     646             : 
     647           0 :       if (lstyle == 'C')
     648             :       {
     649           0 :         state->parsed_one = 1;
     650           0 :         state->lstyle = lstyle;
     651             : 
     652           0 :         p = tokens[tokmarker+4];
     653           0 :         if (toklen[tokmarker+4] == 10) /* newstyle: YYYY-MM-DD format */
     654             :         {
     655           0 :           result->fe_time.tm_year = atoi(p+0) - 1900;
     656           0 :           result->fe_time.tm_month  = atoi(p+5) - 1;
     657           0 :           result->fe_time.tm_mday = atoi(p+8);
     658             :         }
     659             :         else /* oldstyle: [M]M/DD/YY format */
     660             :         {
     661           0 :           pos = toklen[tokmarker+4];
     662           0 :           result->fe_time.tm_month  = atoi(p) - 1;
     663           0 :           result->fe_time.tm_mday = atoi((p+pos)-5);
     664           0 :           result->fe_time.tm_year = atoi((p+pos)-2);
     665           0 :           if (result->fe_time.tm_year < 70)
     666           0 :             result->fe_time.tm_year += 100;
     667             :         }
     668             : 
     669           0 :         p = tokens[tokmarker+5];
     670           0 :         pos = toklen[tokmarker+5];
     671           0 :         result->fe_time.tm_hour  = atoi(p);
     672           0 :         result->fe_time.tm_min = atoi((p+pos)-5);
     673           0 :         result->fe_time.tm_sec = atoi((p+pos)-2);
     674             : 
     675           0 :         result->fe_cinfs = 1;
     676           0 :         result->fe_fname = tokens[0];
     677           0 :         result->fe_fnlen = toklen[0];
     678           0 :         result->fe_type  = 'f';
     679             : 
     680           0 :         p = tokens[tokmarker];
     681           0 :         if (toklen[tokmarker] == 3 && *p=='D' && p[1]=='I' && p[2]=='R')
     682           0 :           result->fe_type  = 'd';
     683             : 
     684           0 :         if ((/*newstyle*/ toklen[tokmarker+4] == 10 && tokmarker > 1) ||
     685           0 :             (/*oldstyle*/ toklen[tokmarker+4] != 10 && tokmarker > 2))
     686             :         {                            /* have a filetype column */
     687             :           char *dot;
     688           0 :           p = &(tokens[0][toklen[0]]);
     689           0 :           memcpy( &dot, &p, sizeof(dot) ); /* NASTY! */
     690           0 :           *dot++ = '.';
     691           0 :           p = tokens[1];
     692           0 :           for (pos = 0; pos < toklen[1]; pos++)
     693           0 :             *dot++ = *p++;
     694           0 :           result->fe_fnlen += 1 + toklen[1];
     695             :         }
     696             : 
     697             :         /* oldstyle LISTING:
     698             :          * files/dirs not on the 'A' minidisk are not RETRievable/CHDIRable
     699             :         if (toklen[tokmarker+4] != 10 && *tokens[tokmarker-1] != 'A')
     700             :           return '?';
     701             :         */
     702             : 
     703             :         /* VM/CMS LISTings have no usable filesize field.
     704             :          * Have to use the 'SIZE' command for that.
     705             :         */
     706           0 :         return result->fe_type;
     707             : 
     708             :       } /* if (lstyle == 'C' && (!state->lstyle || state->lstyle == lstyle)) */
     709             :     } /* VM/CMS */
     710             : #endif
     711             : 
     712             :     /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
     713             : 
     714             : #if defined(SUPPORT_DOS) /* WinNT DOS dirstyle */
     715           0 :     if (!lstyle && (!state->lstyle || state->lstyle == 'W'))
     716             :     {
     717             :       /*
     718             :        * "10-23-00  01:27PM       <DIR>          veronist"
     719             :        * "06-15-00  07:37AM       <DIR>          zoe"
     720             :        * "07-14-00  01:35PM              2094926 canprankdesk.tif"
     721             :        * "07-21-00  01:19PM                95077 Jon Kauffman Enjoys the Good Life.jpg"
     722             :        * "07-21-00  01:19PM                52275 Name Plate.jpg"
     723             :        * "07-14-00  01:38PM              2250540 Valentineoffprank-HiRes.jpg"
     724             :       */
     725             :       // Microsoft FTP server with FtpDirBrowseShowLongDate set returns year
     726             :       // in 4-digit format:
     727             :       // "10-10-2014  10:10AM       <DIR>        FTP"
     728             :       // Windows CE FTP server returns time in 24-hour format:
     729             :       // "05-03-13  22:01       <DIR>          APPS"
     730           0 :       if ((numtoks >= 4) && (toklen[0] == 8 || toklen[0] == 10) &&
     731           0 :           (toklen[1] == 5 || toklen[1] == 7) &&
     732           0 :           (*tokens[2] == '<' || isdigit(*tokens[2])) )
     733             :       {
     734           0 :         p = tokens[0];
     735           0 :         if ( isdigit(p[0]) && isdigit(p[1]) && p[2]=='-' &&
     736           0 :              isdigit(p[3]) && isdigit(p[4]) && p[5]=='-' &&
     737           0 :              isdigit(p[6]) && isdigit(p[7]) )
     738             :         {
     739           0 :           p = tokens[1];
     740           0 :           if ( isdigit(p[0]) && isdigit(p[1]) && p[2]==':' &&
     741           0 :                isdigit(p[3]) && isdigit(p[4]) &&
     742           0 :                (toklen[1] == 5 || (toklen[1] == 7 &&
     743           0 :                                   (p[5]=='A' || p[5]=='P') && p[6]=='M')))
     744             :           {
     745           0 :             lstyle = 'W';
     746           0 :             if (!state->lstyle)
     747             :             {
     748           0 :               p = tokens[2];
     749             :               /* <DIR> or <JUNCTION> */
     750           0 :               if (*p != '<' || p[toklen[2]-1] != '>')
     751             :               {
     752           0 :                 for (pos = 1; (lstyle && pos < toklen[2]); pos++)
     753             :                 {
     754           0 :                   if (!isdigit(*++p))
     755           0 :                     lstyle = 0;
     756             :                 }
     757             :               }
     758             :             }
     759             :           }
     760             :         }
     761             :       }
     762             : 
     763           0 :       if (lstyle == 'W')
     764             :       {
     765           0 :         state->parsed_one = 1;
     766           0 :         state->lstyle = lstyle;
     767             : 
     768           0 :         p = &(line[linelen]); /* line end */
     769           0 :         result->fe_cinfs = 1;
     770           0 :         result->fe_fname = tokens[3];
     771           0 :         result->fe_fnlen = p - tokens[3];
     772           0 :         result->fe_type = 'd';
     773             : 
     774           0 :         if (*tokens[2] != '<') /* not <DIR> or <JUNCTION> */
     775             :         {
     776             :           // try to handle correctly spaces at the beginning of the filename
     777             :           // filesize (token[2]) must end at offset 38
     778           0 :           if (tokens[2] + toklen[2] - line == 38) {
     779           0 :             result->fe_fname = &(line[39]);
     780           0 :             result->fe_fnlen = p - result->fe_fname;
     781             :           }
     782           0 :           result->fe_type = 'f';
     783           0 :           pos = toklen[2];
     784           0 :           while (pos > (sizeof(result->fe_size)-1))
     785           0 :             pos = (sizeof(result->fe_size)-1);
     786           0 :           memcpy( result->fe_size, tokens[2], pos );
     787           0 :           result->fe_size[pos] = '\0';
     788             :         }
     789             :         else {
     790             :           // try to handle correctly spaces at the beginning of the filename
     791             :           // token[2] must begin at offset 24, the length is 5 or 10
     792             :           // token[3] must begin at offset 39 or higher
     793           0 :           if (tokens[2] - line == 24 && (toklen[2] == 5 || toklen[2] == 10) &&
     794           0 :               tokens[3] - line >= 39) {
     795           0 :             result->fe_fname = &(line[39]);
     796           0 :             result->fe_fnlen = p - result->fe_fname;
     797             :           }
     798             : 
     799           0 :           if ((tokens[2][1]) != 'D') /* not <DIR> */
     800             :           {
     801           0 :             result->fe_type = '?'; /* unknown until junc for sure */
     802           0 :             if (result->fe_fnlen > 4)
     803             :             {
     804           0 :               p = result->fe_fname;
     805           0 :               for (pos = result->fe_fnlen - 4; pos > 0; pos--)
     806             :               {
     807           0 :                 if (p[0] == ' ' && p[3] == ' ' && p[2] == '>' &&
     808           0 :                     (p[1] == '=' || p[1] == '-'))
     809             :                 {
     810           0 :                   result->fe_type = 'l';
     811           0 :                   result->fe_fnlen = p - result->fe_fname;
     812           0 :                   result->fe_lname = p + 4;
     813           0 :                   result->fe_lnlen = &(line[linelen])
     814           0 :                                      - result->fe_lname;
     815           0 :                   break;
     816             :                 }
     817           0 :                 p++;
     818             :               }
     819             :             }
     820             :           }
     821             :         }
     822             : 
     823           0 :         result->fe_time.tm_month = atoi(tokens[0]+0);
     824           0 :         if (result->fe_time.tm_month != 0)
     825             :         {
     826           0 :           result->fe_time.tm_month--;
     827           0 :           result->fe_time.tm_mday = atoi(tokens[0]+3);
     828           0 :           result->fe_time.tm_year = atoi(tokens[0]+6);
     829             :           /* if year has only two digits then assume that
     830             :                00-79 is 2000-2079
     831             :                80-99 is 1980-1999 */
     832           0 :           if (result->fe_time.tm_year < 80)
     833           0 :             result->fe_time.tm_year += 2000;
     834           0 :           else if (result->fe_time.tm_year < 100)
     835           0 :             result->fe_time.tm_year += 1900;
     836             :         }
     837             : 
     838           0 :         result->fe_time.tm_hour = atoi(tokens[1]+0);
     839           0 :         result->fe_time.tm_min = atoi(tokens[1]+3);
     840           0 :         if (toklen[1] == 7)
     841             :         {
     842           0 :           if ((tokens[1][5]) == 'P' && result->fe_time.tm_hour < 12)
     843           0 :             result->fe_time.tm_hour += 12;
     844           0 :           else if ((tokens[1][5]) == 'A' && result->fe_time.tm_hour == 12)
     845           0 :             result->fe_time.tm_hour = 0;
     846             :         }
     847             : 
     848             :         /* the caller should do this (if dropping "." and ".." is desired)
     849             :         if (result->fe_type == 'd' && result->fe_fname[0] == '.' &&
     850             :             (result->fe_fnlen == 1 || (result->fe_fnlen == 2 &&
     851             :                                       result->fe_fname[1] == '.')))
     852             :           return '?';
     853             :         */
     854             : 
     855           0 :         return result->fe_type;
     856             :       } /* if (lstyle == 'W' && (!state->lstyle || state->lstyle == lstyle)) */
     857             :     } /* if (!lstyle && (!state->lstyle || state->lstyle == 'W')) */
     858             : #endif
     859             : 
     860             :     /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
     861             : 
     862             : #if defined(SUPPORT_OS2)
     863           0 :     if (!lstyle && (!state->lstyle || state->lstyle == 'O')) /* OS/2 test */
     864             :     {
     865             :       /* 220 server IBM TCP/IP for OS/2 - FTP Server ver 23:04:36 on Jan 15 1997 ready.
     866             :       * fixed position, space padded columns. I have only a vague idea
     867             :       * of what the contents between col 18 and 34 might be: All I can infer
     868             :       * is that there may be attribute flags in there and there may be
     869             :       * a " DIR" in there.
     870             :       *
     871             :       *          1         2         3         4         5         6
     872             :       *0123456789012345678901234567890123456789012345678901234567890123456789
     873             :       *----- size -------|??????????????? MM-DD-YY|  HH:MM| nnnnnnnnn....
     874             :       *                 0  DIR            04-11-95   16:26  .
     875             :       *                 0  DIR            04-11-95   16:26  ..
     876             :       *                 0  DIR            04-11-95   16:26  ADDRESS
     877             :       *               612  RHSA           07-28-95   16:45  air_tra1.bag
     878             :       *               195  A              08-09-95   10:23  Alfa1.bag
     879             :       *                 0  RHS   DIR      04-11-95   16:26  ATTACH
     880             :       *               372  A              08-09-95   10:26  Aussie_1.bag
     881             :       *            310992                 06-28-94   09:56  INSTALL.EXE
     882             :       *                            1         2         3         4
     883             :       *                  01234567890123456789012345678901234567890123456789
     884             :       * dirlist from the mirror.pl project, col positions from Mozilla.
     885             :       */
     886           0 :       p = &(line[toklen[0]]);
     887             :       /* \s(\d\d-\d\d-\d\d)\s+(\d\d:\d\d)\s */
     888           0 :       if (numtoks >= 4 && toklen[0] <= 18 && isdigit(*tokens[0]) &&
     889           0 :          (linelen - toklen[0]) >= (53-18)                        &&
     890           0 :          p[18-18] == ' ' && p[34-18] == ' '                      &&
     891           0 :          p[37-18] == '-' && p[40-18] == '-' && p[43-18] == ' '   &&
     892           0 :          p[45-18] == ' ' && p[48-18] == ':' && p[51-18] == ' '   &&
     893           0 :          isdigit(p[35-18]) && isdigit(p[36-18])                  &&
     894           0 :          isdigit(p[38-18]) && isdigit(p[39-18])                  &&
     895           0 :          isdigit(p[41-18]) && isdigit(p[42-18])                  &&
     896           0 :          isdigit(p[46-18]) && isdigit(p[47-18])                  &&
     897           0 :          isdigit(p[49-18]) && isdigit(p[50-18])
     898             :       )
     899             :       {
     900           0 :         lstyle = 'O'; /* OS/2 */
     901           0 :         if (!state->lstyle)
     902             :         {
     903           0 :           for (pos = 1; lstyle && pos < toklen[0]; pos++)
     904             :           {
     905           0 :             if (!isdigit(tokens[0][pos]))
     906           0 :               lstyle = 0;
     907             :           }
     908             :         }
     909             :       }
     910             : 
     911           0 :       if (lstyle == 'O')
     912             :       {
     913           0 :         state->parsed_one = 1;
     914           0 :         state->lstyle = lstyle;
     915             : 
     916           0 :         p = &(line[toklen[0]]);
     917             : 
     918           0 :         result->fe_cinfs = 1;
     919           0 :         result->fe_fname = &p[53-18];
     920           0 :         result->fe_fnlen = (&(line[linelen_sans_wsp]))
     921           0 :                            - (result->fe_fname);
     922           0 :         result->fe_type = 'f';
     923             : 
     924             :         /* I don't have a real listing to determine exact pos, so scan. */
     925           0 :         for (pos = (18-18); pos < ((35-18)-4); pos++)
     926             :         {
     927           0 :           if (p[pos+0] == ' ' && p[pos+1] == 'D' &&
     928           0 :               p[pos+2] == 'I' && p[pos+3] == 'R')
     929             :           {
     930           0 :             result->fe_type = 'd';
     931           0 :             break;
     932             :           }
     933             :         }
     934             : 
     935           0 :         if (result->fe_type != 'd')
     936             :         {
     937           0 :           pos = toklen[0];
     938           0 :           if (pos > (sizeof(result->fe_size)-1))
     939           0 :             pos = (sizeof(result->fe_size)-1);
     940           0 :           memcpy( result->fe_size, tokens[0], pos );
     941           0 :           result->fe_size[pos] = '\0';
     942             :         }
     943             : 
     944           0 :         result->fe_time.tm_month = atoi(&p[35-18]) - 1;
     945           0 :         result->fe_time.tm_mday = atoi(&p[38-18]);
     946           0 :         result->fe_time.tm_year = atoi(&p[41-18]);
     947           0 :         if (result->fe_time.tm_year < 80)
     948           0 :           result->fe_time.tm_year += 100;
     949           0 :         result->fe_time.tm_hour = atoi(&p[46-18]);
     950           0 :         result->fe_time.tm_min = atoi(&p[49-18]);
     951             : 
     952             :         /* the caller should do this (if dropping "." and ".." is desired)
     953             :         if (result->fe_type == 'd' && result->fe_fname[0] == '.' &&
     954             :             (result->fe_fnlen == 1 || (result->fe_fnlen == 2 &&
     955             :                                       result->fe_fname[1] == '.')))
     956             :           return '?';
     957             :         */
     958             : 
     959           0 :         return result->fe_type;
     960             :       } /* if (lstyle == 'O') */
     961             : 
     962             :     } /* if (!lstyle && (!state->lstyle || state->lstyle == 'O')) */
     963             : #endif
     964             : 
     965             :     /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
     966             : 
     967             : #if defined(SUPPORT_LSL)
     968           0 :     if (!lstyle && (!state->lstyle || state->lstyle == 'U')) /* /bin/ls & co. */
     969             :     {
     970             :       /* UNIX-style listing, without inum and without blocks
     971             :        * "-rw-r--r--   1 root     other        531 Jan 29 03:26 README"
     972             :        * "dr-xr-xr-x   2 root     other        512 Apr  8  1994 etc"
     973             :        * "dr-xr-xr-x   2 root     512 Apr  8  1994 etc"
     974             :        * "lrwxrwxrwx   1 root     other          7 Jan 25 00:17 bin -> usr/bin"
     975             :        * Also produced by Microsoft's FTP servers for Windows:
     976             :        * "----------   1 owner    group         1803128 Jul 10 10:18 ls-lR.Z"
     977             :        * "d---------   1 owner    group               0 May  9 19:45 Softlib"
     978             :        * Also WFTPD for MSDOS:
     979             :        * "-rwxrwxrwx   1 noone    nogroup      322 Aug 19  1996 message.ftp"
     980             :        * Hellsoft for NetWare:
     981             :        * "d[RWCEMFA] supervisor            512       Jan 16 18:53    login"
     982             :        * "-[RWCEMFA] rhesus             214059       Oct 20 15:27    cx.exe"
     983             :        * Newer Hellsoft for NetWare: (netlab2.usu.edu)
     984             :        * - [RWCEAFMS] NFAUUser               192 Apr 27 15:21 HEADER.html
     985             :        * d [RWCEAFMS] jrd                    512 Jul 11 03:01 allupdates
     986             :        * Also NetPresenz for the Mac:
     987             :        * "-------r--         326  1391972  1392298 Nov 22  1995 MegaPhone.sit"
     988             :        * "drwxrwxr-x               folder        2 May 10  1996 network"
     989             :        * Protected directory:
     990             :        * "drwx-wx-wt  2 root  wheel  512 Jul  1 02:15 incoming"
     991             :        * uid/gid instead of username/groupname:
     992             :        * "drwxr-xr-x  2 0  0  512 May 28 22:17 etc"
     993             :       */
     994             : 
     995           0 :       bool is_old_Hellsoft = false;
     996             : 
     997           0 :       if (numtoks >= 6)
     998             :       {
     999             :         /* there are two perm formats (Hellsoft/NetWare and *IX strmode(3)).
    1000             :          * Scan for size column only if the perm format is one or the other.
    1001             :          */
    1002           0 :         if (toklen[0] == 1 || (tokens[0][1]) == '[')
    1003             :         {
    1004           0 :           if (*tokens[0] == 'd' || *tokens[0] == '-')
    1005             :           {
    1006           0 :             pos = toklen[0]-1;
    1007           0 :             p = tokens[0] + 1;
    1008           0 :             if (pos == 0)
    1009             :             {
    1010           0 :               p = tokens[1];
    1011           0 :               pos = toklen[1];
    1012             :             }
    1013           0 :             if ((pos == 9 || pos == 10)        &&
    1014           0 :                 (*p == '[' && p[pos-1] == ']') &&
    1015           0 :                 (p[1] == 'R' || p[1] == '-')   &&
    1016           0 :                 (p[2] == 'W' || p[2] == '-')   &&
    1017           0 :                 (p[3] == 'C' || p[3] == '-')   &&
    1018           0 :                 (p[4] == 'E' || p[4] == '-'))
    1019             :             {
    1020             :               /* rest is FMA[S] or AFM[S] */
    1021           0 :               lstyle = 'U'; /* very likely one of the NetWare servers */
    1022           0 :               if (toklen[0] == 10)
    1023           0 :                 is_old_Hellsoft = true;
    1024             :             }
    1025             :           }
    1026             :         }
    1027           0 :         else if ((toklen[0] == 10 || toklen[0] == 11)
    1028           0 :                    && strchr("-bcdlpsw?DFam", *tokens[0]))
    1029             :         {
    1030           0 :           p = &(tokens[0][1]);
    1031           0 :           if ((p[0] == 'r' || p[0] == '-') &&
    1032           0 :               (p[1] == 'w' || p[1] == '-') &&
    1033           0 :               (p[3] == 'r' || p[3] == '-') &&
    1034           0 :               (p[4] == 'w' || p[4] == '-') &&
    1035           0 :               (p[6] == 'r' || p[6] == '-') &&
    1036           0 :               (p[7] == 'w' || p[7] == '-'))
    1037             :             /* 'x'/p[9] can be S|s|x|-|T|t or implementation specific */
    1038             :           {
    1039           0 :             lstyle = 'U'; /* very likely /bin/ls */
    1040             :           }
    1041             :         }
    1042             :       }
    1043           0 :       if (lstyle == 'U') /* first token checks out */
    1044             :       {
    1045           0 :         lstyle = 0;
    1046           0 :         for (pos = (numtoks-5); !lstyle && pos > 1; pos--)
    1047             :         {
    1048             :           /* scan for: (\d+)\s+([A-Z][a-z][a-z])\s+
    1049             :            *  (\d\d\d\d|\d\:\d\d|\d\d\:\d\d|\d\:\d\d\:\d\d|\d\d\:\d\d\:\d\d)
    1050             :            *  \s+(.+)$
    1051             :           */
    1052           0 :           if (isdigit(*tokens[pos]) /* size */
    1053             :               /* (\w\w\w) */
    1054           0 :            && toklen[pos+1] == 3 && isalpha(*tokens[pos+1]) &&
    1055           0 :               isalpha(tokens[pos+1][1]) && isalpha(tokens[pos+1][2])
    1056             :               /* (\d|\d\d) */
    1057           0 :            && isdigit(*tokens[pos+2]) &&
    1058           0 :                 (toklen[pos+2] == 1 ||
    1059           0 :                   (toklen[pos+2] == 2 && isdigit(tokens[pos+2][1])))
    1060           0 :            && toklen[pos+3] >= 4 && isdigit(*tokens[pos+3])
    1061             :               /* (\d\:\d\d\:\d\d|\d\d\:\d\d\:\d\d) */
    1062           0 :            && (toklen[pos+3] <= 5 || (
    1063           0 :                (toklen[pos+3] == 7 || toklen[pos+3] == 8) &&
    1064           0 :                (tokens[pos+3][toklen[pos+3]-3]) == ':'))
    1065           0 :            && isdigit(tokens[pos+3][toklen[pos+3]-2])
    1066           0 :            && isdigit(tokens[pos+3][toklen[pos+3]-1])
    1067           0 :            && (
    1068             :               /* (\d\d\d\d) */
    1069           0 :                  ((toklen[pos+3] == 4 || toklen[pos+3] == 5) &&
    1070           0 :                   isdigit(tokens[pos+3][1]) &&
    1071           0 :                   isdigit(tokens[pos+3][2])  )
    1072             :               /* (\d\:\d\d|\d\:\d\d\:\d\d) */
    1073           0 :               || ((toklen[pos+3] == 4 || toklen[pos+3] == 7) &&
    1074           0 :                   (tokens[pos+3][1]) == ':' &&
    1075           0 :                   isdigit(tokens[pos+3][2]) && isdigit(tokens[pos+3][3]))
    1076             :               /* (\d\d\:\d\d|\d\d\:\d\d\:\d\d) */
    1077           0 :               || ((toklen[pos+3] == 5 || toklen[pos+3] == 8) &&
    1078           0 :                   isdigit(tokens[pos+3][1]) && (tokens[pos+3][2]) == ':' &&
    1079           0 :                   isdigit(tokens[pos+3][3]) && isdigit(tokens[pos+3][4]))
    1080             :               )
    1081             :            )
    1082             :           {
    1083           0 :             lstyle = 'U'; /* assume /bin/ls or variant format */
    1084           0 :             tokmarker = pos;
    1085             : 
    1086             :             /* check that size is numeric */
    1087           0 :             p = tokens[tokmarker];
    1088             :             unsigned int i;
    1089           0 :             for (i = 0; i < toklen[tokmarker]; i++)
    1090             :             {
    1091           0 :               if (!isdigit(*p++))
    1092             :               {
    1093           0 :                 lstyle = 0;
    1094           0 :                 break;
    1095             :               }
    1096             :             }
    1097           0 :             if (lstyle)
    1098             :             {
    1099           0 :               month_num = 0;
    1100           0 :               p = tokens[tokmarker+1];
    1101           0 :               for (i = 0; i < (12*3); i+=3)
    1102             :               {
    1103           0 :                 if (p[0] == month_names[i+0] &&
    1104           0 :                     p[1] == month_names[i+1] &&
    1105           0 :                     p[2] == month_names[i+2])
    1106           0 :                   break;
    1107           0 :                 month_num++;
    1108             :               }
    1109           0 :               if (month_num >= 12)
    1110           0 :                 lstyle = 0;
    1111             :             }
    1112             :           } /* relative position test */
    1113             :         } /* for (pos = (numtoks-5); !lstyle && pos > 1; pos--) */
    1114             :       } /* if (lstyle == 'U') */
    1115             : 
    1116           0 :       if (lstyle == 'U')
    1117             :       {
    1118           0 :         state->parsed_one = 1;
    1119           0 :         state->lstyle = lstyle;
    1120             : 
    1121           0 :         result->fe_cinfs = 0;
    1122           0 :         result->fe_type = '?';
    1123           0 :         if (*tokens[0] == 'd' || *tokens[0] == 'l')
    1124           0 :           result->fe_type = *tokens[0];
    1125           0 :         else if (*tokens[0] == 'D')
    1126           0 :           result->fe_type = 'd';
    1127           0 :         else if (*tokens[0] == '-' || *tokens[0] == 'F')
    1128           0 :           result->fe_type = 'f'; /* (hopefully a regular file) */
    1129             : 
    1130           0 :         if (result->fe_type != 'd')
    1131             :         {
    1132           0 :           pos = toklen[tokmarker];
    1133           0 :           if (pos > (sizeof(result->fe_size)-1))
    1134           0 :             pos = (sizeof(result->fe_size)-1);
    1135           0 :           memcpy( result->fe_size, tokens[tokmarker], pos );
    1136           0 :           result->fe_size[pos] = '\0';
    1137             :         }
    1138             : 
    1139           0 :         result->fe_time.tm_month  = month_num;
    1140           0 :         result->fe_time.tm_mday = atoi(tokens[tokmarker+2]);
    1141           0 :         if (result->fe_time.tm_mday == 0)
    1142           0 :           result->fe_time.tm_mday++;
    1143             : 
    1144           0 :         p = tokens[tokmarker+3];
    1145           0 :         pos = (unsigned int)atoi(p);
    1146           0 :         if (p[1] == ':') /* one digit hour */
    1147           0 :           p--;
    1148           0 :         if (p[2] != ':') /* year */
    1149             :         {
    1150           0 :           result->fe_time.tm_year = pos;
    1151             :         }
    1152             :         else
    1153             :         {
    1154           0 :           result->fe_time.tm_hour = pos;
    1155           0 :           result->fe_time.tm_min  = atoi(p+3);
    1156           0 :           if (p[5] == ':')
    1157           0 :             result->fe_time.tm_sec = atoi(p+6);
    1158             : 
    1159           0 :           if (!state->now_time)
    1160             :           {
    1161           0 :             state->now_time = PR_Now();
    1162           0 :             PR_ExplodeTime((state->now_time), PR_LocalTimeParameters, &(state->now_tm) );
    1163             :           }
    1164             : 
    1165           0 :           result->fe_time.tm_year = state->now_tm.tm_year;
    1166           0 :           if ( (( state->now_tm.tm_month << 5) + state->now_tm.tm_mday) <
    1167           0 :                ((result->fe_time.tm_month << 5) + result->fe_time.tm_mday) )
    1168           0 :             result->fe_time.tm_year--;
    1169             : 
    1170             :         } /* time/year */
    1171             : 
    1172             :         // The length of the whole date string should be 12. On AIX the length
    1173             :         // is only 11 when the year is present in the date string and there is
    1174             :         // 1 padding space at the end of the string. In both cases the filename
    1175             :         // starts at offset 13 from the start of the date string.
    1176             :         // Don't care about leading spaces when the date string has different
    1177             :         // format or when old Hellsoft output was detected.
    1178             :         {
    1179           0 :           const char *date_start = tokens[tokmarker+1];
    1180           0 :           const char *date_end = tokens[tokmarker+3] + toklen[tokmarker+3];
    1181           0 :           if (!is_old_Hellsoft && ((date_end - date_start) == 12 ||
    1182           0 :               ((date_end - date_start) == 11 && date_end[1] == ' ')))
    1183           0 :             result->fe_fname = date_start + 13;
    1184             :           else
    1185           0 :             result->fe_fname = tokens[tokmarker+4];
    1186             :         }
    1187             : 
    1188           0 :         result->fe_fnlen = (&(line[linelen]))
    1189           0 :                            - (result->fe_fname);
    1190             : 
    1191           0 :         if (result->fe_type == 'l' && result->fe_fnlen > 4)
    1192             :         {
    1193             :           /* First try to use result->fe_size to find " -> " sequence.
    1194             :              This can give proper result for cases like "aaa -> bbb -> ccc". */
    1195           0 :           uint32_t fe_size = atoi(result->fe_size);
    1196             : 
    1197           0 :           if (result->fe_fnlen > (fe_size + 4) &&
    1198           0 :               PL_strncmp(result->fe_fname + result->fe_fnlen - fe_size - 4 , " -> ", 4) == 0)
    1199             :           {
    1200           0 :             result->fe_lname = result->fe_fname + (result->fe_fnlen - fe_size);
    1201           0 :             result->fe_lnlen = (&(line[linelen])) - (result->fe_lname);
    1202           0 :             result->fe_fnlen -= fe_size + 4;
    1203             :           }
    1204             :           else
    1205             :           {
    1206             :             /* Search for sequence " -> " from the end for case when there are
    1207             :                more occurrences. F.e. if ftpd returns "a -> b -> c" assume
    1208             :                "a -> b" as a name. Powerusers can remove unnecessary parts
    1209             :                manually but there is no way to follow the link when some
    1210             :                essential part is missing. */
    1211           0 :             p = result->fe_fname + (result->fe_fnlen - 5);
    1212           0 :             for (pos = (result->fe_fnlen - 5); pos > 0; pos--)
    1213             :             {
    1214           0 :               if (PL_strncmp(p, " -> ", 4) == 0)
    1215             :               {
    1216           0 :                 result->fe_lname = p + 4;
    1217           0 :                 result->fe_lnlen = (&(line[linelen]))
    1218           0 :                                  - (result->fe_lname);
    1219           0 :                 result->fe_fnlen = pos;
    1220           0 :                 break;
    1221             :               }
    1222           0 :               p--;
    1223             :             }
    1224             :           }
    1225             :         }
    1226             : 
    1227             : #if defined(SUPPORT_LSLF) /* some (very rare) servers return ls -lF */
    1228             :         if (result->fe_fnlen > 1)
    1229             :         {
    1230             :           p = result->fe_fname[result->fe_fnlen-1];
    1231             :           pos = result->fe_type;
    1232             :           if (pos == 'd') {
    1233             :              if (*p == '/') result->fe_fnlen--; /* directory */
    1234             :           } else if (pos == 'l') {
    1235             :              if (*p == '@') result->fe_fnlen--; /* symlink */
    1236             :           } else if (pos == 'f') {
    1237             :              if (*p == '*') result->fe_fnlen--; /* executable */
    1238             :           } else if (*p == '=' || *p == '%' || *p == '|') {
    1239             :             result->fe_fnlen--; /* socket, whiteout, fifo */
    1240             :           }
    1241             :         }
    1242             : #endif
    1243             : 
    1244             :         /* the caller should do this (if dropping "." and ".." is desired)
    1245             :         if (result->fe_type == 'd' && result->fe_fname[0] == '.' &&
    1246             :             (result->fe_fnlen == 1 || (result->fe_fnlen == 2 &&
    1247             :                                       result->fe_fname[1] == '.')))
    1248             :           return '?';
    1249             :         */
    1250             : 
    1251           0 :         return result->fe_type;
    1252             : 
    1253             :       } /* if (lstyle == 'U') */
    1254             : 
    1255             :     } /* if (!lstyle && (!state->lstyle || state->lstyle == 'U')) */
    1256             : #endif
    1257             : 
    1258             :     /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
    1259             : 
    1260             : #if defined(SUPPORT_W16) /* 16bit Windows */
    1261           0 :     if (!lstyle && (!state->lstyle || state->lstyle == 'w'))
    1262             :     {       /* old SuperTCP suite FTP server for Win3.1 */
    1263             :             /* old NetManage Chameleon TCP/IP suite FTP server for Win3.1 */
    1264             :       /*
    1265             :       * SuperTCP dirlist from the mirror.pl project
    1266             :       * mon/day/year separator may be '/' or '-'.
    1267             :       * .               <DIR>           11-16-94        17:16
    1268             :       * ..              <DIR>           11-16-94        17:16
    1269             :       * INSTALL         <DIR>           11-16-94        17:17
    1270             :       * CMT             <DIR>           11-21-94        10:17
    1271             :       * DESIGN1.DOC          11264      05-11-95        14:20
    1272             :       * README.TXT            1045      05-10-95        11:01
    1273             :       * WPKIT1.EXE          960338      06-21-95        17:01
    1274             :       * CMT.CSV                  0      07-06-95        14:56
    1275             :       *
    1276             :       * Chameleon dirlist guessed from lynx
    1277             :       * .               <DIR>      Nov 16 1994 17:16
    1278             :       * ..              <DIR>      Nov 16 1994 17:16
    1279             :       * INSTALL         <DIR>      Nov 16 1994 17:17
    1280             :       * CMT             <DIR>      Nov 21 1994 10:17
    1281             :       * DESIGN1.DOC     11264      May 11 1995 14:20   A
    1282             :       * README.TXT       1045      May 10 1995 11:01
    1283             :       * WPKIT1.EXE     960338      Jun 21 1995 17:01   R
    1284             :       * CMT.CSV             0      Jul 06 1995 14:56   RHA
    1285             :       */
    1286           0 :       if (numtoks >= 4 && toklen[0] < 13 &&
    1287           0 :           ((toklen[1] == 5 && *tokens[1] == '<') || isdigit(*tokens[1])) )
    1288             :       {
    1289           0 :         if (numtoks == 4
    1290           0 :          && (toklen[2] == 8 || toklen[2] == 9)
    1291           0 :          && (((tokens[2][2]) == '/' && (tokens[2][5]) == '/') ||
    1292           0 :              ((tokens[2][2]) == '-' && (tokens[2][5]) == '-'))
    1293           0 :          && (toklen[3] == 4 || toklen[3] == 5)
    1294           0 :          && (tokens[3][toklen[3]-3]) == ':'
    1295           0 :          && isdigit(tokens[2][0]) && isdigit(tokens[2][1])
    1296           0 :          && isdigit(tokens[2][3]) && isdigit(tokens[2][4])
    1297           0 :          && isdigit(tokens[2][6]) && isdigit(tokens[2][7])
    1298           0 :          && (toklen[2] < 9 || isdigit(tokens[2][8]))
    1299           0 :          && isdigit(tokens[3][toklen[3]-1]) && isdigit(tokens[3][toklen[3]-2])
    1300           0 :          && isdigit(tokens[3][toklen[3]-4]) && isdigit(*tokens[3])
    1301             :          )
    1302             :         {
    1303           0 :           lstyle = 'w';
    1304             :         }
    1305           0 :         else if ((numtoks == 6 || numtoks == 7)
    1306           0 :          && toklen[2] == 3 && toklen[3] == 2
    1307           0 :          && toklen[4] == 4 && toklen[5] == 5
    1308           0 :          && (tokens[5][2]) == ':'
    1309           0 :          && isalpha(tokens[2][0]) && isalpha(tokens[2][1])
    1310           0 :          &&                          isalpha(tokens[2][2])
    1311           0 :          && isdigit(tokens[3][0]) && isdigit(tokens[3][1])
    1312           0 :          && isdigit(tokens[4][0]) && isdigit(tokens[4][1])
    1313           0 :          && isdigit(tokens[4][2]) && isdigit(tokens[4][3])
    1314           0 :          && isdigit(tokens[5][0]) && isdigit(tokens[5][1])
    1315           0 :          && isdigit(tokens[5][3]) && isdigit(tokens[5][4])
    1316             :          /* could also check that (&(tokens[5][5]) - tokens[2]) == 17 */
    1317             :         )
    1318             :         {
    1319           0 :           lstyle = 'w';
    1320             :         }
    1321           0 :         if (lstyle && state->lstyle != lstyle) /* first time */
    1322             :         {
    1323           0 :           p = tokens[1];
    1324           0 :           if (toklen[1] != 5 || p[0] != '<' || p[1] != 'D' ||
    1325           0 :                  p[2] != 'I' || p[3] != 'R' || p[4] != '>')
    1326             :           {
    1327           0 :             for (pos = 0; lstyle && pos < toklen[1]; pos++)
    1328             :             {
    1329           0 :               if (!isdigit(*p++))
    1330           0 :                 lstyle = 0;
    1331             :             }
    1332             :           } /* not <DIR> */
    1333             :         } /* if (first time) */
    1334             :       } /* if (numtoks == ...) */
    1335             : 
    1336           0 :       if (lstyle == 'w')
    1337             :       {
    1338           0 :         state->parsed_one = 1;
    1339           0 :         state->lstyle = lstyle;
    1340             : 
    1341           0 :         result->fe_cinfs = 1;
    1342           0 :         result->fe_fname = tokens[0];
    1343           0 :         result->fe_fnlen = toklen[0];
    1344           0 :         result->fe_type = 'd';
    1345             : 
    1346           0 :         p = tokens[1];
    1347           0 :         if (isdigit(*p))
    1348             :         {
    1349           0 :           result->fe_type = 'f';
    1350           0 :           pos = toklen[1];
    1351           0 :           if (pos > (sizeof(result->fe_size)-1))
    1352           0 :             pos = sizeof(result->fe_size)-1;
    1353           0 :           memcpy( result->fe_size, p, pos );
    1354           0 :           result->fe_size[pos] = '\0';
    1355             :         }
    1356             : 
    1357           0 :         p = tokens[2];
    1358           0 :         if (toklen[2] == 3) /* Chameleon */
    1359             :         {
    1360           0 :           tbuf[0] = toupper(p[0]);
    1361           0 :           tbuf[1] = tolower(p[1]);
    1362           0 :           tbuf[2] = tolower(p[2]);
    1363           0 :           for (pos = 0; pos < (12*3); pos+=3)
    1364             :           {
    1365           0 :             if (tbuf[0] == month_names[pos+0] &&
    1366           0 :                 tbuf[1] == month_names[pos+1] &&
    1367           0 :                 tbuf[2] == month_names[pos+2])
    1368             :             {
    1369           0 :               result->fe_time.tm_month = pos/3;
    1370           0 :               result->fe_time.tm_mday = atoi(tokens[3]);
    1371           0 :               result->fe_time.tm_year = atoi(tokens[4]) - 1900;
    1372           0 :               break;
    1373             :             }
    1374             :           }
    1375           0 :           pos = 5; /* Chameleon toknum of date field */
    1376             :         }
    1377             :         else
    1378             :         {
    1379           0 :           result->fe_time.tm_month = atoi(p+0)-1;
    1380           0 :           result->fe_time.tm_mday = atoi(p+3);
    1381           0 :           result->fe_time.tm_year = atoi(p+6);
    1382           0 :           if (result->fe_time.tm_year < 80) /* SuperTCP */
    1383           0 :             result->fe_time.tm_year += 100;
    1384             : 
    1385           0 :           pos = 3; /* SuperTCP toknum of date field */
    1386             :         }
    1387             : 
    1388           0 :         result->fe_time.tm_hour = atoi(tokens[pos]);
    1389           0 :         result->fe_time.tm_min = atoi(&(tokens[pos][toklen[pos]-2]));
    1390             : 
    1391             :         /* the caller should do this (if dropping "." and ".." is desired)
    1392             :         if (result->fe_type == 'd' && result->fe_fname[0] == '.' &&
    1393             :             (result->fe_fnlen == 1 || (result->fe_fnlen == 2 &&
    1394             :                                       result->fe_fname[1] == '.')))
    1395             :           return '?';
    1396             :         */
    1397             : 
    1398           0 :         return result->fe_type;
    1399             :       } /* (lstyle == 'w') */
    1400             : 
    1401             :     } /* if (!lstyle && (!state->lstyle || state->lstyle == 'w'))  */
    1402             : #endif
    1403             : 
    1404             :     /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
    1405             : 
    1406             : #if defined(SUPPORT_DLS) /* dls -dtR */
    1407           0 :     if (!lstyle &&
    1408           0 :        (state->lstyle == 'D' || (!state->lstyle && state->numlines == 1)))
    1409             :        /* /bin/dls lines have to be immediately recognizable (first line) */
    1410             :     {
    1411             :       /* I haven't seen an FTP server that delivers a /bin/dls listing,
    1412             :        * but can infer the format from the lynx and mirror.pl projects.
    1413             :        * Both formats are supported.
    1414             :        *
    1415             :        * Lynx says:
    1416             :        * README              763  Information about this server\0
    1417             :        * bin/                  -  \0
    1418             :        * etc/                  =  \0
    1419             :        * ls-lR                 0  \0
    1420             :        * ls-lR.Z               3  \0
    1421             :        * pub/                  =  Public area\0
    1422             :        * usr/                  -  \0
    1423             :        * morgan               14  -> ../real/morgan\0
    1424             :        * TIMIT.mostlikely.Z\0
    1425             :        *                   79215  \0
    1426             :        *
    1427             :        * mirror.pl says:
    1428             :        * filename:  ^(\S*)\s+
    1429             :        * size:      (\-|\=|\d+)\s+
    1430             :        * month/day: ((\w\w\w\s+\d+|\d+\s+\w\w\w)\s+
    1431             :        * time/year: (\d+:\d+|\d\d\d\d))\s+
    1432             :        * rest:      (.+)
    1433             :        *
    1434             :        * README              763  Jul 11 21:05  Information about this server
    1435             :        * bin/                  -  Apr 28  1994
    1436             :        * etc/                  =  11 Jul 21:04
    1437             :        * ls-lR                 0   6 Aug 17:14
    1438             :        * ls-lR.Z               3  05 Sep 1994
    1439             :        * pub/                  =  Jul 11 21:04  Public area
    1440             :        * usr/                  -  Sep  7 09:39
    1441             :        * morgan               14  Apr 18 09:39  -> ../real/morgan
    1442             :        * TIMIT.mostlikely.Z
    1443             :        *                   79215  Jul 11 21:04
    1444             :       */
    1445           0 :       if (!state->lstyle && line[linelen-1] == ':' &&
    1446           0 :           linelen >= 2 && toklen[numtoks-1] != 1)
    1447             :       {
    1448             :         /* code in mirror.pl suggests that a listing may be preceded
    1449             :          * by a PWD line in the form "/some/dir/names/here:"
    1450             :          * but does not necessarily begin with '/'. *sigh*
    1451             :         */
    1452           0 :         pos = 0;
    1453           0 :         p = line;
    1454           0 :         while (pos < (linelen-1))
    1455             :         {
    1456             :           /* illegal (or extremely unusual) chars in a dirspec */
    1457           0 :           if (*p == '<' || *p == '|' || *p == '>' ||
    1458           0 :               *p == '?' || *p == '*' || *p == '\\')
    1459             :             break;
    1460           0 :           if (*p == '/' && pos < (linelen-2) && p[1] == '/')
    1461           0 :             break;
    1462           0 :           pos++;
    1463           0 :           p++;
    1464             :         }
    1465           0 :         if (pos == (linelen-1))
    1466             :         {
    1467           0 :           state->lstyle = 'D';
    1468           0 :           return '?';
    1469             :         }
    1470             :       }
    1471             : 
    1472           0 :       if (!lstyle && numtoks >= 2)
    1473             :       {
    1474           0 :         pos = 22; /* pos of (\d+|-|=) if this is not part of a multiline */
    1475           0 :         if (state->lstyle && carry_buf_len) /* first is from previous line */
    1476           0 :           pos = toklen[1]-1; /* and is 'as-is' (may contain whitespace) */
    1477             : 
    1478           0 :         if (linelen > pos)
    1479             :         {
    1480           0 :           p = &line[pos];
    1481           0 :           if ((*p == '-' || *p == '=' || isdigit(*p)) &&
    1482           0 :               ((linelen == (pos+1)) ||
    1483           0 :                (linelen >= (pos+3) && p[1] == ' ' && p[2] == ' ')) )
    1484             :           {
    1485           0 :             tokmarker = 1;
    1486           0 :             if (!carry_buf_len)
    1487             :             {
    1488           0 :               pos = 1;
    1489           0 :               while (pos < numtoks && (tokens[pos]+toklen[pos]) < (&line[23]))
    1490           0 :                 pos++;
    1491           0 :               tokmarker = 0;
    1492           0 :               if ((tokens[pos]+toklen[pos]) == (&line[23]))
    1493           0 :                 tokmarker = pos;
    1494             :             }
    1495           0 :             if (tokmarker)
    1496             :             {
    1497           0 :               lstyle = 'D';
    1498           0 :               if (*tokens[tokmarker] == '-' || *tokens[tokmarker] == '=')
    1499             :               {
    1500           0 :                 if (toklen[tokmarker] != 1 ||
    1501           0 :                    (tokens[tokmarker-1][toklen[tokmarker-1]-1]) != '/')
    1502           0 :                   lstyle = 0;
    1503             :               }
    1504             :               else
    1505             :               {
    1506           0 :                 for (pos = 0; lstyle && pos < toklen[tokmarker]; pos++)
    1507             :                 {
    1508           0 :                   if (!isdigit(tokens[tokmarker][pos]))
    1509           0 :                     lstyle = 0;
    1510             :                 }
    1511             :               }
    1512           0 :               if (lstyle && !state->lstyle) /* first time */
    1513             :               {
    1514             :                 /* scan for illegal (or incredibly unusual) chars in fname */
    1515           0 :                 for (p = tokens[0]; lstyle &&
    1516           0 :                      p < &(tokens[tokmarker-1][toklen[tokmarker-1]]); p++)
    1517             :                 {
    1518           0 :                   if (*p == '<' || *p == '|' || *p == '>' ||
    1519           0 :                       *p == '?' || *p == '*' || *p == '/' || *p == '\\')
    1520           0 :                     lstyle = 0;
    1521             :                 }
    1522             :               }
    1523             : 
    1524             :             } /* size token found */
    1525             :           } /* expected chars behind expected size token */
    1526             :         } /* if (linelen > pos) */
    1527             :       } /* if (!lstyle && numtoks >= 2) */
    1528             : 
    1529           0 :       if (!lstyle && state->lstyle == 'D' && !carry_buf_len)
    1530             :       {
    1531             :         /* the filename of a multi-line entry can be identified
    1532             :          * correctly only if dls format had been previously established.
    1533             :          * This should always be true because there should be entries
    1534             :          * for '.' and/or '..' and/or CWD that precede the rest of the
    1535             :          * listing.
    1536             :         */
    1537           0 :         pos = linelen;
    1538           0 :         if (pos > (sizeof(state->carry_buf)-1))
    1539           0 :           pos = sizeof(state->carry_buf)-1;
    1540           0 :         memcpy( state->carry_buf, line, pos );
    1541           0 :         state->carry_buf_len = pos;
    1542           0 :         return '?';
    1543             :       }
    1544             : 
    1545           0 :       if (lstyle == 'D')
    1546             :       {
    1547           0 :         state->parsed_one = 1;
    1548           0 :         state->lstyle = lstyle;
    1549             : 
    1550           0 :         p = &(tokens[tokmarker-1][toklen[tokmarker-1]]);
    1551           0 :         result->fe_fname = tokens[0];
    1552           0 :         result->fe_fnlen = p - tokens[0];
    1553           0 :         result->fe_type  = 'f';
    1554             : 
    1555           0 :         if (result->fe_fname[result->fe_fnlen-1] == '/')
    1556             :         {
    1557           0 :           if (result->fe_lnlen == 1)
    1558           0 :             result->fe_type = '?';
    1559             :           else
    1560             :           {
    1561           0 :             result->fe_fnlen--;
    1562           0 :             result->fe_type  = 'd';
    1563             :           }
    1564             :         }
    1565           0 :         else if (isdigit(*tokens[tokmarker]))
    1566             :         {
    1567           0 :           pos = toklen[tokmarker];
    1568           0 :           if (pos > (sizeof(result->fe_size)-1))
    1569           0 :             pos = sizeof(result->fe_size)-1;
    1570           0 :           memcpy( result->fe_size, tokens[tokmarker], pos );
    1571           0 :           result->fe_size[pos] = '\0';
    1572             :         }
    1573             : 
    1574           0 :         if ((tokmarker+3) < numtoks &&
    1575           0 :               (&(tokens[numtoks-1][toklen[numtoks-1]]) -
    1576           0 :                tokens[tokmarker+1]) >= (1+1+3+1+4) )
    1577             :         {
    1578           0 :           pos = (tokmarker+3);
    1579           0 :           p = tokens[pos];
    1580           0 :           pos = toklen[pos];
    1581             : 
    1582           0 :           if ((pos == 4 || pos == 5)
    1583           0 :           &&  isdigit(*p) && isdigit(p[pos-1]) && isdigit(p[pos-2])
    1584           0 :           &&  ((pos == 5 && p[2] == ':') ||
    1585           0 :                (pos == 4 && (isdigit(p[1]) || p[1] == ':')))
    1586             :              )
    1587             :           {
    1588           0 :             month_num = tokmarker+1; /* assumed position of month field */
    1589           0 :             pos = tokmarker+2;       /* assumed position of mday field */
    1590           0 :             if (isdigit(*tokens[month_num])) /* positions are reversed */
    1591             :             {
    1592           0 :               month_num++;
    1593           0 :               pos--;
    1594             :             }
    1595           0 :             p = tokens[month_num];
    1596           0 :             if (isdigit(*tokens[pos])
    1597           0 :             && (toklen[pos] == 1 ||
    1598           0 :                   (toklen[pos] == 2 && isdigit(tokens[pos][1])))
    1599           0 :             && toklen[month_num] == 3
    1600           0 :             && isalpha(*p) && isalpha(p[1]) && isalpha(p[2])  )
    1601             :             {
    1602           0 :               pos = atoi(tokens[pos]);
    1603           0 :               if (pos > 0 && pos <= 31)
    1604             :               {
    1605           0 :                 result->fe_time.tm_mday = pos;
    1606           0 :                 month_num = 1;
    1607           0 :                 for (pos = 0; pos < (12*3); pos+=3)
    1608             :                 {
    1609           0 :                   if (p[0] == month_names[pos+0] &&
    1610           0 :                       p[1] == month_names[pos+1] &&
    1611           0 :                       p[2] == month_names[pos+2])
    1612           0 :                     break;
    1613           0 :                   month_num++;
    1614             :                 }
    1615           0 :                 if (month_num > 12)
    1616           0 :                   result->fe_time.tm_mday = 0;
    1617             :                 else
    1618           0 :                   result->fe_time.tm_month = month_num - 1;
    1619             :               }
    1620             :             }
    1621           0 :             if (result->fe_time.tm_mday)
    1622             :             {
    1623           0 :               tokmarker += 3; /* skip mday/mon/yrtime (to find " -> ") */
    1624           0 :               p = tokens[tokmarker];
    1625             : 
    1626           0 :               pos = atoi(p);
    1627           0 :               if (pos > 24)
    1628           0 :                 result->fe_time.tm_year = pos-1900;
    1629             :               else
    1630             :               {
    1631           0 :                 if (p[1] == ':')
    1632           0 :                   p--;
    1633           0 :                 result->fe_time.tm_hour = pos;
    1634           0 :                 result->fe_time.tm_min = atoi(p+3);
    1635           0 :                 if (!state->now_time)
    1636             :                 {
    1637           0 :                   state->now_time = PR_Now();
    1638           0 :                   PR_ExplodeTime((state->now_time), PR_LocalTimeParameters, &(state->now_tm) );
    1639             :                 }
    1640           0 :                 result->fe_time.tm_year = state->now_tm.tm_year;
    1641           0 :                 if ( (( state->now_tm.tm_month  << 4) + state->now_tm.tm_mday) <
    1642           0 :                      ((result->fe_time.tm_month << 4) + result->fe_time.tm_mday) )
    1643           0 :                   result->fe_time.tm_year--;
    1644             :               } /* got year or time */
    1645             :             } /* got month/mday */
    1646             :           } /* may have year or time */
    1647             :         } /* enough remaining to possibly have date/time */
    1648             : 
    1649           0 :         if (numtoks > (tokmarker+2))
    1650             :         {
    1651           0 :           pos = tokmarker+1;
    1652           0 :           p = tokens[pos];
    1653           0 :           if (toklen[pos] == 2 && *p == '-' && p[1] == '>')
    1654             :           {
    1655           0 :             p = &(tokens[numtoks-1][toklen[numtoks-1]]);
    1656           0 :             result->fe_type  = 'l';
    1657           0 :             result->fe_lname = tokens[pos+1];
    1658           0 :             result->fe_lnlen = p - result->fe_lname;
    1659           0 :             if (result->fe_lnlen > 1 &&
    1660           0 :                 result->fe_lname[result->fe_lnlen-1] == '/')
    1661           0 :               result->fe_lnlen--;
    1662             :           }
    1663             :         } /* if (numtoks > (tokmarker+2)) */
    1664             : 
    1665             :         /* the caller should do this (if dropping "." and ".." is desired)
    1666             :         if (result->fe_type == 'd' && result->fe_fname[0] == '.' &&
    1667             :             (result->fe_fnlen == 1 || (result->fe_fnlen == 2 &&
    1668             :                                       result->fe_fname[1] == '.')))
    1669             :           return '?';
    1670             :         */
    1671             : 
    1672           0 :         return result->fe_type;
    1673             : 
    1674             :       } /* if (lstyle == 'D') */
    1675             :     } /* if (!lstyle && (!state->lstyle || state->lstyle == 'D')) */
    1676             : #endif
    1677             : 
    1678             :     /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
    1679             : 
    1680             :   } /* if (linelen > 0) */
    1681             : 
    1682           0 :   return ParsingFailed(state);
    1683             : }
    1684             : 
    1685             : /* ==================================================================== */
    1686             : /* standalone testing                                                   */
    1687             : /* ==================================================================== */
    1688             : #if 0
    1689             : 
    1690             : #include <stdio.h>
    1691             : 
    1692             : static int do_it(FILE *outfile,
    1693             :                  char *line, size_t linelen, struct list_state *state,
    1694             :                  char **cmnt_buf, unsigned int *cmnt_buf_sz,
    1695             :                  char **list_buf, unsigned int *list_buf_sz )
    1696             : {
    1697             :   struct list_result result;
    1698             :   char *p;
    1699             :   int rc;
    1700             : 
    1701             :   rc = ParseFTPList( line, state, &result );
    1702             : 
    1703             :   if (!outfile)
    1704             :   {
    1705             :     outfile = stdout;
    1706             :     if (rc == '?')
    1707             :       fprintf(outfile, "junk: %.*s\n", (int)linelen, line );
    1708             :     else if (rc == '"')
    1709             :       fprintf(outfile, "cmnt: %.*s\n", (int)linelen, line );
    1710             :     else
    1711             :       fprintf(outfile,
    1712             :               "list: %02u-%02u-%02u  %02u:%02u%cM %20s %.*s%s%.*s\n",
    1713             :               (result.fe_time.tm_mday ? (result.fe_time.tm_month + 1) : 0),
    1714             :               result.fe_time.tm_mday,
    1715             :               (result.fe_time.tm_mday ? (result.fe_time.tm_year % 100) : 0),
    1716             :               result.fe_time.tm_hour -
    1717             :                 ((result.fe_time.tm_hour > 12)?(12):(0)),
    1718             :               result.fe_time.tm_min,
    1719             :               ((result.fe_time.tm_hour >= 12) ? 'P' : 'A'),
    1720             :               (rc == 'd' ? "<DIR>         " :
    1721             :               (rc == 'l' ? "<JUNCTION>    " : result.fe_size)),
    1722             :               (int)result.fe_fnlen, result.fe_fname,
    1723             :               ((rc == 'l' && result.fe_lnlen) ? " -> " : ""),
    1724             :               (int)((rc == 'l' && result.fe_lnlen) ? result.fe_lnlen : 0),
    1725             :               ((rc == 'l' && result.fe_lnlen) ? result.fe_lname : "") );
    1726             :   }
    1727             :   else if (rc != '?') /* NOT junk */
    1728             :   {
    1729             :     char **bufp = list_buf;
    1730             :     unsigned int *bufz = list_buf_sz;
    1731             : 
    1732             :     if (rc == '"') /* comment - make it a 'result' */
    1733             :     {
    1734             :       memset( &result, 0, sizeof(result));
    1735             :       result.fe_fname = line;
    1736             :       result.fe_fnlen = linelen;
    1737             :       result.fe_type = 'f';
    1738             :       if (line[linelen-1] == '/')
    1739             :       {
    1740             :         result.fe_type = 'd';
    1741             :         result.fe_fnlen--;
    1742             :       }
    1743             :       bufp = cmnt_buf;
    1744             :       bufz = cmnt_buf_sz;
    1745             :       rc = result.fe_type;
    1746             :     }
    1747             : 
    1748             :     linelen = 80 + result.fe_fnlen + result.fe_lnlen;
    1749             :     p = (char *)realloc( *bufp, *bufz + linelen );
    1750             :     if (!p)
    1751             :       return -1;
    1752             :     sprintf( &p[*bufz],
    1753             :              "%02u-%02u-%04u  %02u:%02u:%02u %20s %.*s%s%.*s\n",
    1754             :               (result.fe_time.tm_mday ? (result.fe_time.tm_month + 1) : 0),
    1755             :               result.fe_time.tm_mday,
    1756             :               (result.fe_time.tm_mday ? (result.fe_time.tm_year + 1900) : 0),
    1757             :               result.fe_time.tm_hour,
    1758             :               result.fe_time.tm_min,
    1759             :               result.fe_time.tm_sec,
    1760             :               (rc == 'd' ? "<DIR>         " :
    1761             :               (rc == 'l' ? "<JUNCTION>    " : result.fe_size)),
    1762             :               (int)result.fe_fnlen, result.fe_fname,
    1763             :               ((rc == 'l' && result.fe_lnlen) ? " -> " : ""),
    1764             :               (int)((rc == 'l' && result.fe_lnlen) ? result.fe_lnlen : 0),
    1765             :               ((rc == 'l' && result.fe_lnlen) ? result.fe_lname : "") );
    1766             :     linelen = strlen(&p[*bufz]);
    1767             :     *bufp = p;
    1768             :     *bufz = *bufz + linelen;
    1769             :   }
    1770             :   return 0;
    1771             : }
    1772             : 
    1773             : int main(int argc, char *argv[])
    1774             : {
    1775             :   FILE *infile = (FILE *)0;
    1776             :   FILE *outfile = (FILE *)0;
    1777             :   int need_close_in = 0;
    1778             :   int need_close_out = 0;
    1779             : 
    1780             :   if (argc > 1)
    1781             :   {
    1782             :     infile = stdin;
    1783             :     if (strcmp(argv[1], "-") == 0)
    1784             :       need_close_in = 0;
    1785             :     else if ((infile = fopen(argv[1], "r")) != ((FILE *)0))
    1786             :       need_close_in = 1;
    1787             :     else
    1788             :       fprintf(stderr, "Unable to open input file '%s'\n", argv[1]);
    1789             :   }
    1790             :   if (infile && argc > 2)
    1791             :   {
    1792             :     outfile = stdout;
    1793             :     if (strcmp(argv[2], "-") == 0)
    1794             :       need_close_out = 0;
    1795             :     else if ((outfile = fopen(argv[2], "w")) != ((FILE *)0))
    1796             :       need_close_out = 1;
    1797             :     else
    1798             :     {
    1799             :       fprintf(stderr, "Unable to open output file '%s'\n", argv[2]);
    1800             :       fclose(infile);
    1801             :       infile = (FILE *)0;
    1802             :     }
    1803             :   }
    1804             : 
    1805             :   if (!infile)
    1806             :   {
    1807             :     char *appname = &(argv[0][strlen(argv[0])]);
    1808             :     while (appname > argv[0])
    1809             :     {
    1810             :       appname--;
    1811             :       if (*appname == '/' || *appname == '\\' || *appname == ':')
    1812             :       {
    1813             :         appname++;
    1814             :         break;
    1815             :       }
    1816             :     }
    1817             :     fprintf(stderr,
    1818             :         "Usage: %s <inputfilename> [<outputfilename>]\n"
    1819             :         "\nIf an outout file is specified the results will be"
    1820             :         "\nbe post-processed, and only the file entries will appear"
    1821             :         "\n(or all comments if there are no file entries)."
    1822             :         "\nNot specifying an output file causes %s to run in \"debug\""
    1823             :         "\nmode, ie results are printed as lines are parsed."
    1824             :         "\nIf a filename is a single dash ('-'), stdin/stdout is used."
    1825             :         "\n", appname, appname );
    1826             :   }
    1827             :   else
    1828             :   {
    1829             :     char *cmnt_buf = (char *)0;
    1830             :     unsigned int cmnt_buf_sz = 0;
    1831             :     char *list_buf = (char *)0;
    1832             :     unsigned int list_buf_sz = 0;
    1833             : 
    1834             :     struct list_state state;
    1835             :     char line[512];
    1836             : 
    1837             :     memset( &state, 0, sizeof(state) );
    1838             :     while (fgets(line, sizeof(line), infile))
    1839             :     {
    1840             :       size_t linelen = strlen(line);
    1841             :       if (linelen < (sizeof(line)-1))
    1842             :       {
    1843             :         if (linelen > 0 && line[linelen-1] == '\n')
    1844             :           linelen--;
    1845             :         if (do_it( outfile, line, linelen, &state,
    1846             :                    &cmnt_buf, &cmnt_buf_sz, &list_buf, &list_buf_sz) != 0)
    1847             :         {
    1848             :           fprintf(stderr, "Insufficient memory. Listing may be incomplete.\n");
    1849             :           break;
    1850             :         }
    1851             :       }
    1852             :       else
    1853             :       {
    1854             :         /* no '\n' found. drop this and everything up to the next '\n' */
    1855             :         fprintf(stderr, "drop: %.*s", (int)linelen, line );
    1856             :         while (linelen == sizeof(line))
    1857             :         {
    1858             :           if (!fgets(line, sizeof(line), infile))
    1859             :             break;
    1860             :           linelen = 0;
    1861             :           while (linelen < sizeof(line) && line[linelen] != '\n')
    1862             :             linelen++;
    1863             :           fprintf(stderr, "%.*s", (int)linelen, line );
    1864             :         }
    1865             :         fprintf(stderr, "\n");
    1866             :       }
    1867             :     }
    1868             :     if (outfile)
    1869             :     {
    1870             :       if (list_buf)
    1871             :         fwrite( list_buf, 1, list_buf_sz, outfile );
    1872             :       else if (cmnt_buf)
    1873             :         fwrite( cmnt_buf, 1, cmnt_buf_sz, outfile );
    1874             :     }
    1875             :     if (list_buf)
    1876             :       free(list_buf);
    1877             :     if (cmnt_buf)
    1878             :       free(cmnt_buf);
    1879             : 
    1880             :     if (need_close_in)
    1881             :       fclose(infile);
    1882             :     if (outfile && need_close_out)
    1883             :       fclose(outfile);
    1884             :   }
    1885             : 
    1886             :   return 0;
    1887             : }
    1888             : #endif

Generated by: LCOV version 1.13