]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
ftplistparser: split up into more functions
authorDaniel Stenberg <daniel@haxx.se>
Mon, 19 May 2025 08:31:24 +0000 (10:31 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 19 May 2025 11:46:12 +0000 (13:46 +0200)
And some general cleanups

Closes #17384

lib/ftplistparser.c

index f15759fb2bf361112d1e8288ef465a942badde3a..f416308e164104d67d21d5c83112f7076e994c4e 100644 (file)
@@ -363,6 +363,571 @@ static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data,
 
 #define MAX_FTPLIST_BUFFER 10000 /* arbitrarily set */
 
+static CURLcode unix_filetype(const char c, curlfiletype *t)
+{
+  switch(c) {
+  case '-':
+    *t = CURLFILETYPE_FILE;
+    break;
+  case 'd':
+    *t = CURLFILETYPE_DIRECTORY;
+    break;
+  case 'l':
+    *t = CURLFILETYPE_SYMLINK;
+    break;
+  case 'p':
+    *t = CURLFILETYPE_NAMEDPIPE;
+    break;
+  case 's':
+    *t = CURLFILETYPE_SOCKET;
+    break;
+  case 'c':
+    *t = CURLFILETYPE_DEVICE_CHAR;
+    break;
+  case 'b':
+    *t = CURLFILETYPE_DEVICE_BLOCK;
+    break;
+  case 'D':
+    *t = CURLFILETYPE_DOOR;
+    break;
+  default:
+    return CURLE_FTP_BAD_FILE_LIST;
+  }
+  return CURLE_OK;
+}
+
+static CURLcode parse_unix(struct Curl_easy *data,
+                           struct ftp_parselist_data *parser,
+                           struct fileinfo *infop,
+                           const char c)
+{
+  struct curl_fileinfo *finfo = &infop->info;
+  size_t len = curlx_dyn_len(&infop->buf);
+  char *mem = curlx_dyn_ptr(&infop->buf);
+  CURLcode result = CURLE_OK;
+
+  switch(parser->state.UNIX.main) {
+  case PL_UNIX_TOTALSIZE:
+    switch(parser->state.UNIX.sub.total_dirsize) {
+    case PL_UNIX_TOTALSIZE_INIT:
+      if(c == 't') {
+        parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING;
+        parser->item_length++;
+      }
+      else {
+        parser->state.UNIX.main = PL_UNIX_FILETYPE;
+        /* continue to fall through */
+      }
+      break;
+    case PL_UNIX_TOTALSIZE_READING:
+      parser->item_length++;
+      if(c == '\r') {
+        parser->item_length--;
+        curlx_dyn_setlen(&infop->buf, --len);
+      }
+      else if(c == '\n') {
+        mem[parser->item_length - 1] = 0;
+        if(!strncmp("total ", mem, 6)) {
+          const char *endptr = mem + 6;
+          /* here we can deal with directory size, pass the leading
+             whitespace and then the digits */
+          curlx_str_passblanks(&endptr);
+          while(ISDIGIT(*endptr))
+            endptr++;
+          if(*endptr) {
+            return CURLE_FTP_BAD_FILE_LIST;
+          }
+          parser->state.UNIX.main = PL_UNIX_FILETYPE;
+          curlx_dyn_reset(&infop->buf);
+        }
+        else
+          return CURLE_FTP_BAD_FILE_LIST;
+
+      }
+      break;
+    }
+    if(parser->state.UNIX.main != PL_UNIX_FILETYPE)
+      break;
+    FALLTHROUGH();
+  case PL_UNIX_FILETYPE:
+    result = unix_filetype(c, &finfo->filetype);
+    if(result)
+      return result;
+    parser->state.UNIX.main = PL_UNIX_PERMISSION;
+    parser->item_length = 0;
+    parser->item_offset = 1;
+    break;
+  case PL_UNIX_PERMISSION:
+    parser->item_length++;
+    if((parser->item_length <= 9) && !strchr("rwx-tTsS", c))
+      return CURLE_FTP_BAD_FILE_LIST;
+
+    else if(parser->item_length == 10) {
+      unsigned int perm;
+      if(c != ' ')
+        return CURLE_FTP_BAD_FILE_LIST;
+
+      mem[10] = 0; /* terminate permissions */
+      perm = ftp_pl_get_permission(mem + parser->item_offset);
+      if(perm & FTP_LP_MALFORMATED_PERM)
+        return CURLE_FTP_BAD_FILE_LIST;
+
+      parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_PERM;
+      parser->file_data->info.perm = perm;
+      parser->offsets.perm = parser->item_offset;
+
+      parser->item_length = 0;
+      parser->state.UNIX.main = PL_UNIX_HLINKS;
+      parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE;
+    }
+    break;
+  case PL_UNIX_HLINKS:
+    switch(parser->state.UNIX.sub.hlinks) {
+    case PL_UNIX_HLINKS_PRESPACE:
+      if(c != ' ') {
+        if(ISDIGIT(c)) {
+          parser->item_offset = len - 1;
+          parser->item_length = 1;
+          parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER;
+        }
+        else
+          return CURLE_FTP_BAD_FILE_LIST;
+      }
+      break;
+    case PL_UNIX_HLINKS_NUMBER:
+      parser->item_length ++;
+      if(c == ' ') {
+        const char *p = &mem[parser->item_offset];
+        curl_off_t hlinks;
+        mem[parser->item_offset + parser->item_length - 1] = 0;
+
+        if(!curlx_str_number(&p, &hlinks, LONG_MAX)) {
+          parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT;
+          parser->file_data->info.hardlinks = (long)hlinks;
+        }
+        parser->item_length = 0;
+        parser->item_offset = 0;
+        parser->state.UNIX.main = PL_UNIX_USER;
+        parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE;
+      }
+      else if(!ISDIGIT(c))
+        return CURLE_FTP_BAD_FILE_LIST;
+
+      break;
+    }
+    break;
+  case PL_UNIX_USER:
+    switch(parser->state.UNIX.sub.user) {
+    case PL_UNIX_USER_PRESPACE:
+      if(c != ' ') {
+        parser->item_offset = len - 1;
+        parser->item_length = 1;
+        parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING;
+      }
+      break;
+    case PL_UNIX_USER_PARSING:
+      parser->item_length++;
+      if(c == ' ') {
+        mem[parser->item_offset + parser->item_length - 1] = 0;
+        parser->offsets.user = parser->item_offset;
+        parser->state.UNIX.main = PL_UNIX_GROUP;
+        parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE;
+        parser->item_offset = 0;
+        parser->item_length = 0;
+      }
+      break;
+    }
+    break;
+  case PL_UNIX_GROUP:
+    switch(parser->state.UNIX.sub.group) {
+    case PL_UNIX_GROUP_PRESPACE:
+      if(c != ' ') {
+        parser->item_offset = len - 1;
+        parser->item_length = 1;
+        parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME;
+      }
+      break;
+    case PL_UNIX_GROUP_NAME:
+      parser->item_length++;
+      if(c == ' ') {
+        mem[parser->item_offset + parser->item_length - 1] = 0;
+        parser->offsets.group = parser->item_offset;
+        parser->state.UNIX.main = PL_UNIX_SIZE;
+        parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE;
+        parser->item_offset = 0;
+        parser->item_length = 0;
+      }
+      break;
+    }
+    break;
+  case PL_UNIX_SIZE:
+    switch(parser->state.UNIX.sub.size) {
+    case PL_UNIX_SIZE_PRESPACE:
+      if(c != ' ') {
+        if(ISDIGIT(c)) {
+          parser->item_offset = len - 1;
+          parser->item_length = 1;
+          parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER;
+        }
+        else
+          return CURLE_FTP_BAD_FILE_LIST;
+      }
+      break;
+    case PL_UNIX_SIZE_NUMBER:
+      parser->item_length++;
+      if(c == ' ') {
+        const char *p = mem + parser->item_offset;
+        curl_off_t fsize;
+        mem[parser->item_offset + parser->item_length - 1] = 0;
+        if(!curlx_str_numblanks(&p, &fsize)) {
+          if(p[0] == '\0' && fsize != CURL_OFF_T_MAX) {
+            parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
+            parser->file_data->info.size = fsize;
+          }
+          parser->item_length = 0;
+          parser->item_offset = 0;
+          parser->state.UNIX.main = PL_UNIX_TIME;
+          parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1;
+        }
+      }
+      else if(!ISDIGIT(c))
+        return CURLE_FTP_BAD_FILE_LIST;
+
+      break;
+    }
+    break;
+  case PL_UNIX_TIME:
+    switch(parser->state.UNIX.sub.time) {
+    case PL_UNIX_TIME_PREPART1:
+      if(c != ' ') {
+        if(ISALNUM(c)) {
+          parser->item_offset = len -1;
+          parser->item_length = 1;
+          parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1;
+        }
+        else
+          return CURLE_FTP_BAD_FILE_LIST;
+      }
+      break;
+    case PL_UNIX_TIME_PART1:
+      parser->item_length++;
+      if(c == ' ')
+        parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2;
+
+      else if(!ISALNUM(c) && c != '.')
+        return CURLE_FTP_BAD_FILE_LIST;
+
+      break;
+    case PL_UNIX_TIME_PREPART2:
+      parser->item_length++;
+      if(c != ' ') {
+        if(ISALNUM(c))
+          parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2;
+        else
+          return CURLE_FTP_BAD_FILE_LIST;
+      }
+      break;
+    case PL_UNIX_TIME_PART2:
+      parser->item_length++;
+      if(c == ' ')
+        parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3;
+      else if(!ISALNUM(c) && c != '.')
+        return CURLE_FTP_BAD_FILE_LIST;
+      break;
+    case PL_UNIX_TIME_PREPART3:
+      parser->item_length++;
+      if(c != ' ') {
+        if(ISALNUM(c))
+          parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3;
+        else
+          return CURLE_FTP_BAD_FILE_LIST;
+      }
+      break;
+    case PL_UNIX_TIME_PART3:
+      parser->item_length++;
+      if(c == ' ') {
+        mem[parser->item_offset + parser->item_length -1] = 0;
+        parser->offsets.time = parser->item_offset;
+        if(finfo->filetype == CURLFILETYPE_SYMLINK) {
+          parser->state.UNIX.main = PL_UNIX_SYMLINK;
+          parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE;
+        }
+        else {
+          parser->state.UNIX.main = PL_UNIX_FILENAME;
+          parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE;
+        }
+      }
+      else if(!ISALNUM(c) && c != '.' && c != ':')
+        return CURLE_FTP_BAD_FILE_LIST;
+      break;
+    }
+    break;
+  case PL_UNIX_FILENAME:
+    switch(parser->state.UNIX.sub.filename) {
+    case PL_UNIX_FILENAME_PRESPACE:
+      if(c != ' ') {
+        parser->item_offset = len - 1;
+        parser->item_length = 1;
+        parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME;
+      }
+      break;
+    case PL_UNIX_FILENAME_NAME:
+      parser->item_length++;
+      if(c == '\r')
+        parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL;
+
+      else if(c == '\n') {
+        mem[parser->item_offset + parser->item_length - 1] = 0;
+        parser->offsets.filename = parser->item_offset;
+        parser->state.UNIX.main = PL_UNIX_FILETYPE;
+        result = ftp_pl_insert_finfo(data, infop);
+        if(result)
+          return result;
+      }
+      break;
+    case PL_UNIX_FILENAME_WINDOWSEOL:
+      if(c == '\n') {
+        mem[parser->item_offset + parser->item_length - 1] = 0;
+        parser->offsets.filename = parser->item_offset;
+        parser->state.UNIX.main = PL_UNIX_FILETYPE;
+        result = ftp_pl_insert_finfo(data, infop);
+        if(result)
+          return result;
+      }
+      else
+        return CURLE_FTP_BAD_FILE_LIST;
+
+      break;
+    }
+    break;
+  case PL_UNIX_SYMLINK:
+    switch(parser->state.UNIX.sub.symlink) {
+    case PL_UNIX_SYMLINK_PRESPACE:
+      if(c != ' ') {
+        parser->item_offset = len - 1;
+        parser->item_length = 1;
+        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+      }
+      break;
+    case PL_UNIX_SYMLINK_NAME:
+      parser->item_length++;
+      if(c == ' ')
+        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1;
+
+      else if(c == '\r' || c == '\n')
+        return CURLE_FTP_BAD_FILE_LIST;
+
+      break;
+    case PL_UNIX_SYMLINK_PRETARGET1:
+      parser->item_length++;
+      if(c == '-')
+        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2;
+
+      else if(c == '\r' || c == '\n')
+        return CURLE_FTP_BAD_FILE_LIST;
+      else
+        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+      break;
+    case PL_UNIX_SYMLINK_PRETARGET2:
+      parser->item_length++;
+      if(c == '>')
+        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3;
+      else if(c == '\r' || c == '\n')
+        return CURLE_FTP_BAD_FILE_LIST;
+      else
+        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+
+      break;
+    case PL_UNIX_SYMLINK_PRETARGET3:
+      parser->item_length++;
+      if(c == ' ') {
+        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4;
+        /* now place where is symlink following */
+        mem[parser->item_offset + parser->item_length - 4] = 0;
+        parser->offsets.filename = parser->item_offset;
+        parser->item_length = 0;
+        parser->item_offset = 0;
+      }
+      else if(c == '\r' || c == '\n')
+        return CURLE_FTP_BAD_FILE_LIST;
+      else
+        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+      break;
+    case PL_UNIX_SYMLINK_PRETARGET4:
+      if(c != '\r' && c != '\n') {
+        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET;
+        parser->item_offset = len - 1;
+        parser->item_length = 1;
+      }
+      else
+        return CURLE_FTP_BAD_FILE_LIST;
+
+      break;
+    case PL_UNIX_SYMLINK_TARGET:
+      parser->item_length++;
+      if(c == '\r')
+        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL;
+
+      else if(c == '\n') {
+        mem[parser->item_offset + parser->item_length - 1] = 0;
+        parser->offsets.symlink_target = parser->item_offset;
+        result = ftp_pl_insert_finfo(data, infop);
+        if(result)
+          return result;
+
+        parser->state.UNIX.main = PL_UNIX_FILETYPE;
+      }
+      break;
+    case PL_UNIX_SYMLINK_WINDOWSEOL:
+      if(c == '\n') {
+        mem[parser->item_offset + parser->item_length - 1] = 0;
+        parser->offsets.symlink_target = parser->item_offset;
+        result = ftp_pl_insert_finfo(data, infop);
+        if(result)
+          return result;
+
+        parser->state.UNIX.main = PL_UNIX_FILETYPE;
+      }
+      else
+        return CURLE_FTP_BAD_FILE_LIST;
+
+      break;
+    }
+    break;
+  }
+  return CURLE_OK;
+}
+
+static CURLcode parse_winnt(struct Curl_easy *data,
+                            struct ftp_parselist_data *parser,
+                            struct fileinfo *infop,
+                            const char c)
+{
+  struct curl_fileinfo *finfo = &infop->info;
+  size_t len = curlx_dyn_len(&infop->buf);
+  char *mem = curlx_dyn_ptr(&infop->buf);
+  CURLcode result = CURLE_OK;
+
+  switch(parser->state.NT.main) {
+  case PL_WINNT_DATE:
+    parser->item_length++;
+    if(parser->item_length < 9) {
+      if(!strchr("0123456789-", c)) { /* only simple control */
+        return CURLE_FTP_BAD_FILE_LIST;
+      }
+    }
+    else if(parser->item_length == 9) {
+      if(c == ' ') {
+        parser->state.NT.main = PL_WINNT_TIME;
+        parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE;
+      }
+      else
+        return CURLE_FTP_BAD_FILE_LIST;
+    }
+    else
+      return CURLE_FTP_BAD_FILE_LIST;
+    break;
+  case PL_WINNT_TIME:
+    parser->item_length++;
+    switch(parser->state.NT.sub.time) {
+    case PL_WINNT_TIME_PRESPACE:
+      if(!ISBLANK(c))
+        parser->state.NT.sub.time = PL_WINNT_TIME_TIME;
+      break;
+    case PL_WINNT_TIME_TIME:
+      if(c == ' ') {
+        parser->offsets.time = parser->item_offset;
+        mem[parser->item_offset + parser->item_length -1] = 0;
+        parser->state.NT.main = PL_WINNT_DIRORSIZE;
+        parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE;
+        parser->item_length = 0;
+      }
+      else if(!strchr("APM0123456789:", c))
+        return CURLE_FTP_BAD_FILE_LIST;
+      break;
+    }
+    break;
+  case PL_WINNT_DIRORSIZE:
+    switch(parser->state.NT.sub.dirorsize) {
+    case PL_WINNT_DIRORSIZE_PRESPACE:
+      if(c != ' ') {
+        parser->item_offset = len - 1;
+        parser->item_length = 1;
+        parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT;
+      }
+      break;
+    case PL_WINNT_DIRORSIZE_CONTENT:
+      parser->item_length ++;
+      if(c == ' ') {
+        mem[parser->item_offset + parser->item_length - 1] = 0;
+        if(strcmp("<DIR>", mem + parser->item_offset) == 0) {
+          finfo->filetype = CURLFILETYPE_DIRECTORY;
+          finfo->size = 0;
+        }
+        else {
+          const char *p = mem + parser->item_offset;
+          if(curlx_str_numblanks(&p, &finfo->size)) {
+            return CURLE_FTP_BAD_FILE_LIST;
+          }
+          /* correct file type */
+          parser->file_data->info.filetype = CURLFILETYPE_FILE;
+        }
+
+        parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
+        parser->item_length = 0;
+        parser->state.NT.main = PL_WINNT_FILENAME;
+        parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
+      }
+      break;
+    }
+    break;
+  case PL_WINNT_FILENAME:
+    switch(parser->state.NT.sub.filename) {
+    case PL_WINNT_FILENAME_PRESPACE:
+      if(c != ' ') {
+        parser->item_offset = len -1;
+        parser->item_length = 1;
+        parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT;
+      }
+      break;
+    case PL_WINNT_FILENAME_CONTENT:
+      parser->item_length++;
+      if(c == '\r') {
+        parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL;
+        mem[len - 1] = 0;
+      }
+      else if(c == '\n') {
+        parser->offsets.filename = parser->item_offset;
+        mem[len - 1] = 0;
+        result = ftp_pl_insert_finfo(data, infop);
+        if(result)
+          return result;
+
+        parser->state.NT.main = PL_WINNT_DATE;
+        parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
+      }
+      break;
+    case PL_WINNT_FILENAME_WINEOL:
+      if(c == '\n') {
+        parser->offsets.filename = parser->item_offset;
+        result = ftp_pl_insert_finfo(data, infop);
+        if(result)
+          return result;
+
+        parser->state.NT.main = PL_WINNT_DATE;
+        parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
+      }
+      else
+        return CURLE_FTP_BAD_FILE_LIST;
+
+      break;
+    }
+    break;
+  }
+
+  return CURLE_OK;
+}
+
 size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
                           void *connptr)
 {
@@ -390,11 +955,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
   }
 
   while(i < bufflen) { /* FSM */
-    char *mem;
-    size_t len; /* number of bytes of data in the dynbuf */
     char c = buffer[i];
     struct fileinfo *infop;
-    struct curl_fileinfo *finfo;
     if(!parser->file_data) { /* tmp file data is not allocated yet */
       parser->file_data = Curl_fileinfo_alloc();
       if(!parser->file_data) {
@@ -407,618 +969,27 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
     }
 
     infop = parser->file_data;
-    finfo = &infop->info;
 
     if(curlx_dyn_addn(&infop->buf, &c, 1)) {
       parser->error = CURLE_OUT_OF_MEMORY;
       goto fail;
     }
-    len = curlx_dyn_len(&infop->buf);
-    mem = curlx_dyn_ptr(&infop->buf);
 
     switch(parser->os_type) {
     case OS_TYPE_UNIX:
-      switch(parser->state.UNIX.main) {
-      case PL_UNIX_TOTALSIZE:
-        switch(parser->state.UNIX.sub.total_dirsize) {
-        case PL_UNIX_TOTALSIZE_INIT:
-          if(c == 't') {
-            parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING;
-            parser->item_length++;
-          }
-          else {
-            parser->state.UNIX.main = PL_UNIX_FILETYPE;
-            /* start FSM again not considering size of directory */
-            curlx_dyn_reset(&infop->buf);
-            continue;
-          }
-          break;
-        case PL_UNIX_TOTALSIZE_READING:
-          parser->item_length++;
-          if(c == '\r') {
-            parser->item_length--;
-            curlx_dyn_setlen(&infop->buf, --len);
-          }
-          else if(c == '\n') {
-            mem[parser->item_length - 1] = 0;
-            if(!strncmp("total ", mem, 6)) {
-              const char *endptr = mem + 6;
-              /* here we can deal with directory size, pass the leading
-                 whitespace and then the digits */
-              curlx_str_passblanks(&endptr);
-              while(ISDIGIT(*endptr))
-                endptr++;
-              if(*endptr) {
-                parser->error = CURLE_FTP_BAD_FILE_LIST;
-                goto fail;
-              }
-              parser->state.UNIX.main = PL_UNIX_FILETYPE;
-              curlx_dyn_reset(&infop->buf);
-            }
-            else {
-              parser->error = CURLE_FTP_BAD_FILE_LIST;
-              goto fail;
-            }
-          }
-          break;
-        }
-        break;
-      case PL_UNIX_FILETYPE:
-        switch(c) {
-        case '-':
-          finfo->filetype = CURLFILETYPE_FILE;
-          break;
-        case 'd':
-          finfo->filetype = CURLFILETYPE_DIRECTORY;
-          break;
-        case 'l':
-          finfo->filetype = CURLFILETYPE_SYMLINK;
-          break;
-        case 'p':
-          finfo->filetype = CURLFILETYPE_NAMEDPIPE;
-          break;
-        case 's':
-          finfo->filetype = CURLFILETYPE_SOCKET;
-          break;
-        case 'c':
-          finfo->filetype = CURLFILETYPE_DEVICE_CHAR;
-          break;
-        case 'b':
-          finfo->filetype = CURLFILETYPE_DEVICE_BLOCK;
-          break;
-        case 'D':
-          finfo->filetype = CURLFILETYPE_DOOR;
-          break;
-        default:
-          parser->error = CURLE_FTP_BAD_FILE_LIST;
-          goto fail;
-        }
-        parser->state.UNIX.main = PL_UNIX_PERMISSION;
-        parser->item_length = 0;
-        parser->item_offset = 1;
-        break;
-      case PL_UNIX_PERMISSION:
-        parser->item_length++;
-        if(parser->item_length <= 9) {
-          if(!strchr("rwx-tTsS", c)) {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-        }
-        else if(parser->item_length == 10) {
-          unsigned int perm;
-          if(c != ' ') {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          mem[10] = 0; /* terminate permissions */
-          perm = ftp_pl_get_permission(mem + parser->item_offset);
-          if(perm & FTP_LP_MALFORMATED_PERM) {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_PERM;
-          parser->file_data->info.perm = perm;
-          parser->offsets.perm = parser->item_offset;
-
-          parser->item_length = 0;
-          parser->state.UNIX.main = PL_UNIX_HLINKS;
-          parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE;
-        }
-        break;
-      case PL_UNIX_HLINKS:
-        switch(parser->state.UNIX.sub.hlinks) {
-        case PL_UNIX_HLINKS_PRESPACE:
-          if(c != ' ') {
-            if(ISDIGIT(c)) {
-              parser->item_offset = len - 1;
-              parser->item_length = 1;
-              parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER;
-            }
-            else {
-              parser->error = CURLE_FTP_BAD_FILE_LIST;
-              goto fail;
-            }
-          }
-          break;
-        case PL_UNIX_HLINKS_NUMBER:
-          parser->item_length ++;
-          if(c == ' ') {
-            const char *p = &mem[parser->item_offset];
-            curl_off_t hlinks;
-            mem[parser->item_offset + parser->item_length - 1] = 0;
-
-            if(!curlx_str_number(&p, &hlinks, LONG_MAX)) {
-              parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT;
-              parser->file_data->info.hardlinks = (long)hlinks;
-            }
-            parser->item_length = 0;
-            parser->item_offset = 0;
-            parser->state.UNIX.main = PL_UNIX_USER;
-            parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE;
-          }
-          else if(!ISDIGIT(c)) {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          break;
-        }
-        break;
-      case PL_UNIX_USER:
-        switch(parser->state.UNIX.sub.user) {
-        case PL_UNIX_USER_PRESPACE:
-          if(c != ' ') {
-            parser->item_offset = len - 1;
-            parser->item_length = 1;
-            parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING;
-          }
-          break;
-        case PL_UNIX_USER_PARSING:
-          parser->item_length++;
-          if(c == ' ') {
-            mem[parser->item_offset + parser->item_length - 1] = 0;
-            parser->offsets.user = parser->item_offset;
-            parser->state.UNIX.main = PL_UNIX_GROUP;
-            parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE;
-            parser->item_offset = 0;
-            parser->item_length = 0;
-          }
-          break;
-        }
-        break;
-      case PL_UNIX_GROUP:
-        switch(parser->state.UNIX.sub.group) {
-        case PL_UNIX_GROUP_PRESPACE:
-          if(c != ' ') {
-            parser->item_offset = len - 1;
-            parser->item_length = 1;
-            parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME;
-          }
-          break;
-        case PL_UNIX_GROUP_NAME:
-          parser->item_length++;
-          if(c == ' ') {
-            mem[parser->item_offset + parser->item_length - 1] = 0;
-            parser->offsets.group = parser->item_offset;
-            parser->state.UNIX.main = PL_UNIX_SIZE;
-            parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE;
-            parser->item_offset = 0;
-            parser->item_length = 0;
-          }
-          break;
-        }
-        break;
-      case PL_UNIX_SIZE:
-        switch(parser->state.UNIX.sub.size) {
-        case PL_UNIX_SIZE_PRESPACE:
-          if(c != ' ') {
-            if(ISDIGIT(c)) {
-              parser->item_offset = len - 1;
-              parser->item_length = 1;
-              parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER;
-            }
-            else {
-              parser->error = CURLE_FTP_BAD_FILE_LIST;
-              goto fail;
-            }
-          }
-          break;
-        case PL_UNIX_SIZE_NUMBER:
-          parser->item_length++;
-          if(c == ' ') {
-            const char *p = mem + parser->item_offset;
-            curl_off_t fsize;
-            mem[parser->item_offset + parser->item_length - 1] = 0;
-            if(!curlx_str_numblanks(&p, &fsize)) {
-              if(p[0] == '\0' && fsize != CURL_OFF_T_MAX) {
-                parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
-                parser->file_data->info.size = fsize;
-              }
-              parser->item_length = 0;
-              parser->item_offset = 0;
-              parser->state.UNIX.main = PL_UNIX_TIME;
-              parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1;
-            }
-          }
-          else if(!ISDIGIT(c)) {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          break;
-        }
-        break;
-      case PL_UNIX_TIME:
-        switch(parser->state.UNIX.sub.time) {
-        case PL_UNIX_TIME_PREPART1:
-          if(c != ' ') {
-            if(ISALNUM(c)) {
-              parser->item_offset = len -1;
-              parser->item_length = 1;
-              parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1;
-            }
-            else {
-              parser->error = CURLE_FTP_BAD_FILE_LIST;
-              goto fail;
-            }
-          }
-          break;
-        case PL_UNIX_TIME_PART1:
-          parser->item_length++;
-          if(c == ' ') {
-            parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2;
-          }
-          else if(!ISALNUM(c) && c != '.') {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          break;
-        case PL_UNIX_TIME_PREPART2:
-          parser->item_length++;
-          if(c != ' ') {
-            if(ISALNUM(c)) {
-              parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2;
-            }
-            else {
-              parser->error = CURLE_FTP_BAD_FILE_LIST;
-              goto fail;
-            }
-          }
-          break;
-        case PL_UNIX_TIME_PART2:
-          parser->item_length++;
-          if(c == ' ') {
-            parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3;
-          }
-          else if(!ISALNUM(c) && c != '.') {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          break;
-        case PL_UNIX_TIME_PREPART3:
-          parser->item_length++;
-          if(c != ' ') {
-            if(ISALNUM(c)) {
-              parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3;
-            }
-            else {
-              parser->error = CURLE_FTP_BAD_FILE_LIST;
-              goto fail;
-            }
-          }
-          break;
-        case PL_UNIX_TIME_PART3:
-          parser->item_length++;
-          if(c == ' ') {
-            mem[parser->item_offset + parser->item_length -1] = 0;
-            parser->offsets.time = parser->item_offset;
-            /*
-              if(ftp_pl_gettime(parser, finfo->mem + parser->item_offset)) {
-                parser->file_data->flags |= CURLFINFOFLAG_KNOWN_TIME;
-              }
-            */
-            if(finfo->filetype == CURLFILETYPE_SYMLINK) {
-              parser->state.UNIX.main = PL_UNIX_SYMLINK;
-              parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE;
-            }
-            else {
-              parser->state.UNIX.main = PL_UNIX_FILENAME;
-              parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE;
-            }
-          }
-          else if(!ISALNUM(c) && c != '.' && c != ':') {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          break;
-        }
-        break;
-      case PL_UNIX_FILENAME:
-        switch(parser->state.UNIX.sub.filename) {
-        case PL_UNIX_FILENAME_PRESPACE:
-          if(c != ' ') {
-            parser->item_offset = len - 1;
-            parser->item_length = 1;
-            parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME;
-          }
-          break;
-        case PL_UNIX_FILENAME_NAME:
-          parser->item_length++;
-          if(c == '\r') {
-            parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL;
-          }
-          else if(c == '\n') {
-            mem[parser->item_offset + parser->item_length - 1] = 0;
-            parser->offsets.filename = parser->item_offset;
-            parser->state.UNIX.main = PL_UNIX_FILETYPE;
-            result = ftp_pl_insert_finfo(data, infop);
-            if(result) {
-              parser->error = result;
-              goto fail;
-            }
-          }
-          break;
-        case PL_UNIX_FILENAME_WINDOWSEOL:
-          if(c == '\n') {
-            mem[parser->item_offset + parser->item_length - 1] = 0;
-            parser->offsets.filename = parser->item_offset;
-            parser->state.UNIX.main = PL_UNIX_FILETYPE;
-            result = ftp_pl_insert_finfo(data, infop);
-            if(result) {
-              parser->error = result;
-              goto fail;
-            }
-          }
-          else {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          break;
-        }
-        break;
-      case PL_UNIX_SYMLINK:
-        switch(parser->state.UNIX.sub.symlink) {
-        case PL_UNIX_SYMLINK_PRESPACE:
-          if(c != ' ') {
-            parser->item_offset = len - 1;
-            parser->item_length = 1;
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
-          }
-          break;
-        case PL_UNIX_SYMLINK_NAME:
-          parser->item_length++;
-          if(c == ' ') {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1;
-          }
-          else if(c == '\r' || c == '\n') {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          break;
-        case PL_UNIX_SYMLINK_PRETARGET1:
-          parser->item_length++;
-          if(c == '-') {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2;
-          }
-          else if(c == '\r' || c == '\n') {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          else {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
-          }
-          break;
-        case PL_UNIX_SYMLINK_PRETARGET2:
-          parser->item_length++;
-          if(c == '>') {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3;
-          }
-          else if(c == '\r' || c == '\n') {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          else {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
-          }
-          break;
-        case PL_UNIX_SYMLINK_PRETARGET3:
-          parser->item_length++;
-          if(c == ' ') {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4;
-            /* now place where is symlink following */
-            mem[parser->item_offset + parser->item_length - 4] = 0;
-            parser->offsets.filename = parser->item_offset;
-            parser->item_length = 0;
-            parser->item_offset = 0;
-          }
-          else if(c == '\r' || c == '\n') {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          else {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
-          }
-          break;
-        case PL_UNIX_SYMLINK_PRETARGET4:
-          if(c != '\r' && c != '\n') {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET;
-            parser->item_offset = len - 1;
-            parser->item_length = 1;
-          }
-          else {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          break;
-        case PL_UNIX_SYMLINK_TARGET:
-          parser->item_length++;
-          if(c == '\r') {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL;
-          }
-          else if(c == '\n') {
-            mem[parser->item_offset + parser->item_length - 1] = 0;
-            parser->offsets.symlink_target = parser->item_offset;
-            result = ftp_pl_insert_finfo(data, infop);
-            if(result) {
-              parser->error = result;
-              goto fail;
-            }
-            parser->state.UNIX.main = PL_UNIX_FILETYPE;
-          }
-          break;
-        case PL_UNIX_SYMLINK_WINDOWSEOL:
-          if(c == '\n') {
-            mem[parser->item_offset + parser->item_length - 1] = 0;
-            parser->offsets.symlink_target = parser->item_offset;
-            result = ftp_pl_insert_finfo(data, infop);
-            if(result) {
-              parser->error = result;
-              goto fail;
-            }
-            parser->state.UNIX.main = PL_UNIX_FILETYPE;
-          }
-          else {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          break;
-        }
-        break;
-      }
+      result = parse_unix(data, parser, infop, c);
       break;
     case OS_TYPE_WIN_NT:
-      switch(parser->state.NT.main) {
-      case PL_WINNT_DATE:
-        parser->item_length++;
-        if(parser->item_length < 9) {
-          if(!strchr("0123456789-", c)) { /* only simple control */
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-        }
-        else if(parser->item_length == 9) {
-          if(c == ' ') {
-            parser->state.NT.main = PL_WINNT_TIME;
-            parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE;
-          }
-          else {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-        }
-        else {
-          parser->error = CURLE_FTP_BAD_FILE_LIST;
-          goto fail;
-        }
-        break;
-      case PL_WINNT_TIME:
-        parser->item_length++;
-        switch(parser->state.NT.sub.time) {
-        case PL_WINNT_TIME_PRESPACE:
-          if(!ISBLANK(c)) {
-            parser->state.NT.sub.time = PL_WINNT_TIME_TIME;
-          }
-          break;
-        case PL_WINNT_TIME_TIME:
-          if(c == ' ') {
-            parser->offsets.time = parser->item_offset;
-            mem[parser->item_offset + parser->item_length -1] = 0;
-            parser->state.NT.main = PL_WINNT_DIRORSIZE;
-            parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE;
-            parser->item_length = 0;
-          }
-          else if(!strchr("APM0123456789:", c)) {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          break;
-        }
-        break;
-      case PL_WINNT_DIRORSIZE:
-        switch(parser->state.NT.sub.dirorsize) {
-        case PL_WINNT_DIRORSIZE_PRESPACE:
-          if(c != ' ') {
-            parser->item_offset = len - 1;
-            parser->item_length = 1;
-            parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT;
-          }
-          break;
-        case PL_WINNT_DIRORSIZE_CONTENT:
-          parser->item_length ++;
-          if(c == ' ') {
-            mem[parser->item_offset + parser->item_length - 1] = 0;
-            if(strcmp("<DIR>", mem + parser->item_offset) == 0) {
-              finfo->filetype = CURLFILETYPE_DIRECTORY;
-              finfo->size = 0;
-            }
-            else {
-              const char *p = mem + parser->item_offset;
-              if(curlx_str_numblanks(&p, &finfo->size)) {
-                parser->error = CURLE_FTP_BAD_FILE_LIST;
-                goto fail;
-              }
-              /* correct file type */
-              parser->file_data->info.filetype = CURLFILETYPE_FILE;
-            }
-
-            parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
-            parser->item_length = 0;
-            parser->state.NT.main = PL_WINNT_FILENAME;
-            parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
-          }
-          break;
-        }
-        break;
-      case PL_WINNT_FILENAME:
-        switch(parser->state.NT.sub.filename) {
-        case PL_WINNT_FILENAME_PRESPACE:
-          if(c != ' ') {
-            parser->item_offset = len -1;
-            parser->item_length = 1;
-            parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT;
-          }
-          break;
-        case PL_WINNT_FILENAME_CONTENT:
-          parser->item_length++;
-          if(c == '\r') {
-            parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL;
-            mem[len - 1] = 0;
-          }
-          else if(c == '\n') {
-            parser->offsets.filename = parser->item_offset;
-            mem[len - 1] = 0;
-            result = ftp_pl_insert_finfo(data, infop);
-            if(result) {
-              parser->error = result;
-              goto fail;
-            }
-            parser->state.NT.main = PL_WINNT_DATE;
-            parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
-          }
-          break;
-        case PL_WINNT_FILENAME_WINEOL:
-          if(c == '\n') {
-            parser->offsets.filename = parser->item_offset;
-            result = ftp_pl_insert_finfo(data, infop);
-            if(result) {
-              parser->error = result;
-              goto fail;
-            }
-            parser->state.NT.main = PL_WINNT_DATE;
-            parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
-          }
-          else {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          break;
-        }
-        break;
-      }
+      result = parse_winnt(data, parser, infop, c);
       break;
     default:
       retsize = bufflen + 1;
       goto fail;
     }
+    if(result) {
+      parser->error = result;
+      goto fail;
+    }
 
     i++;
   }