netwerk/streamconv/converters/ParseFTPList.cpp

Wed, 31 Dec 2014 06:55:46 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:46 +0100
changeset 1
ca08bd8f51b2
permissions
-rw-r--r--

Added tag TORBROWSER_REPLICA for changeset 6474c204b198

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

mercurial