1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/streamconv/converters/ParseFTPList.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1877 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "ParseFTPList.h" 1.10 +#include <stdlib.h> 1.11 +#include <string.h> 1.12 +#include <ctype.h> 1.13 +#include "plstr.h" 1.14 +#include "nsDebug.h" 1.15 +#include "prprf.h" 1.16 + 1.17 +/* ==================================================================== */ 1.18 + 1.19 +static inline int ParsingFailed(struct list_state *state) 1.20 +{ 1.21 + if (state->parsed_one || state->lstyle) /* junk if we fail to parse */ 1.22 + return '?'; /* this time but had previously parsed successfully */ 1.23 + return '"'; /* its part of a comment or error message */ 1.24 +} 1.25 + 1.26 +int ParseFTPList(const char *line, struct list_state *state, 1.27 + struct list_result *result ) 1.28 +{ 1.29 + unsigned int carry_buf_len; /* copy of state->carry_buf_len */ 1.30 + unsigned int linelen, pos; 1.31 + const char *p; 1.32 + 1.33 + if (!line || !state || !result) 1.34 + return 0; 1.35 + 1.36 + memset( result, 0, sizeof(*result) ); 1.37 + state->numlines++; 1.38 + 1.39 + /* carry buffer is only valid from one line to the next */ 1.40 + carry_buf_len = state->carry_buf_len; 1.41 + state->carry_buf_len = 0; 1.42 + 1.43 + linelen = 0; 1.44 + 1.45 + /* strip leading whitespace */ 1.46 + while (*line == ' ' || *line == '\t') 1.47 + line++; 1.48 + 1.49 + /* line is terminated at first '\0' or '\n' */ 1.50 + p = line; 1.51 + while (*p && *p != '\n') 1.52 + p++; 1.53 + linelen = p - line; 1.54 + 1.55 + if (linelen > 0 && *p == '\n' && *(p-1) == '\r') 1.56 + linelen--; 1.57 + 1.58 + /* DON'T strip trailing whitespace. */ 1.59 + 1.60 + if (linelen > 0) 1.61 + { 1.62 + static const char *month_names = "JanFebMarAprMayJunJulAugSepOctNovDec"; 1.63 + const char *tokens[16]; /* 16 is more than enough */ 1.64 + unsigned int toklen[(sizeof(tokens)/sizeof(tokens[0]))]; 1.65 + unsigned int linelen_sans_wsp; // line length sans whitespace 1.66 + unsigned int numtoks = 0; 1.67 + unsigned int tokmarker = 0; /* extra info for lstyle handler */ 1.68 + unsigned int month_num = 0; 1.69 + char tbuf[4]; 1.70 + int lstyle = 0; 1.71 + 1.72 + if (carry_buf_len) /* VMS long filename carryover buffer */ 1.73 + { 1.74 + tokens[0] = state->carry_buf; 1.75 + toklen[0] = carry_buf_len; 1.76 + numtoks++; 1.77 + } 1.78 + 1.79 + pos = 0; 1.80 + while (pos < linelen && numtoks < (sizeof(tokens)/sizeof(tokens[0])) ) 1.81 + { 1.82 + while (pos < linelen && 1.83 + (line[pos] == ' ' || line[pos] == '\t' || line[pos] == '\r')) 1.84 + pos++; 1.85 + if (pos < linelen) 1.86 + { 1.87 + tokens[numtoks] = &line[pos]; 1.88 + while (pos < linelen && 1.89 + (line[pos] != ' ' && line[pos] != '\t' && line[pos] != '\r')) 1.90 + pos++; 1.91 + if (tokens[numtoks] != &line[pos]) 1.92 + { 1.93 + toklen[numtoks] = (&line[pos] - tokens[numtoks]); 1.94 + numtoks++; 1.95 + } 1.96 + } 1.97 + } 1.98 + 1.99 + if (!numtoks) 1.100 + return ParsingFailed(state); 1.101 + 1.102 + linelen_sans_wsp = &(tokens[numtoks-1][toklen[numtoks-1]]) - tokens[0]; 1.103 + if (numtoks == (sizeof(tokens)/sizeof(tokens[0])) ) 1.104 + { 1.105 + pos = linelen; 1.106 + while (pos > 0 && (line[pos-1] == ' ' || line[pos-1] == '\t')) 1.107 + pos--; 1.108 + linelen_sans_wsp = pos; 1.109 + } 1.110 + 1.111 + /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ 1.112 + 1.113 +#if defined(SUPPORT_EPLF) 1.114 + /* EPLF handling must come somewhere before /bin/dls handling. */ 1.115 + if (!lstyle && (!state->lstyle || state->lstyle == 'E')) 1.116 + { 1.117 + if (*line == '+' && linelen > 4 && numtoks >= 2) 1.118 + { 1.119 + pos = 1; 1.120 + while (pos < (linelen-1)) 1.121 + { 1.122 + p = &line[pos++]; 1.123 + if (*p == '/') 1.124 + result->fe_type = 'd'; /* its a dir */ 1.125 + else if (*p == 'r') 1.126 + result->fe_type = 'f'; /* its a file */ 1.127 + else if (*p == 'm') 1.128 + { 1.129 + if (isdigit(line[pos])) 1.130 + { 1.131 + while (pos < linelen && isdigit(line[pos])) 1.132 + pos++; 1.133 + if (pos < linelen && line[pos] == ',') 1.134 + { 1.135 + PRTime t; 1.136 + PRTime seconds; 1.137 + PR_sscanf(p+1, "%llu", &seconds); 1.138 + t = seconds * PR_USEC_PER_SEC; 1.139 + PR_ExplodeTime(t, PR_LocalTimeParameters, &(result->fe_time) ); 1.140 + } 1.141 + } 1.142 + } 1.143 + else if (*p == 's') 1.144 + { 1.145 + if (isdigit(line[pos])) 1.146 + { 1.147 + while (pos < linelen && isdigit(line[pos])) 1.148 + pos++; 1.149 + if (pos < linelen && line[pos] == ',' && 1.150 + ((&line[pos]) - (p+1)) < int(sizeof(result->fe_size)-1) ) 1.151 + { 1.152 + memcpy( result->fe_size, p+1, (unsigned)(&line[pos] - (p+1)) ); 1.153 + result->fe_size[(&line[pos] - (p+1))] = '\0'; 1.154 + } 1.155 + } 1.156 + } 1.157 + else if (isalpha(*p)) /* 'i'/'up' or unknown "fact" (property) */ 1.158 + { 1.159 + while (pos < linelen && *++p != ',') 1.160 + pos++; 1.161 + } 1.162 + else if (*p != '\t' || (p+1) != tokens[1]) 1.163 + { 1.164 + break; /* its not EPLF after all */ 1.165 + } 1.166 + else 1.167 + { 1.168 + state->parsed_one = 1; 1.169 + state->lstyle = lstyle = 'E'; 1.170 + 1.171 + p = &(line[linelen_sans_wsp]); 1.172 + result->fe_fname = tokens[1]; 1.173 + result->fe_fnlen = p - tokens[1]; 1.174 + 1.175 + if (!result->fe_type) /* access denied */ 1.176 + { 1.177 + result->fe_type = 'f'; /* is assuming 'f'ile correct? */ 1.178 + return '?'; /* NO! junk it. */ 1.179 + } 1.180 + return result->fe_type; 1.181 + } 1.182 + if (pos >= (linelen-1) || line[pos] != ',') 1.183 + break; 1.184 + pos++; 1.185 + } /* while (pos < linelen) */ 1.186 + memset( result, 0, sizeof(*result) ); 1.187 + } /* if (*line == '+' && linelen > 4 && numtoks >= 2) */ 1.188 + } /* if (!lstyle && (!state->lstyle || state->lstyle == 'E')) */ 1.189 +#endif /* SUPPORT_EPLF */ 1.190 + 1.191 + /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ 1.192 + 1.193 +#if defined(SUPPORT_VMS) 1.194 + if (!lstyle && (!state->lstyle || state->lstyle == 'V')) 1.195 + { /* try VMS Multinet/UCX/CMS server */ 1.196 + /* 1.197 + * Legal characters in a VMS file/dir spec are [A-Z0-9$.-_~]. 1.198 + * '$' cannot begin a filename and `-' cannot be used as the first 1.199 + * or last character. '.' is only valid as a directory separator 1.200 + * and <file>.<type> separator. A canonical filename spec might look 1.201 + * like this: DISK$VOL:[DIR1.DIR2.DIR3]FILE.TYPE;123 1.202 + * All VMS FTP servers LIST in uppercase. 1.203 + * 1.204 + * We need to be picky about this in order to support 1.205 + * multi-line listings correctly. 1.206 + */ 1.207 + if (!state->parsed_one && 1.208 + (numtoks == 1 || (numtoks == 2 && toklen[0] == 9 && 1.209 + memcmp(tokens[0], "Directory", 9)==0 ))) 1.210 + { 1.211 + /* If no dirstyle has been detected yet, and this line is a 1.212 + * VMS list's dirname, then turn on VMS dirstyle. 1.213 + * eg "ACA:[ANONYMOUS]", "DISK$FTP:[ANONYMOUS]", "SYS$ANONFTP:" 1.214 + */ 1.215 + p = tokens[0]; 1.216 + pos = toklen[0]; 1.217 + if (numtoks == 2) 1.218 + { 1.219 + p = tokens[1]; 1.220 + pos = toklen[1]; 1.221 + } 1.222 + pos--; 1.223 + if (pos >= 3) 1.224 + { 1.225 + while (pos > 0 && p[pos] != '[') 1.226 + { 1.227 + pos--; 1.228 + if (p[pos] == '-' || p[pos] == '$') 1.229 + { 1.230 + if (pos == 0 || p[pos-1] == '[' || p[pos-1] == '.' || 1.231 + (p[pos] == '-' && (p[pos+1] == ']' || p[pos+1] == '.'))) 1.232 + break; 1.233 + } 1.234 + else if (p[pos] != '.' && p[pos] != '~' && 1.235 + !isdigit(p[pos]) && !isalpha(p[pos])) 1.236 + break; 1.237 + else if (isalpha(p[pos]) && p[pos] != toupper(p[pos])) 1.238 + break; 1.239 + } 1.240 + if (pos > 0) 1.241 + { 1.242 + pos--; 1.243 + if (p[pos] != ':' || p[pos+1] != '[') 1.244 + pos = 0; 1.245 + } 1.246 + } 1.247 + if (pos > 0 && p[pos] == ':') 1.248 + { 1.249 + while (pos > 0) 1.250 + { 1.251 + pos--; 1.252 + if (p[pos] != '$' && p[pos] != '_' && p[pos] != '-' && 1.253 + p[pos] != '~' && !isdigit(p[pos]) && !isalpha(p[pos])) 1.254 + break; 1.255 + else if (isalpha(p[pos]) && p[pos] != toupper(p[pos])) 1.256 + break; 1.257 + } 1.258 + if (pos == 0) 1.259 + { 1.260 + state->lstyle = 'V'; 1.261 + return '?'; /* its junk */ 1.262 + } 1.263 + } 1.264 + /* fallthrough */ 1.265 + } 1.266 + else if ((tokens[0][toklen[0]-1]) != ';') 1.267 + { 1.268 + if (numtoks == 1 && (state->lstyle == 'V' && !carry_buf_len)) 1.269 + lstyle = 'V'; 1.270 + else if (numtoks < 4) 1.271 + ; 1.272 + else if (toklen[1] >= 10 && memcmp(tokens[1], "%RMS-E-PRV", 10) == 0) 1.273 + lstyle = 'V'; 1.274 + else if ((&line[linelen] - tokens[1]) >= 22 && 1.275 + memcmp(tokens[1], "insufficient privilege", 22) == 0) 1.276 + lstyle = 'V'; 1.277 + else if (numtoks != 4 && numtoks != 6) 1.278 + ; 1.279 + else if (numtoks == 6 && ( 1.280 + toklen[5] < 4 || *tokens[5] != '(' || /* perms */ 1.281 + (tokens[5][toklen[5]-1]) != ')' )) 1.282 + ; 1.283 + else if ( (toklen[2] == 10 || toklen[2] == 11) && 1.284 + (tokens[2][toklen[2]-5]) == '-' && 1.285 + (tokens[2][toklen[2]-9]) == '-' && 1.286 + (((toklen[3]==4 || toklen[3]==5 || toklen[3]==7 || toklen[3]==8) && 1.287 + (tokens[3][toklen[3]-3]) == ':' ) || 1.288 + ((toklen[3]==10 || toklen[3]==11 ) && 1.289 + (tokens[3][toklen[3]-3]) == '.' ) 1.290 + ) && /* time in [H]H:MM[:SS[.CC]] format */ 1.291 + isdigit(*tokens[1]) && /* size */ 1.292 + isdigit(*tokens[2]) && /* date */ 1.293 + isdigit(*tokens[3]) /* time */ 1.294 + ) 1.295 + { 1.296 + lstyle = 'V'; 1.297 + } 1.298 + if (lstyle == 'V') 1.299 + { 1.300 + /* 1.301 + * MultiNet FTP: 1.302 + * LOGIN.COM;2 1 4-NOV-1994 04:09 [ANONYMOUS] (RWE,RWE,,) 1.303 + * PUB.DIR;1 1 27-JAN-1994 14:46 [ANONYMOUS] (RWE,RWE,RE,RWE) 1.304 + * README.FTP;1 %RMS-E-PRV, insufficient privilege or file protection violation 1.305 + * ROUSSOS.DIR;1 1 27-JAN-1994 14:48 [CS,ROUSSOS] (RWE,RWE,RE,R) 1.306 + * S67-50903.JPG;1 328 22-SEP-1998 16:19 [ANONYMOUS] (RWED,RWED,,) 1.307 + * UCX FTP: 1.308 + * CII-MANUAL.TEX;1 213/216 29-JAN-1996 03:33:12 [ANONYMOU,ANONYMOUS] (RWED,RWED,,) 1.309 + * CMU/VMS-IP FTP 1.310 + * [VMSSERV.FILES]ALARM.DIR;1 1/3 5-MAR-1993 18:09 1.311 + * TCPware FTP 1.312 + * FOO.BAR;1 4 5-MAR-1993 18:09:01.12 1.313 + * Long filename example: 1.314 + * THIS-IS-A-LONG-VMS-FILENAME.AND-THIS-IS-A-LONG-VMS-FILETYPE\r\n 1.315 + * 213[/nnn] 29-JAN-1996 03:33[:nn] [ANONYMOU,ANONYMOUS] (RWED,RWED,,) 1.316 + */ 1.317 + tokmarker = 0; 1.318 + p = tokens[0]; 1.319 + pos = 0; 1.320 + if (*p == '[' && toklen[0] >= 4) /* CMU style */ 1.321 + { 1.322 + if (p[1] != ']') 1.323 + { 1.324 + p++; 1.325 + pos++; 1.326 + } 1.327 + while (lstyle && pos < toklen[0] && *p != ']') 1.328 + { 1.329 + if (*p != '$' && *p != '.' && *p != '_' && *p != '-' && 1.330 + *p != '~' && !isdigit(*p) && !isalpha(*p)) 1.331 + lstyle = 0; 1.332 + pos++; 1.333 + p++; 1.334 + } 1.335 + if (lstyle && pos < (toklen[0]-1)) 1.336 + { 1.337 + /* ']' was found and there is at least one character after it */ 1.338 + NS_ASSERTION(*p == ']', "unexpected state"); 1.339 + pos++; 1.340 + p++; 1.341 + tokmarker = pos; /* length of leading "[DIR1.DIR2.etc]" */ 1.342 + } else { 1.343 + /* not a CMU style listing */ 1.344 + lstyle = 0; 1.345 + } 1.346 + } 1.347 + while (lstyle && pos < toklen[0] && *p != ';') 1.348 + { 1.349 + if (*p != '$' && *p != '.' && *p != '_' && *p != '-' && 1.350 + *p != '~' && !isdigit(*p) && !isalpha(*p)) 1.351 + lstyle = 0; 1.352 + else if (isalpha(*p) && *p != toupper(*p)) 1.353 + lstyle = 0; 1.354 + p++; 1.355 + pos++; 1.356 + } 1.357 + if (lstyle && *p == ';') 1.358 + { 1.359 + if (pos == 0 || pos == (toklen[0]-1)) 1.360 + lstyle = 0; 1.361 + for (pos++;lstyle && pos < toklen[0];pos++) 1.362 + { 1.363 + if (!isdigit(tokens[0][pos])) 1.364 + lstyle = 0; 1.365 + } 1.366 + } 1.367 + pos = (p - tokens[0]); /* => fnlength sans ";####" */ 1.368 + pos -= tokmarker; /* => fnlength sans "[DIR1.DIR2.etc]" */ 1.369 + p = &(tokens[0][tokmarker]); /* offset of basename */ 1.370 + 1.371 + if (!lstyle || pos == 0 || pos > 80) /* VMS filenames can't be longer than that */ 1.372 + { 1.373 + lstyle = 0; 1.374 + } 1.375 + else if (numtoks == 1) 1.376 + { 1.377 + /* if VMS has been detected and there is only one token and that 1.378 + * token was a VMS filename then this is a multiline VMS LIST entry. 1.379 + */ 1.380 + if (pos >= (sizeof(state->carry_buf)-1)) 1.381 + pos = (sizeof(state->carry_buf)-1); /* shouldn't happen */ 1.382 + memcpy( state->carry_buf, p, pos ); 1.383 + state->carry_buf_len = pos; 1.384 + return '?'; /* tell caller to treat as junk */ 1.385 + } 1.386 + else if (isdigit(*tokens[1])) /* not no-privs message */ 1.387 + { 1.388 + for (pos = 0; lstyle && pos < (toklen[1]); pos++) 1.389 + { 1.390 + if (!isdigit((tokens[1][pos])) && (tokens[1][pos]) != '/') 1.391 + lstyle = 0; 1.392 + } 1.393 + if (lstyle && numtoks > 4) /* Multinet or UCX but not CMU */ 1.394 + { 1.395 + for (pos = 1; lstyle && pos < (toklen[5]-1); pos++) 1.396 + { 1.397 + p = &(tokens[5][pos]); 1.398 + if (*p!='R' && *p!='W' && *p!='E' && *p!='D' && *p!=',') 1.399 + lstyle = 0; 1.400 + } 1.401 + } 1.402 + } 1.403 + } /* passed initial tests */ 1.404 + } /* else if ((tokens[0][toklen[0]-1]) != ';') */ 1.405 + 1.406 + if (lstyle == 'V') 1.407 + { 1.408 + state->parsed_one = 1; 1.409 + state->lstyle = lstyle; 1.410 + 1.411 + if (isdigit(*tokens[1])) /* not permission denied etc */ 1.412 + { 1.413 + /* strip leading directory name */ 1.414 + if (*tokens[0] == '[') /* CMU server */ 1.415 + { 1.416 + pos = toklen[0]-1; 1.417 + p = tokens[0]+1; 1.418 + while (*p != ']') 1.419 + { 1.420 + p++; 1.421 + pos--; 1.422 + } 1.423 + toklen[0] = --pos; 1.424 + tokens[0] = ++p; 1.425 + } 1.426 + pos = 0; 1.427 + while (pos < toklen[0] && (tokens[0][pos]) != ';') 1.428 + pos++; 1.429 + 1.430 + result->fe_cinfs = 1; 1.431 + result->fe_type = 'f'; 1.432 + result->fe_fname = tokens[0]; 1.433 + result->fe_fnlen = pos; 1.434 + 1.435 + if (pos > 4) 1.436 + { 1.437 + p = &(tokens[0][pos-4]); 1.438 + if (p[0] == '.' && p[1] == 'D' && p[2] == 'I' && p[3] == 'R') 1.439 + { 1.440 + result->fe_fnlen -= 4; 1.441 + result->fe_type = 'd'; 1.442 + } 1.443 + } 1.444 + 1.445 + if (result->fe_type != 'd') 1.446 + { 1.447 + /* #### or used/allocated form. If used/allocated form, then 1.448 + * 'used' is the size in bytes if and only if 'used'<=allocated. 1.449 + * If 'used' is size in bytes then it can be > 2^32 1.450 + * If 'used' is not size in bytes then it is size in blocks. 1.451 + */ 1.452 + pos = 0; 1.453 + while (pos < toklen[1] && (tokens[1][pos]) != '/') 1.454 + pos++; 1.455 + 1.456 +/* 1.457 + * I've never seen size come back in bytes, its always in blocks, and 1.458 + * the following test fails. So, always perform the "size in blocks". 1.459 + * I'm leaving the "size in bytes" code if'd out in case we ever need 1.460 + * to re-instate it. 1.461 +*/ 1.462 +#if 0 1.463 + if (pos < toklen[1] && ( (pos<<1) > (toklen[1]-1) || 1.464 + (strtoul(tokens[1], (char **)0, 10) > 1.465 + strtoul(tokens[1]+pos+1, (char **)0, 10)) )) 1.466 + { /* size is in bytes */ 1.467 + if (pos > (sizeof(result->fe_size)-1)) 1.468 + pos = sizeof(result->fe_size)-1; 1.469 + memcpy( result->fe_size, tokens[1], pos ); 1.470 + result->fe_size[pos] = '\0'; 1.471 + } 1.472 + else /* size is in blocks */ 1.473 +#endif 1.474 + { 1.475 + /* size requires multiplication by blocksize. 1.476 + * 1.477 + * We could assume blocksize is 512 (like Lynx does) and 1.478 + * shift by 9, but that might not be right. Even if it 1.479 + * were, doing that wouldn't reflect what the file's 1.480 + * real size was. The sanest thing to do is not use the 1.481 + * LISTing's filesize, so we won't (like ftpmirror). 1.482 + * 1.483 + * ulltoa(((unsigned long long)fsz)<<9, result->fe_size, 10); 1.484 + * 1.485 + * A block is always 512 bytes on OpenVMS, compute size. 1.486 + * So its rounded up to the next block, so what, its better 1.487 + * than not showing the size at all. 1.488 + * A block is always 512 bytes on OpenVMS, compute size. 1.489 + * So its rounded up to the next block, so what, its better 1.490 + * than not showing the size at all. 1.491 + */ 1.492 + uint64_t fsz = uint64_t(strtoul(tokens[1], (char **)0, 10) * 512); 1.493 + PR_snprintf(result->fe_size, sizeof(result->fe_size), 1.494 + "%lld", fsz); 1.495 + } 1.496 + 1.497 + } /* if (result->fe_type != 'd') */ 1.498 + 1.499 + p = tokens[2] + 2; 1.500 + if (*p == '-') 1.501 + p++; 1.502 + tbuf[0] = p[0]; 1.503 + tbuf[1] = tolower(p[1]); 1.504 + tbuf[2] = tolower(p[2]); 1.505 + month_num = 0; 1.506 + for (pos = 0; pos < (12*3); pos+=3) 1.507 + { 1.508 + if (tbuf[0] == month_names[pos+0] && 1.509 + tbuf[1] == month_names[pos+1] && 1.510 + tbuf[2] == month_names[pos+2]) 1.511 + break; 1.512 + month_num++; 1.513 + } 1.514 + if (month_num >= 12) 1.515 + month_num = 0; 1.516 + result->fe_time.tm_month = month_num; 1.517 + result->fe_time.tm_mday = atoi(tokens[2]); 1.518 + result->fe_time.tm_year = atoi(p+4); // NSPR wants year as XXXX 1.519 + 1.520 + p = tokens[3] + 2; 1.521 + if (*p == ':') 1.522 + p++; 1.523 + if (p[2] == ':') 1.524 + result->fe_time.tm_sec = atoi(p+3); 1.525 + result->fe_time.tm_hour = atoi(tokens[3]); 1.526 + result->fe_time.tm_min = atoi(p); 1.527 + 1.528 + return result->fe_type; 1.529 + 1.530 + } /* if (isdigit(*tokens[1])) */ 1.531 + 1.532 + return '?'; /* junk */ 1.533 + 1.534 + } /* if (lstyle == 'V') */ 1.535 + } /* if (!lstyle && (!state->lstyle || state->lstyle == 'V')) */ 1.536 +#endif 1.537 + 1.538 + /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ 1.539 + 1.540 +#if defined(SUPPORT_CMS) 1.541 + /* Virtual Machine/Conversational Monitor System (IBM Mainframe) */ 1.542 + if (!lstyle && (!state->lstyle || state->lstyle == 'C')) /* VM/CMS */ 1.543 + { 1.544 + /* LISTing according to mirror.pl 1.545 + * Filename FileType Fm Format Lrecl Records Blocks Date Time 1.546 + * LASTING GLOBALV A1 V 41 21 1 9/16/91 15:10:32 1.547 + * J43401 NETLOG A0 V 77 1 1 9/12/91 12:36:04 1.548 + * PROFILE EXEC A1 V 17 3 1 9/12/91 12:39:07 1.549 + * DIRUNIX SCRIPT A1 V 77 1216 17 1/04/93 20:30:47 1.550 + * MAIL PROFILE A2 F 80 1 1 10/14/92 16:12:27 1.551 + * BADY2K TEXT A0 V 1 1 1 1/03/102 10:11:12 1.552 + * AUTHORS A1 DIR - - - 9/20/99 10:31:11 1.553 + * 1.554 + * LISTing from vm.marist.edu and vm.sc.edu 1.555 + * 220-FTPSERVE IBM VM Level 420 at VM.MARIST.EDU, 04:58:12 EDT WEDNESDAY 2002-07-10 1.556 + * AUTHORS DIR - - - 1999-09-20 10:31:11 - 1.557 + * HARRINGTON DIR - - - 1997-02-12 15:33:28 - 1.558 + * PICS DIR - - - 2000-10-12 15:43:23 - 1.559 + * SYSFILE DIR - - - 2000-07-20 17:48:01 - 1.560 + * WELCNVT EXEC V 72 9 1 1999-09-20 17:16:18 - 1.561 + * WELCOME EREADME F 80 21 1 1999-12-27 16:19:00 - 1.562 + * WELCOME README V 82 21 1 1999-12-27 16:19:04 - 1.563 + * README ANONYMOU V 71 26 1 1997-04-02 12:33:20 TCP291 1.564 + * README ANONYOLD V 71 15 1 1995-08-25 16:04:27 TCP291 1.565 + */ 1.566 + if (numtoks >= 7 && (toklen[0]+toklen[1]) <= 16) 1.567 + { 1.568 + for (pos = 1; !lstyle && (pos+5) < numtoks; pos++) 1.569 + { 1.570 + p = tokens[pos]; 1.571 + if ((toklen[pos] == 1 && (*p == 'F' || *p == 'V')) || 1.572 + (toklen[pos] == 3 && *p == 'D' && p[1] == 'I' && p[2] == 'R')) 1.573 + { 1.574 + if (toklen[pos+5] == 8 && (tokens[pos+5][2]) == ':' && 1.575 + (tokens[pos+5][5]) == ':' ) 1.576 + { 1.577 + p = tokens[pos+4]; 1.578 + if ((toklen[pos+4] == 10 && p[4] == '-' && p[7] == '-') || 1.579 + (toklen[pos+4] >= 7 && toklen[pos+4] <= 9 && 1.580 + p[((p[1]!='/')?(2):(1))] == '/' && 1.581 + p[((p[1]!='/')?(5):(4))] == '/')) 1.582 + /* Y2K bugs possible ("7/06/102" or "13/02/101") */ 1.583 + { 1.584 + if ( (*tokens[pos+1] == '-' && 1.585 + *tokens[pos+2] == '-' && 1.586 + *tokens[pos+3] == '-') || 1.587 + (isdigit(*tokens[pos+1]) && 1.588 + isdigit(*tokens[pos+2]) && 1.589 + isdigit(*tokens[pos+3])) ) 1.590 + { 1.591 + lstyle = 'C'; 1.592 + tokmarker = pos; 1.593 + } 1.594 + } 1.595 + } 1.596 + } 1.597 + } /* for (pos = 1; !lstyle && (pos+5) < numtoks; pos++) */ 1.598 + } /* if (numtoks >= 7) */ 1.599 + 1.600 + /* extra checking if first pass */ 1.601 + if (lstyle && !state->lstyle) 1.602 + { 1.603 + for (pos = 0, p = tokens[0]; lstyle && pos < toklen[0]; pos++, p++) 1.604 + { 1.605 + if (isalpha(*p) && toupper(*p) != *p) 1.606 + lstyle = 0; 1.607 + } 1.608 + for (pos = tokmarker+1; pos <= tokmarker+3; pos++) 1.609 + { 1.610 + if (!(toklen[pos] == 1 && *tokens[pos] == '-')) 1.611 + { 1.612 + for (p = tokens[pos]; lstyle && p<(tokens[pos]+toklen[pos]); p++) 1.613 + { 1.614 + if (!isdigit(*p)) 1.615 + lstyle = 0; 1.616 + } 1.617 + } 1.618 + } 1.619 + for (pos = 0, p = tokens[tokmarker+4]; 1.620 + lstyle && pos < toklen[tokmarker+4]; pos++, p++) 1.621 + { 1.622 + if (*p == '/') 1.623 + { 1.624 + /* There may be Y2K bugs in the date. Don't simplify to 1.625 + * pos != (len-3) && pos != (len-6) like time is done. 1.626 + */ 1.627 + if ((tokens[tokmarker+4][1]) == '/') 1.628 + { 1.629 + if (pos != 1 && pos != 4) 1.630 + lstyle = 0; 1.631 + } 1.632 + else if (pos != 2 && pos != 5) 1.633 + lstyle = 0; 1.634 + } 1.635 + else if (*p != '-' && !isdigit(*p)) 1.636 + lstyle = 0; 1.637 + else if (*p == '-' && pos != 4 && pos != 7) 1.638 + lstyle = 0; 1.639 + } 1.640 + for (pos = 0, p = tokens[tokmarker+5]; 1.641 + lstyle && pos < toklen[tokmarker+5]; pos++, p++) 1.642 + { 1.643 + if (*p != ':' && !isdigit(*p)) 1.644 + lstyle = 0; 1.645 + else if (*p == ':' && pos != (toklen[tokmarker+5]-3) 1.646 + && pos != (toklen[tokmarker+5]-6)) 1.647 + lstyle = 0; 1.648 + } 1.649 + } /* initial if() */ 1.650 + 1.651 + if (lstyle == 'C') 1.652 + { 1.653 + state->parsed_one = 1; 1.654 + state->lstyle = lstyle; 1.655 + 1.656 + p = tokens[tokmarker+4]; 1.657 + if (toklen[tokmarker+4] == 10) /* newstyle: YYYY-MM-DD format */ 1.658 + { 1.659 + result->fe_time.tm_year = atoi(p+0) - 1900; 1.660 + result->fe_time.tm_month = atoi(p+5) - 1; 1.661 + result->fe_time.tm_mday = atoi(p+8); 1.662 + } 1.663 + else /* oldstyle: [M]M/DD/YY format */ 1.664 + { 1.665 + pos = toklen[tokmarker+4]; 1.666 + result->fe_time.tm_month = atoi(p) - 1; 1.667 + result->fe_time.tm_mday = atoi((p+pos)-5); 1.668 + result->fe_time.tm_year = atoi((p+pos)-2); 1.669 + if (result->fe_time.tm_year < 70) 1.670 + result->fe_time.tm_year += 100; 1.671 + } 1.672 + 1.673 + p = tokens[tokmarker+5]; 1.674 + pos = toklen[tokmarker+5]; 1.675 + result->fe_time.tm_hour = atoi(p); 1.676 + result->fe_time.tm_min = atoi((p+pos)-5); 1.677 + result->fe_time.tm_sec = atoi((p+pos)-2); 1.678 + 1.679 + result->fe_cinfs = 1; 1.680 + result->fe_fname = tokens[0]; 1.681 + result->fe_fnlen = toklen[0]; 1.682 + result->fe_type = 'f'; 1.683 + 1.684 + p = tokens[tokmarker]; 1.685 + if (toklen[tokmarker] == 3 && *p=='D' && p[1]=='I' && p[2]=='R') 1.686 + result->fe_type = 'd'; 1.687 + 1.688 + if ((/*newstyle*/ toklen[tokmarker+4] == 10 && tokmarker > 1) || 1.689 + (/*oldstyle*/ toklen[tokmarker+4] != 10 && tokmarker > 2)) 1.690 + { /* have a filetype column */ 1.691 + char *dot; 1.692 + p = &(tokens[0][toklen[0]]); 1.693 + memcpy( &dot, &p, sizeof(dot) ); /* NASTY! */ 1.694 + *dot++ = '.'; 1.695 + p = tokens[1]; 1.696 + for (pos = 0; pos < toklen[1]; pos++) 1.697 + *dot++ = *p++; 1.698 + result->fe_fnlen += 1 + toklen[1]; 1.699 + } 1.700 + 1.701 + /* oldstyle LISTING: 1.702 + * files/dirs not on the 'A' minidisk are not RETRievable/CHDIRable 1.703 + if (toklen[tokmarker+4] != 10 && *tokens[tokmarker-1] != 'A') 1.704 + return '?'; 1.705 + */ 1.706 + 1.707 + /* VM/CMS LISTings have no usable filesize field. 1.708 + * Have to use the 'SIZE' command for that. 1.709 + */ 1.710 + return result->fe_type; 1.711 + 1.712 + } /* if (lstyle == 'C' && (!state->lstyle || state->lstyle == lstyle)) */ 1.713 + } /* VM/CMS */ 1.714 +#endif 1.715 + 1.716 + /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ 1.717 + 1.718 +#if defined(SUPPORT_DOS) /* WinNT DOS dirstyle */ 1.719 + if (!lstyle && (!state->lstyle || state->lstyle == 'W')) 1.720 + { 1.721 + /* 1.722 + * "10-23-00 01:27PM <DIR> veronist" 1.723 + * "06-15-00 07:37AM <DIR> zoe" 1.724 + * "07-14-00 01:35PM 2094926 canprankdesk.tif" 1.725 + * "07-21-00 01:19PM 95077 Jon Kauffman Enjoys the Good Life.jpg" 1.726 + * "07-21-00 01:19PM 52275 Name Plate.jpg" 1.727 + * "07-14-00 01:38PM 2250540 Valentineoffprank-HiRes.jpg" 1.728 + */ 1.729 + if ((numtoks >= 4) && toklen[0] == 8 && toklen[1] == 7 && 1.730 + (*tokens[2] == '<' || isdigit(*tokens[2])) ) 1.731 + { 1.732 + p = tokens[0]; 1.733 + if ( isdigit(p[0]) && isdigit(p[1]) && p[2]=='-' && 1.734 + isdigit(p[3]) && isdigit(p[4]) && p[5]=='-' && 1.735 + isdigit(p[6]) && isdigit(p[7]) ) 1.736 + { 1.737 + p = tokens[1]; 1.738 + if ( isdigit(p[0]) && isdigit(p[1]) && p[2]==':' && 1.739 + isdigit(p[3]) && isdigit(p[4]) && 1.740 + (p[5]=='A' || p[5]=='P') && p[6]=='M') 1.741 + { 1.742 + lstyle = 'W'; 1.743 + if (!state->lstyle) 1.744 + { 1.745 + p = tokens[2]; 1.746 + /* <DIR> or <JUNCTION> */ 1.747 + if (*p != '<' || p[toklen[2]-1] != '>') 1.748 + { 1.749 + for (pos = 1; (lstyle && pos < toklen[2]); pos++) 1.750 + { 1.751 + if (!isdigit(*++p)) 1.752 + lstyle = 0; 1.753 + } 1.754 + } 1.755 + } 1.756 + } 1.757 + } 1.758 + } 1.759 + 1.760 + if (lstyle == 'W') 1.761 + { 1.762 + state->parsed_one = 1; 1.763 + state->lstyle = lstyle; 1.764 + 1.765 + p = &(line[linelen]); /* line end */ 1.766 + result->fe_cinfs = 1; 1.767 + result->fe_fname = tokens[3]; 1.768 + result->fe_fnlen = p - tokens[3]; 1.769 + result->fe_type = 'd'; 1.770 + 1.771 + if (*tokens[2] != '<') /* not <DIR> or <JUNCTION> */ 1.772 + { 1.773 + // try to handle correctly spaces at the beginning of the filename 1.774 + // filesize (token[2]) must end at offset 38 1.775 + if (tokens[2] + toklen[2] - line == 38) { 1.776 + result->fe_fname = &(line[39]); 1.777 + result->fe_fnlen = p - result->fe_fname; 1.778 + } 1.779 + result->fe_type = 'f'; 1.780 + pos = toklen[2]; 1.781 + while (pos > (sizeof(result->fe_size)-1)) 1.782 + pos = (sizeof(result->fe_size)-1); 1.783 + memcpy( result->fe_size, tokens[2], pos ); 1.784 + result->fe_size[pos] = '\0'; 1.785 + } 1.786 + else { 1.787 + // try to handle correctly spaces at the beginning of the filename 1.788 + // token[2] must begin at offset 24, the length is 5 or 10 1.789 + // token[3] must begin at offset 39 or higher 1.790 + if (tokens[2] - line == 24 && (toklen[2] == 5 || toklen[2] == 10) && 1.791 + tokens[3] - line >= 39) { 1.792 + result->fe_fname = &(line[39]); 1.793 + result->fe_fnlen = p - result->fe_fname; 1.794 + } 1.795 + 1.796 + if ((tokens[2][1]) != 'D') /* not <DIR> */ 1.797 + { 1.798 + result->fe_type = '?'; /* unknown until junc for sure */ 1.799 + if (result->fe_fnlen > 4) 1.800 + { 1.801 + p = result->fe_fname; 1.802 + for (pos = result->fe_fnlen - 4; pos > 0; pos--) 1.803 + { 1.804 + if (p[0] == ' ' && p[3] == ' ' && p[2] == '>' && 1.805 + (p[1] == '=' || p[1] == '-')) 1.806 + { 1.807 + result->fe_type = 'l'; 1.808 + result->fe_fnlen = p - result->fe_fname; 1.809 + result->fe_lname = p + 4; 1.810 + result->fe_lnlen = &(line[linelen]) 1.811 + - result->fe_lname; 1.812 + break; 1.813 + } 1.814 + p++; 1.815 + } 1.816 + } 1.817 + } 1.818 + } 1.819 + 1.820 + result->fe_time.tm_month = atoi(tokens[0]+0); 1.821 + if (result->fe_time.tm_month != 0) 1.822 + { 1.823 + result->fe_time.tm_month--; 1.824 + result->fe_time.tm_mday = atoi(tokens[0]+3); 1.825 + result->fe_time.tm_year = atoi(tokens[0]+6); 1.826 + /* if year has only two digits then assume that 1.827 + 00-79 is 2000-2079 1.828 + 80-99 is 1980-1999 */ 1.829 + if (result->fe_time.tm_year < 80) 1.830 + result->fe_time.tm_year += 2000; 1.831 + else if (result->fe_time.tm_year < 100) 1.832 + result->fe_time.tm_year += 1900; 1.833 + } 1.834 + 1.835 + result->fe_time.tm_hour = atoi(tokens[1]+0); 1.836 + result->fe_time.tm_min = atoi(tokens[1]+3); 1.837 + if ((tokens[1][5]) == 'P' && result->fe_time.tm_hour < 12) 1.838 + result->fe_time.tm_hour += 12; 1.839 + 1.840 + /* the caller should do this (if dropping "." and ".." is desired) 1.841 + if (result->fe_type == 'd' && result->fe_fname[0] == '.' && 1.842 + (result->fe_fnlen == 1 || (result->fe_fnlen == 2 && 1.843 + result->fe_fname[1] == '.'))) 1.844 + return '?'; 1.845 + */ 1.846 + 1.847 + return result->fe_type; 1.848 + } /* if (lstyle == 'W' && (!state->lstyle || state->lstyle == lstyle)) */ 1.849 + } /* if (!lstyle && (!state->lstyle || state->lstyle == 'W')) */ 1.850 +#endif 1.851 + 1.852 + /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ 1.853 + 1.854 +#if defined(SUPPORT_OS2) 1.855 + if (!lstyle && (!state->lstyle || state->lstyle == 'O')) /* OS/2 test */ 1.856 + { 1.857 + /* 220 server IBM TCP/IP for OS/2 - FTP Server ver 23:04:36 on Jan 15 1997 ready. 1.858 + * fixed position, space padded columns. I have only a vague idea 1.859 + * of what the contents between col 18 and 34 might be: All I can infer 1.860 + * is that there may be attribute flags in there and there may be 1.861 + * a " DIR" in there. 1.862 + * 1.863 + * 1 2 3 4 5 6 1.864 + *0123456789012345678901234567890123456789012345678901234567890123456789 1.865 + *----- size -------|??????????????? MM-DD-YY| HH:MM| nnnnnnnnn.... 1.866 + * 0 DIR 04-11-95 16:26 . 1.867 + * 0 DIR 04-11-95 16:26 .. 1.868 + * 0 DIR 04-11-95 16:26 ADDRESS 1.869 + * 612 RHSA 07-28-95 16:45 air_tra1.bag 1.870 + * 195 A 08-09-95 10:23 Alfa1.bag 1.871 + * 0 RHS DIR 04-11-95 16:26 ATTACH 1.872 + * 372 A 08-09-95 10:26 Aussie_1.bag 1.873 + * 310992 06-28-94 09:56 INSTALL.EXE 1.874 + * 1 2 3 4 1.875 + * 01234567890123456789012345678901234567890123456789 1.876 + * dirlist from the mirror.pl project, col positions from Mozilla. 1.877 + */ 1.878 + p = &(line[toklen[0]]); 1.879 + /* \s(\d\d-\d\d-\d\d)\s+(\d\d:\d\d)\s */ 1.880 + if (numtoks >= 4 && toklen[0] <= 18 && isdigit(*tokens[0]) && 1.881 + (linelen - toklen[0]) >= (53-18) && 1.882 + p[18-18] == ' ' && p[34-18] == ' ' && 1.883 + p[37-18] == '-' && p[40-18] == '-' && p[43-18] == ' ' && 1.884 + p[45-18] == ' ' && p[48-18] == ':' && p[51-18] == ' ' && 1.885 + isdigit(p[35-18]) && isdigit(p[36-18]) && 1.886 + isdigit(p[38-18]) && isdigit(p[39-18]) && 1.887 + isdigit(p[41-18]) && isdigit(p[42-18]) && 1.888 + isdigit(p[46-18]) && isdigit(p[47-18]) && 1.889 + isdigit(p[49-18]) && isdigit(p[50-18]) 1.890 + ) 1.891 + { 1.892 + lstyle = 'O'; /* OS/2 */ 1.893 + if (!state->lstyle) 1.894 + { 1.895 + for (pos = 1; lstyle && pos < toklen[0]; pos++) 1.896 + { 1.897 + if (!isdigit(tokens[0][pos])) 1.898 + lstyle = 0; 1.899 + } 1.900 + } 1.901 + } 1.902 + 1.903 + if (lstyle == 'O') 1.904 + { 1.905 + state->parsed_one = 1; 1.906 + state->lstyle = lstyle; 1.907 + 1.908 + p = &(line[toklen[0]]); 1.909 + 1.910 + result->fe_cinfs = 1; 1.911 + result->fe_fname = &p[53-18]; 1.912 + result->fe_fnlen = (&(line[linelen_sans_wsp])) 1.913 + - (result->fe_fname); 1.914 + result->fe_type = 'f'; 1.915 + 1.916 + /* I don't have a real listing to determine exact pos, so scan. */ 1.917 + for (pos = (18-18); pos < ((35-18)-4); pos++) 1.918 + { 1.919 + if (p[pos+0] == ' ' && p[pos+1] == 'D' && 1.920 + p[pos+2] == 'I' && p[pos+3] == 'R') 1.921 + { 1.922 + result->fe_type = 'd'; 1.923 + break; 1.924 + } 1.925 + } 1.926 + 1.927 + if (result->fe_type != 'd') 1.928 + { 1.929 + pos = toklen[0]; 1.930 + if (pos > (sizeof(result->fe_size)-1)) 1.931 + pos = (sizeof(result->fe_size)-1); 1.932 + memcpy( result->fe_size, tokens[0], pos ); 1.933 + result->fe_size[pos] = '\0'; 1.934 + } 1.935 + 1.936 + result->fe_time.tm_month = atoi(&p[35-18]) - 1; 1.937 + result->fe_time.tm_mday = atoi(&p[38-18]); 1.938 + result->fe_time.tm_year = atoi(&p[41-18]); 1.939 + if (result->fe_time.tm_year < 80) 1.940 + result->fe_time.tm_year += 100; 1.941 + result->fe_time.tm_hour = atoi(&p[46-18]); 1.942 + result->fe_time.tm_min = atoi(&p[49-18]); 1.943 + 1.944 + /* the caller should do this (if dropping "." and ".." is desired) 1.945 + if (result->fe_type == 'd' && result->fe_fname[0] == '.' && 1.946 + (result->fe_fnlen == 1 || (result->fe_fnlen == 2 && 1.947 + result->fe_fname[1] == '.'))) 1.948 + return '?'; 1.949 + */ 1.950 + 1.951 + return result->fe_type; 1.952 + } /* if (lstyle == 'O') */ 1.953 + 1.954 + } /* if (!lstyle && (!state->lstyle || state->lstyle == 'O')) */ 1.955 +#endif 1.956 + 1.957 + /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ 1.958 + 1.959 +#if defined(SUPPORT_LSL) 1.960 + if (!lstyle && (!state->lstyle || state->lstyle == 'U')) /* /bin/ls & co. */ 1.961 + { 1.962 + /* UNIX-style listing, without inum and without blocks 1.963 + * "-rw-r--r-- 1 root other 531 Jan 29 03:26 README" 1.964 + * "dr-xr-xr-x 2 root other 512 Apr 8 1994 etc" 1.965 + * "dr-xr-xr-x 2 root 512 Apr 8 1994 etc" 1.966 + * "lrwxrwxrwx 1 root other 7 Jan 25 00:17 bin -> usr/bin" 1.967 + * Also produced by Microsoft's FTP servers for Windows: 1.968 + * "---------- 1 owner group 1803128 Jul 10 10:18 ls-lR.Z" 1.969 + * "d--------- 1 owner group 0 May 9 19:45 Softlib" 1.970 + * Also WFTPD for MSDOS: 1.971 + * "-rwxrwxrwx 1 noone nogroup 322 Aug 19 1996 message.ftp" 1.972 + * Hellsoft for NetWare: 1.973 + * "d[RWCEMFA] supervisor 512 Jan 16 18:53 login" 1.974 + * "-[RWCEMFA] rhesus 214059 Oct 20 15:27 cx.exe" 1.975 + * Newer Hellsoft for NetWare: (netlab2.usu.edu) 1.976 + * - [RWCEAFMS] NFAUUser 192 Apr 27 15:21 HEADER.html 1.977 + * d [RWCEAFMS] jrd 512 Jul 11 03:01 allupdates 1.978 + * Also NetPresenz for the Mac: 1.979 + * "-------r-- 326 1391972 1392298 Nov 22 1995 MegaPhone.sit" 1.980 + * "drwxrwxr-x folder 2 May 10 1996 network" 1.981 + * Protected directory: 1.982 + * "drwx-wx-wt 2 root wheel 512 Jul 1 02:15 incoming" 1.983 + * uid/gid instead of username/groupname: 1.984 + * "drwxr-xr-x 2 0 0 512 May 28 22:17 etc" 1.985 + */ 1.986 + 1.987 + bool is_old_Hellsoft = false; 1.988 + 1.989 + if (numtoks >= 6) 1.990 + { 1.991 + /* there are two perm formats (Hellsoft/NetWare and *IX strmode(3)). 1.992 + * Scan for size column only if the perm format is one or the other. 1.993 + */ 1.994 + if (toklen[0] == 1 || (tokens[0][1]) == '[') 1.995 + { 1.996 + if (*tokens[0] == 'd' || *tokens[0] == '-') 1.997 + { 1.998 + pos = toklen[0]-1; 1.999 + p = tokens[0] + 1; 1.1000 + if (pos == 0) 1.1001 + { 1.1002 + p = tokens[1]; 1.1003 + pos = toklen[1]; 1.1004 + } 1.1005 + if ((pos == 9 || pos == 10) && 1.1006 + (*p == '[' && p[pos-1] == ']') && 1.1007 + (p[1] == 'R' || p[1] == '-') && 1.1008 + (p[2] == 'W' || p[2] == '-') && 1.1009 + (p[3] == 'C' || p[3] == '-') && 1.1010 + (p[4] == 'E' || p[4] == '-')) 1.1011 + { 1.1012 + /* rest is FMA[S] or AFM[S] */ 1.1013 + lstyle = 'U'; /* very likely one of the NetWare servers */ 1.1014 + if (toklen[0] == 10) 1.1015 + is_old_Hellsoft = true; 1.1016 + } 1.1017 + } 1.1018 + } 1.1019 + else if ((toklen[0] == 10 || toklen[0] == 11) 1.1020 + && strchr("-bcdlpsw?DFam", *tokens[0])) 1.1021 + { 1.1022 + p = &(tokens[0][1]); 1.1023 + if ((p[0] == 'r' || p[0] == '-') && 1.1024 + (p[1] == 'w' || p[1] == '-') && 1.1025 + (p[3] == 'r' || p[3] == '-') && 1.1026 + (p[4] == 'w' || p[4] == '-') && 1.1027 + (p[6] == 'r' || p[6] == '-') && 1.1028 + (p[7] == 'w' || p[7] == '-')) 1.1029 + /* 'x'/p[9] can be S|s|x|-|T|t or implementation specific */ 1.1030 + { 1.1031 + lstyle = 'U'; /* very likely /bin/ls */ 1.1032 + } 1.1033 + } 1.1034 + } 1.1035 + if (lstyle == 'U') /* first token checks out */ 1.1036 + { 1.1037 + lstyle = 0; 1.1038 + for (pos = (numtoks-5); !lstyle && pos > 1; pos--) 1.1039 + { 1.1040 + /* scan for: (\d+)\s+([A-Z][a-z][a-z])\s+ 1.1041 + * (\d\d\d\d|\d\:\d\d|\d\d\:\d\d|\d\:\d\d\:\d\d|\d\d\:\d\d\:\d\d) 1.1042 + * \s+(.+)$ 1.1043 + */ 1.1044 + if (isdigit(*tokens[pos]) /* size */ 1.1045 + /* (\w\w\w) */ 1.1046 + && toklen[pos+1] == 3 && isalpha(*tokens[pos+1]) && 1.1047 + isalpha(tokens[pos+1][1]) && isalpha(tokens[pos+1][2]) 1.1048 + /* (\d|\d\d) */ 1.1049 + && isdigit(*tokens[pos+2]) && 1.1050 + (toklen[pos+2] == 1 || 1.1051 + (toklen[pos+2] == 2 && isdigit(tokens[pos+2][1]))) 1.1052 + && toklen[pos+3] >= 4 && isdigit(*tokens[pos+3]) 1.1053 + /* (\d\:\d\d\:\d\d|\d\d\:\d\d\:\d\d) */ 1.1054 + && (toklen[pos+3] <= 5 || ( 1.1055 + (toklen[pos+3] == 7 || toklen[pos+3] == 8) && 1.1056 + (tokens[pos+3][toklen[pos+3]-3]) == ':')) 1.1057 + && isdigit(tokens[pos+3][toklen[pos+3]-2]) 1.1058 + && isdigit(tokens[pos+3][toklen[pos+3]-1]) 1.1059 + && ( 1.1060 + /* (\d\d\d\d) */ 1.1061 + ((toklen[pos+3] == 4 || toklen[pos+3] == 5) && 1.1062 + isdigit(tokens[pos+3][1]) && 1.1063 + isdigit(tokens[pos+3][2]) ) 1.1064 + /* (\d\:\d\d|\d\:\d\d\:\d\d) */ 1.1065 + || ((toklen[pos+3] == 4 || toklen[pos+3] == 7) && 1.1066 + (tokens[pos+3][1]) == ':' && 1.1067 + isdigit(tokens[pos+3][2]) && isdigit(tokens[pos+3][3])) 1.1068 + /* (\d\d\:\d\d|\d\d\:\d\d\:\d\d) */ 1.1069 + || ((toklen[pos+3] == 5 || toklen[pos+3] == 8) && 1.1070 + isdigit(tokens[pos+3][1]) && (tokens[pos+3][2]) == ':' && 1.1071 + isdigit(tokens[pos+3][3]) && isdigit(tokens[pos+3][4])) 1.1072 + ) 1.1073 + ) 1.1074 + { 1.1075 + lstyle = 'U'; /* assume /bin/ls or variant format */ 1.1076 + tokmarker = pos; 1.1077 + 1.1078 + /* check that size is numeric */ 1.1079 + p = tokens[tokmarker]; 1.1080 + unsigned int i; 1.1081 + for (i = 0; i < toklen[tokmarker]; i++) 1.1082 + { 1.1083 + if (!isdigit(*p++)) 1.1084 + { 1.1085 + lstyle = 0; 1.1086 + break; 1.1087 + } 1.1088 + } 1.1089 + if (lstyle) 1.1090 + { 1.1091 + month_num = 0; 1.1092 + p = tokens[tokmarker+1]; 1.1093 + for (i = 0; i < (12*3); i+=3) 1.1094 + { 1.1095 + if (p[0] == month_names[i+0] && 1.1096 + p[1] == month_names[i+1] && 1.1097 + p[2] == month_names[i+2]) 1.1098 + break; 1.1099 + month_num++; 1.1100 + } 1.1101 + if (month_num >= 12) 1.1102 + lstyle = 0; 1.1103 + } 1.1104 + } /* relative position test */ 1.1105 + } /* for (pos = (numtoks-5); !lstyle && pos > 1; pos--) */ 1.1106 + } /* if (lstyle == 'U') */ 1.1107 + 1.1108 + if (lstyle == 'U') 1.1109 + { 1.1110 + state->parsed_one = 1; 1.1111 + state->lstyle = lstyle; 1.1112 + 1.1113 + result->fe_cinfs = 0; 1.1114 + result->fe_type = '?'; 1.1115 + if (*tokens[0] == 'd' || *tokens[0] == 'l') 1.1116 + result->fe_type = *tokens[0]; 1.1117 + else if (*tokens[0] == 'D') 1.1118 + result->fe_type = 'd'; 1.1119 + else if (*tokens[0] == '-' || *tokens[0] == 'F') 1.1120 + result->fe_type = 'f'; /* (hopefully a regular file) */ 1.1121 + 1.1122 + if (result->fe_type != 'd') 1.1123 + { 1.1124 + pos = toklen[tokmarker]; 1.1125 + if (pos > (sizeof(result->fe_size)-1)) 1.1126 + pos = (sizeof(result->fe_size)-1); 1.1127 + memcpy( result->fe_size, tokens[tokmarker], pos ); 1.1128 + result->fe_size[pos] = '\0'; 1.1129 + } 1.1130 + 1.1131 + result->fe_time.tm_month = month_num; 1.1132 + result->fe_time.tm_mday = atoi(tokens[tokmarker+2]); 1.1133 + if (result->fe_time.tm_mday == 0) 1.1134 + result->fe_time.tm_mday++; 1.1135 + 1.1136 + p = tokens[tokmarker+3]; 1.1137 + pos = (unsigned int)atoi(p); 1.1138 + if (p[1] == ':') /* one digit hour */ 1.1139 + p--; 1.1140 + if (p[2] != ':') /* year */ 1.1141 + { 1.1142 + result->fe_time.tm_year = pos; 1.1143 + } 1.1144 + else 1.1145 + { 1.1146 + result->fe_time.tm_hour = pos; 1.1147 + result->fe_time.tm_min = atoi(p+3); 1.1148 + if (p[5] == ':') 1.1149 + result->fe_time.tm_sec = atoi(p+6); 1.1150 + 1.1151 + if (!state->now_time) 1.1152 + { 1.1153 + state->now_time = PR_Now(); 1.1154 + PR_ExplodeTime((state->now_time), PR_LocalTimeParameters, &(state->now_tm) ); 1.1155 + } 1.1156 + 1.1157 + result->fe_time.tm_year = state->now_tm.tm_year; 1.1158 + if ( (( state->now_tm.tm_month << 5) + state->now_tm.tm_mday) < 1.1159 + ((result->fe_time.tm_month << 5) + result->fe_time.tm_mday) ) 1.1160 + result->fe_time.tm_year--; 1.1161 + 1.1162 + } /* time/year */ 1.1163 + 1.1164 + // The length of the whole date string should be 12. On AIX the length 1.1165 + // is only 11 when the year is present in the date string and there is 1.1166 + // 1 padding space at the end of the string. In both cases the filename 1.1167 + // starts at offset 13 from the start of the date string. 1.1168 + // Don't care about leading spaces when the date string has different 1.1169 + // format or when old Hellsoft output was detected. 1.1170 + { 1.1171 + const char *date_start = tokens[tokmarker+1]; 1.1172 + const char *date_end = tokens[tokmarker+3] + toklen[tokmarker+3]; 1.1173 + if (!is_old_Hellsoft && ((date_end - date_start) == 12 || 1.1174 + ((date_end - date_start) == 11 && date_end[1] == ' '))) 1.1175 + result->fe_fname = date_start + 13; 1.1176 + else 1.1177 + result->fe_fname = tokens[tokmarker+4]; 1.1178 + } 1.1179 + 1.1180 + result->fe_fnlen = (&(line[linelen])) 1.1181 + - (result->fe_fname); 1.1182 + 1.1183 + if (result->fe_type == 'l' && result->fe_fnlen > 4) 1.1184 + { 1.1185 + /* First try to use result->fe_size to find " -> " sequence. 1.1186 + This can give proper result for cases like "aaa -> bbb -> ccc". */ 1.1187 + uint32_t fe_size = atoi(result->fe_size); 1.1188 + 1.1189 + if (result->fe_fnlen > (fe_size + 4) && 1.1190 + PL_strncmp(result->fe_fname + result->fe_fnlen - fe_size - 4 , " -> ", 4) == 0) 1.1191 + { 1.1192 + result->fe_lname = result->fe_fname + (result->fe_fnlen - fe_size); 1.1193 + result->fe_lnlen = (&(line[linelen])) - (result->fe_lname); 1.1194 + result->fe_fnlen -= fe_size + 4; 1.1195 + } 1.1196 + else 1.1197 + { 1.1198 + /* Search for sequence " -> " from the end for case when there are 1.1199 + more occurrences. F.e. if ftpd returns "a -> b -> c" assume 1.1200 + "a -> b" as a name. Powerusers can remove unnecessary parts 1.1201 + manually but there is no way to follow the link when some 1.1202 + essential part is missing. */ 1.1203 + p = result->fe_fname + (result->fe_fnlen - 5); 1.1204 + for (pos = (result->fe_fnlen - 5); pos > 0; pos--) 1.1205 + { 1.1206 + if (PL_strncmp(p, " -> ", 4) == 0) 1.1207 + { 1.1208 + result->fe_lname = p + 4; 1.1209 + result->fe_lnlen = (&(line[linelen])) 1.1210 + - (result->fe_lname); 1.1211 + result->fe_fnlen = pos; 1.1212 + break; 1.1213 + } 1.1214 + p--; 1.1215 + } 1.1216 + } 1.1217 + } 1.1218 + 1.1219 +#if defined(SUPPORT_LSLF) /* some (very rare) servers return ls -lF */ 1.1220 + if (result->fe_fnlen > 1) 1.1221 + { 1.1222 + p = result->fe_fname[result->fe_fnlen-1]; 1.1223 + pos = result->fe_type; 1.1224 + if (pos == 'd') { 1.1225 + if (*p == '/') result->fe_fnlen--; /* directory */ 1.1226 + } else if (pos == 'l') { 1.1227 + if (*p == '@') result->fe_fnlen--; /* symlink */ 1.1228 + } else if (pos == 'f') { 1.1229 + if (*p == '*') result->fe_fnlen--; /* executable */ 1.1230 + } else if (*p == '=' || *p == '%' || *p == '|') { 1.1231 + result->fe_fnlen--; /* socket, whiteout, fifo */ 1.1232 + } 1.1233 + } 1.1234 +#endif 1.1235 + 1.1236 + /* the caller should do this (if dropping "." and ".." is desired) 1.1237 + if (result->fe_type == 'd' && result->fe_fname[0] == '.' && 1.1238 + (result->fe_fnlen == 1 || (result->fe_fnlen == 2 && 1.1239 + result->fe_fname[1] == '.'))) 1.1240 + return '?'; 1.1241 + */ 1.1242 + 1.1243 + return result->fe_type; 1.1244 + 1.1245 + } /* if (lstyle == 'U') */ 1.1246 + 1.1247 + } /* if (!lstyle && (!state->lstyle || state->lstyle == 'U')) */ 1.1248 +#endif 1.1249 + 1.1250 + /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ 1.1251 + 1.1252 +#if defined(SUPPORT_W16) /* 16bit Windows */ 1.1253 + if (!lstyle && (!state->lstyle || state->lstyle == 'w')) 1.1254 + { /* old SuperTCP suite FTP server for Win3.1 */ 1.1255 + /* old NetManage Chameleon TCP/IP suite FTP server for Win3.1 */ 1.1256 + /* 1.1257 + * SuperTCP dirlist from the mirror.pl project 1.1258 + * mon/day/year separator may be '/' or '-'. 1.1259 + * . <DIR> 11-16-94 17:16 1.1260 + * .. <DIR> 11-16-94 17:16 1.1261 + * INSTALL <DIR> 11-16-94 17:17 1.1262 + * CMT <DIR> 11-21-94 10:17 1.1263 + * DESIGN1.DOC 11264 05-11-95 14:20 1.1264 + * README.TXT 1045 05-10-95 11:01 1.1265 + * WPKIT1.EXE 960338 06-21-95 17:01 1.1266 + * CMT.CSV 0 07-06-95 14:56 1.1267 + * 1.1268 + * Chameleon dirlist guessed from lynx 1.1269 + * . <DIR> Nov 16 1994 17:16 1.1270 + * .. <DIR> Nov 16 1994 17:16 1.1271 + * INSTALL <DIR> Nov 16 1994 17:17 1.1272 + * CMT <DIR> Nov 21 1994 10:17 1.1273 + * DESIGN1.DOC 11264 May 11 1995 14:20 A 1.1274 + * README.TXT 1045 May 10 1995 11:01 1.1275 + * WPKIT1.EXE 960338 Jun 21 1995 17:01 R 1.1276 + * CMT.CSV 0 Jul 06 1995 14:56 RHA 1.1277 + */ 1.1278 + if (numtoks >= 4 && toklen[0] < 13 && 1.1279 + ((toklen[1] == 5 && *tokens[1] == '<') || isdigit(*tokens[1])) ) 1.1280 + { 1.1281 + if (numtoks == 4 1.1282 + && (toklen[2] == 8 || toklen[2] == 9) 1.1283 + && (((tokens[2][2]) == '/' && (tokens[2][5]) == '/') || 1.1284 + ((tokens[2][2]) == '-' && (tokens[2][5]) == '-')) 1.1285 + && (toklen[3] == 4 || toklen[3] == 5) 1.1286 + && (tokens[3][toklen[3]-3]) == ':' 1.1287 + && isdigit(tokens[2][0]) && isdigit(tokens[2][1]) 1.1288 + && isdigit(tokens[2][3]) && isdigit(tokens[2][4]) 1.1289 + && isdigit(tokens[2][6]) && isdigit(tokens[2][7]) 1.1290 + && (toklen[2] < 9 || isdigit(tokens[2][8])) 1.1291 + && isdigit(tokens[3][toklen[3]-1]) && isdigit(tokens[3][toklen[3]-2]) 1.1292 + && isdigit(tokens[3][toklen[3]-4]) && isdigit(*tokens[3]) 1.1293 + ) 1.1294 + { 1.1295 + lstyle = 'w'; 1.1296 + } 1.1297 + else if ((numtoks == 6 || numtoks == 7) 1.1298 + && toklen[2] == 3 && toklen[3] == 2 1.1299 + && toklen[4] == 4 && toklen[5] == 5 1.1300 + && (tokens[5][2]) == ':' 1.1301 + && isalpha(tokens[2][0]) && isalpha(tokens[2][1]) 1.1302 + && isalpha(tokens[2][2]) 1.1303 + && isdigit(tokens[3][0]) && isdigit(tokens[3][1]) 1.1304 + && isdigit(tokens[4][0]) && isdigit(tokens[4][1]) 1.1305 + && isdigit(tokens[4][2]) && isdigit(tokens[4][3]) 1.1306 + && isdigit(tokens[5][0]) && isdigit(tokens[5][1]) 1.1307 + && isdigit(tokens[5][3]) && isdigit(tokens[5][4]) 1.1308 + /* could also check that (&(tokens[5][5]) - tokens[2]) == 17 */ 1.1309 + ) 1.1310 + { 1.1311 + lstyle = 'w'; 1.1312 + } 1.1313 + if (lstyle && state->lstyle != lstyle) /* first time */ 1.1314 + { 1.1315 + p = tokens[1]; 1.1316 + if (toklen[1] != 5 || p[0] != '<' || p[1] != 'D' || 1.1317 + p[2] != 'I' || p[3] != 'R' || p[4] != '>') 1.1318 + { 1.1319 + for (pos = 0; lstyle && pos < toklen[1]; pos++) 1.1320 + { 1.1321 + if (!isdigit(*p++)) 1.1322 + lstyle = 0; 1.1323 + } 1.1324 + } /* not <DIR> */ 1.1325 + } /* if (first time) */ 1.1326 + } /* if (numtoks == ...) */ 1.1327 + 1.1328 + if (lstyle == 'w') 1.1329 + { 1.1330 + state->parsed_one = 1; 1.1331 + state->lstyle = lstyle; 1.1332 + 1.1333 + result->fe_cinfs = 1; 1.1334 + result->fe_fname = tokens[0]; 1.1335 + result->fe_fnlen = toklen[0]; 1.1336 + result->fe_type = 'd'; 1.1337 + 1.1338 + p = tokens[1]; 1.1339 + if (isdigit(*p)) 1.1340 + { 1.1341 + result->fe_type = 'f'; 1.1342 + pos = toklen[1]; 1.1343 + if (pos > (sizeof(result->fe_size)-1)) 1.1344 + pos = sizeof(result->fe_size)-1; 1.1345 + memcpy( result->fe_size, p, pos ); 1.1346 + result->fe_size[pos] = '\0'; 1.1347 + } 1.1348 + 1.1349 + p = tokens[2]; 1.1350 + if (toklen[2] == 3) /* Chameleon */ 1.1351 + { 1.1352 + tbuf[0] = toupper(p[0]); 1.1353 + tbuf[1] = tolower(p[1]); 1.1354 + tbuf[2] = tolower(p[2]); 1.1355 + for (pos = 0; pos < (12*3); pos+=3) 1.1356 + { 1.1357 + if (tbuf[0] == month_names[pos+0] && 1.1358 + tbuf[1] == month_names[pos+1] && 1.1359 + tbuf[2] == month_names[pos+2]) 1.1360 + { 1.1361 + result->fe_time.tm_month = pos/3; 1.1362 + result->fe_time.tm_mday = atoi(tokens[3]); 1.1363 + result->fe_time.tm_year = atoi(tokens[4]) - 1900; 1.1364 + break; 1.1365 + } 1.1366 + } 1.1367 + pos = 5; /* Chameleon toknum of date field */ 1.1368 + } 1.1369 + else 1.1370 + { 1.1371 + result->fe_time.tm_month = atoi(p+0)-1; 1.1372 + result->fe_time.tm_mday = atoi(p+3); 1.1373 + result->fe_time.tm_year = atoi(p+6); 1.1374 + if (result->fe_time.tm_year < 80) /* SuperTCP */ 1.1375 + result->fe_time.tm_year += 100; 1.1376 + 1.1377 + pos = 3; /* SuperTCP toknum of date field */ 1.1378 + } 1.1379 + 1.1380 + result->fe_time.tm_hour = atoi(tokens[pos]); 1.1381 + result->fe_time.tm_min = atoi(&(tokens[pos][toklen[pos]-2])); 1.1382 + 1.1383 + /* the caller should do this (if dropping "." and ".." is desired) 1.1384 + if (result->fe_type == 'd' && result->fe_fname[0] == '.' && 1.1385 + (result->fe_fnlen == 1 || (result->fe_fnlen == 2 && 1.1386 + result->fe_fname[1] == '.'))) 1.1387 + return '?'; 1.1388 + */ 1.1389 + 1.1390 + return result->fe_type; 1.1391 + } /* (lstyle == 'w') */ 1.1392 + 1.1393 + } /* if (!lstyle && (!state->lstyle || state->lstyle == 'w')) */ 1.1394 +#endif 1.1395 + 1.1396 + /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ 1.1397 + 1.1398 +#if defined(SUPPORT_DLS) /* dls -dtR */ 1.1399 + if (!lstyle && 1.1400 + (state->lstyle == 'D' || (!state->lstyle && state->numlines == 1))) 1.1401 + /* /bin/dls lines have to be immediately recognizable (first line) */ 1.1402 + { 1.1403 + /* I haven't seen an FTP server that delivers a /bin/dls listing, 1.1404 + * but can infer the format from the lynx and mirror.pl projects. 1.1405 + * Both formats are supported. 1.1406 + * 1.1407 + * Lynx says: 1.1408 + * README 763 Information about this server\0 1.1409 + * bin/ - \0 1.1410 + * etc/ = \0 1.1411 + * ls-lR 0 \0 1.1412 + * ls-lR.Z 3 \0 1.1413 + * pub/ = Public area\0 1.1414 + * usr/ - \0 1.1415 + * morgan 14 -> ../real/morgan\0 1.1416 + * TIMIT.mostlikely.Z\0 1.1417 + * 79215 \0 1.1418 + * 1.1419 + * mirror.pl says: 1.1420 + * filename: ^(\S*)\s+ 1.1421 + * size: (\-|\=|\d+)\s+ 1.1422 + * month/day: ((\w\w\w\s+\d+|\d+\s+\w\w\w)\s+ 1.1423 + * time/year: (\d+:\d+|\d\d\d\d))\s+ 1.1424 + * rest: (.+) 1.1425 + * 1.1426 + * README 763 Jul 11 21:05 Information about this server 1.1427 + * bin/ - Apr 28 1994 1.1428 + * etc/ = 11 Jul 21:04 1.1429 + * ls-lR 0 6 Aug 17:14 1.1430 + * ls-lR.Z 3 05 Sep 1994 1.1431 + * pub/ = Jul 11 21:04 Public area 1.1432 + * usr/ - Sep 7 09:39 1.1433 + * morgan 14 Apr 18 09:39 -> ../real/morgan 1.1434 + * TIMIT.mostlikely.Z 1.1435 + * 79215 Jul 11 21:04 1.1436 + */ 1.1437 + if (!state->lstyle && line[linelen-1] == ':' && 1.1438 + linelen >= 2 && toklen[numtoks-1] != 1) 1.1439 + { 1.1440 + /* code in mirror.pl suggests that a listing may be preceded 1.1441 + * by a PWD line in the form "/some/dir/names/here:" 1.1442 + * but does not necessarily begin with '/'. *sigh* 1.1443 + */ 1.1444 + pos = 0; 1.1445 + p = line; 1.1446 + while (pos < (linelen-1)) 1.1447 + { 1.1448 + /* illegal (or extremely unusual) chars in a dirspec */ 1.1449 + if (*p == '<' || *p == '|' || *p == '>' || 1.1450 + *p == '?' || *p == '*' || *p == '\\') 1.1451 + break; 1.1452 + if (*p == '/' && pos < (linelen-2) && p[1] == '/') 1.1453 + break; 1.1454 + pos++; 1.1455 + p++; 1.1456 + } 1.1457 + if (pos == (linelen-1)) 1.1458 + { 1.1459 + state->lstyle = 'D'; 1.1460 + return '?'; 1.1461 + } 1.1462 + } 1.1463 + 1.1464 + if (!lstyle && numtoks >= 2) 1.1465 + { 1.1466 + pos = 22; /* pos of (\d+|-|=) if this is not part of a multiline */ 1.1467 + if (state->lstyle && carry_buf_len) /* first is from previous line */ 1.1468 + pos = toklen[1]-1; /* and is 'as-is' (may contain whitespace) */ 1.1469 + 1.1470 + if (linelen > pos) 1.1471 + { 1.1472 + p = &line[pos]; 1.1473 + if ((*p == '-' || *p == '=' || isdigit(*p)) && 1.1474 + ((linelen == (pos+1)) || 1.1475 + (linelen >= (pos+3) && p[1] == ' ' && p[2] == ' ')) ) 1.1476 + { 1.1477 + tokmarker = 1; 1.1478 + if (!carry_buf_len) 1.1479 + { 1.1480 + pos = 1; 1.1481 + while (pos < numtoks && (tokens[pos]+toklen[pos]) < (&line[23])) 1.1482 + pos++; 1.1483 + tokmarker = 0; 1.1484 + if ((tokens[pos]+toklen[pos]) == (&line[23])) 1.1485 + tokmarker = pos; 1.1486 + } 1.1487 + if (tokmarker) 1.1488 + { 1.1489 + lstyle = 'D'; 1.1490 + if (*tokens[tokmarker] == '-' || *tokens[tokmarker] == '=') 1.1491 + { 1.1492 + if (toklen[tokmarker] != 1 || 1.1493 + (tokens[tokmarker-1][toklen[tokmarker-1]-1]) != '/') 1.1494 + lstyle = 0; 1.1495 + } 1.1496 + else 1.1497 + { 1.1498 + for (pos = 0; lstyle && pos < toklen[tokmarker]; pos++) 1.1499 + { 1.1500 + if (!isdigit(tokens[tokmarker][pos])) 1.1501 + lstyle = 0; 1.1502 + } 1.1503 + } 1.1504 + if (lstyle && !state->lstyle) /* first time */ 1.1505 + { 1.1506 + /* scan for illegal (or incredibly unusual) chars in fname */ 1.1507 + for (p = tokens[0]; lstyle && 1.1508 + p < &(tokens[tokmarker-1][toklen[tokmarker-1]]); p++) 1.1509 + { 1.1510 + if (*p == '<' || *p == '|' || *p == '>' || 1.1511 + *p == '?' || *p == '*' || *p == '/' || *p == '\\') 1.1512 + lstyle = 0; 1.1513 + } 1.1514 + } 1.1515 + 1.1516 + } /* size token found */ 1.1517 + } /* expected chars behind expected size token */ 1.1518 + } /* if (linelen > pos) */ 1.1519 + } /* if (!lstyle && numtoks >= 2) */ 1.1520 + 1.1521 + if (!lstyle && state->lstyle == 'D' && !carry_buf_len) 1.1522 + { 1.1523 + /* the filename of a multi-line entry can be identified 1.1524 + * correctly only if dls format had been previously established. 1.1525 + * This should always be true because there should be entries 1.1526 + * for '.' and/or '..' and/or CWD that precede the rest of the 1.1527 + * listing. 1.1528 + */ 1.1529 + pos = linelen; 1.1530 + if (pos > (sizeof(state->carry_buf)-1)) 1.1531 + pos = sizeof(state->carry_buf)-1; 1.1532 + memcpy( state->carry_buf, line, pos ); 1.1533 + state->carry_buf_len = pos; 1.1534 + return '?'; 1.1535 + } 1.1536 + 1.1537 + if (lstyle == 'D') 1.1538 + { 1.1539 + state->parsed_one = 1; 1.1540 + state->lstyle = lstyle; 1.1541 + 1.1542 + p = &(tokens[tokmarker-1][toklen[tokmarker-1]]); 1.1543 + result->fe_fname = tokens[0]; 1.1544 + result->fe_fnlen = p - tokens[0]; 1.1545 + result->fe_type = 'f'; 1.1546 + 1.1547 + if (result->fe_fname[result->fe_fnlen-1] == '/') 1.1548 + { 1.1549 + if (result->fe_lnlen == 1) 1.1550 + result->fe_type = '?'; 1.1551 + else 1.1552 + { 1.1553 + result->fe_fnlen--; 1.1554 + result->fe_type = 'd'; 1.1555 + } 1.1556 + } 1.1557 + else if (isdigit(*tokens[tokmarker])) 1.1558 + { 1.1559 + pos = toklen[tokmarker]; 1.1560 + if (pos > (sizeof(result->fe_size)-1)) 1.1561 + pos = sizeof(result->fe_size)-1; 1.1562 + memcpy( result->fe_size, tokens[tokmarker], pos ); 1.1563 + result->fe_size[pos] = '\0'; 1.1564 + } 1.1565 + 1.1566 + if ((tokmarker+3) < numtoks && 1.1567 + (&(tokens[numtoks-1][toklen[numtoks-1]]) - 1.1568 + tokens[tokmarker+1]) >= (1+1+3+1+4) ) 1.1569 + { 1.1570 + pos = (tokmarker+3); 1.1571 + p = tokens[pos]; 1.1572 + pos = toklen[pos]; 1.1573 + 1.1574 + if ((pos == 4 || pos == 5) 1.1575 + && isdigit(*p) && isdigit(p[pos-1]) && isdigit(p[pos-2]) 1.1576 + && ((pos == 5 && p[2] == ':') || 1.1577 + (pos == 4 && (isdigit(p[1]) || p[1] == ':'))) 1.1578 + ) 1.1579 + { 1.1580 + month_num = tokmarker+1; /* assumed position of month field */ 1.1581 + pos = tokmarker+2; /* assumed position of mday field */ 1.1582 + if (isdigit(*tokens[month_num])) /* positions are reversed */ 1.1583 + { 1.1584 + month_num++; 1.1585 + pos--; 1.1586 + } 1.1587 + p = tokens[month_num]; 1.1588 + if (isdigit(*tokens[pos]) 1.1589 + && (toklen[pos] == 1 || 1.1590 + (toklen[pos] == 2 && isdigit(tokens[pos][1]))) 1.1591 + && toklen[month_num] == 3 1.1592 + && isalpha(*p) && isalpha(p[1]) && isalpha(p[2]) ) 1.1593 + { 1.1594 + pos = atoi(tokens[pos]); 1.1595 + if (pos > 0 && pos <= 31) 1.1596 + { 1.1597 + result->fe_time.tm_mday = pos; 1.1598 + month_num = 1; 1.1599 + for (pos = 0; pos < (12*3); pos+=3) 1.1600 + { 1.1601 + if (p[0] == month_names[pos+0] && 1.1602 + p[1] == month_names[pos+1] && 1.1603 + p[2] == month_names[pos+2]) 1.1604 + break; 1.1605 + month_num++; 1.1606 + } 1.1607 + if (month_num > 12) 1.1608 + result->fe_time.tm_mday = 0; 1.1609 + else 1.1610 + result->fe_time.tm_month = month_num - 1; 1.1611 + } 1.1612 + } 1.1613 + if (result->fe_time.tm_mday) 1.1614 + { 1.1615 + tokmarker += 3; /* skip mday/mon/yrtime (to find " -> ") */ 1.1616 + p = tokens[tokmarker]; 1.1617 + 1.1618 + pos = atoi(p); 1.1619 + if (pos > 24) 1.1620 + result->fe_time.tm_year = pos-1900; 1.1621 + else 1.1622 + { 1.1623 + if (p[1] == ':') 1.1624 + p--; 1.1625 + result->fe_time.tm_hour = pos; 1.1626 + result->fe_time.tm_min = atoi(p+3); 1.1627 + if (!state->now_time) 1.1628 + { 1.1629 + state->now_time = PR_Now(); 1.1630 + PR_ExplodeTime((state->now_time), PR_LocalTimeParameters, &(state->now_tm) ); 1.1631 + } 1.1632 + result->fe_time.tm_year = state->now_tm.tm_year; 1.1633 + if ( (( state->now_tm.tm_month << 4) + state->now_tm.tm_mday) < 1.1634 + ((result->fe_time.tm_month << 4) + result->fe_time.tm_mday) ) 1.1635 + result->fe_time.tm_year--; 1.1636 + } /* got year or time */ 1.1637 + } /* got month/mday */ 1.1638 + } /* may have year or time */ 1.1639 + } /* enough remaining to possibly have date/time */ 1.1640 + 1.1641 + if (numtoks > (tokmarker+2)) 1.1642 + { 1.1643 + pos = tokmarker+1; 1.1644 + p = tokens[pos]; 1.1645 + if (toklen[pos] == 2 && *p == '-' && p[1] == '>') 1.1646 + { 1.1647 + p = &(tokens[numtoks-1][toklen[numtoks-1]]); 1.1648 + result->fe_type = 'l'; 1.1649 + result->fe_lname = tokens[pos+1]; 1.1650 + result->fe_lnlen = p - result->fe_lname; 1.1651 + if (result->fe_lnlen > 1 && 1.1652 + result->fe_lname[result->fe_lnlen-1] == '/') 1.1653 + result->fe_lnlen--; 1.1654 + } 1.1655 + } /* if (numtoks > (tokmarker+2)) */ 1.1656 + 1.1657 + /* the caller should do this (if dropping "." and ".." is desired) 1.1658 + if (result->fe_type == 'd' && result->fe_fname[0] == '.' && 1.1659 + (result->fe_fnlen == 1 || (result->fe_fnlen == 2 && 1.1660 + result->fe_fname[1] == '.'))) 1.1661 + return '?'; 1.1662 + */ 1.1663 + 1.1664 + return result->fe_type; 1.1665 + 1.1666 + } /* if (lstyle == 'D') */ 1.1667 + } /* if (!lstyle && (!state->lstyle || state->lstyle == 'D')) */ 1.1668 +#endif 1.1669 + 1.1670 + /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ 1.1671 + 1.1672 + } /* if (linelen > 0) */ 1.1673 + 1.1674 + return ParsingFailed(state); 1.1675 +} 1.1676 + 1.1677 +/* ==================================================================== */ 1.1678 +/* standalone testing */ 1.1679 +/* ==================================================================== */ 1.1680 +#if 0 1.1681 + 1.1682 +#include <stdio.h> 1.1683 + 1.1684 +static int do_it(FILE *outfile, 1.1685 + char *line, size_t linelen, struct list_state *state, 1.1686 + char **cmnt_buf, unsigned int *cmnt_buf_sz, 1.1687 + char **list_buf, unsigned int *list_buf_sz ) 1.1688 +{ 1.1689 + struct list_result result; 1.1690 + char *p; 1.1691 + int rc; 1.1692 + 1.1693 + rc = ParseFTPList( line, state, &result ); 1.1694 + 1.1695 + if (!outfile) 1.1696 + { 1.1697 + outfile = stdout; 1.1698 + if (rc == '?') 1.1699 + fprintf(outfile, "junk: %.*s\n", (int)linelen, line ); 1.1700 + else if (rc == '"') 1.1701 + fprintf(outfile, "cmnt: %.*s\n", (int)linelen, line ); 1.1702 + else 1.1703 + fprintf(outfile, 1.1704 + "list: %02u-%02u-%02u %02u:%02u%cM %20s %.*s%s%.*s\n", 1.1705 + (result.fe_time.tm_mday ? (result.fe_time.tm_month + 1) : 0), 1.1706 + result.fe_time.tm_mday, 1.1707 + (result.fe_time.tm_mday ? (result.fe_time.tm_year % 100) : 0), 1.1708 + result.fe_time.tm_hour - 1.1709 + ((result.fe_time.tm_hour > 12)?(12):(0)), 1.1710 + result.fe_time.tm_min, 1.1711 + ((result.fe_time.tm_hour >= 12) ? 'P' : 'A'), 1.1712 + (rc == 'd' ? "<DIR> " : 1.1713 + (rc == 'l' ? "<JUNCTION> " : result.fe_size)), 1.1714 + (int)result.fe_fnlen, result.fe_fname, 1.1715 + ((rc == 'l' && result.fe_lnlen) ? " -> " : ""), 1.1716 + (int)((rc == 'l' && result.fe_lnlen) ? result.fe_lnlen : 0), 1.1717 + ((rc == 'l' && result.fe_lnlen) ? result.fe_lname : "") ); 1.1718 + } 1.1719 + else if (rc != '?') /* NOT junk */ 1.1720 + { 1.1721 + char **bufp = list_buf; 1.1722 + unsigned int *bufz = list_buf_sz; 1.1723 + 1.1724 + if (rc == '"') /* comment - make it a 'result' */ 1.1725 + { 1.1726 + memset( &result, 0, sizeof(result)); 1.1727 + result.fe_fname = line; 1.1728 + result.fe_fnlen = linelen; 1.1729 + result.fe_type = 'f'; 1.1730 + if (line[linelen-1] == '/') 1.1731 + { 1.1732 + result.fe_type = 'd'; 1.1733 + result.fe_fnlen--; 1.1734 + } 1.1735 + bufp = cmnt_buf; 1.1736 + bufz = cmnt_buf_sz; 1.1737 + rc = result.fe_type; 1.1738 + } 1.1739 + 1.1740 + linelen = 80 + result.fe_fnlen + result.fe_lnlen; 1.1741 + p = (char *)realloc( *bufp, *bufz + linelen ); 1.1742 + if (!p) 1.1743 + return -1; 1.1744 + sprintf( &p[*bufz], 1.1745 + "%02u-%02u-%04u %02u:%02u:%02u %20s %.*s%s%.*s\n", 1.1746 + (result.fe_time.tm_mday ? (result.fe_time.tm_month + 1) : 0), 1.1747 + result.fe_time.tm_mday, 1.1748 + (result.fe_time.tm_mday ? (result.fe_time.tm_year + 1900) : 0), 1.1749 + result.fe_time.tm_hour, 1.1750 + result.fe_time.tm_min, 1.1751 + result.fe_time.tm_sec, 1.1752 + (rc == 'd' ? "<DIR> " : 1.1753 + (rc == 'l' ? "<JUNCTION> " : result.fe_size)), 1.1754 + (int)result.fe_fnlen, result.fe_fname, 1.1755 + ((rc == 'l' && result.fe_lnlen) ? " -> " : ""), 1.1756 + (int)((rc == 'l' && result.fe_lnlen) ? result.fe_lnlen : 0), 1.1757 + ((rc == 'l' && result.fe_lnlen) ? result.fe_lname : "") ); 1.1758 + linelen = strlen(&p[*bufz]); 1.1759 + *bufp = p; 1.1760 + *bufz = *bufz + linelen; 1.1761 + } 1.1762 + return 0; 1.1763 +} 1.1764 + 1.1765 +int main(int argc, char *argv[]) 1.1766 +{ 1.1767 + FILE *infile = (FILE *)0; 1.1768 + FILE *outfile = (FILE *)0; 1.1769 + int need_close_in = 0; 1.1770 + int need_close_out = 0; 1.1771 + 1.1772 + if (argc > 1) 1.1773 + { 1.1774 + infile = stdin; 1.1775 + if (strcmp(argv[1], "-") == 0) 1.1776 + need_close_in = 0; 1.1777 + else if ((infile = fopen(argv[1], "r")) != ((FILE *)0)) 1.1778 + need_close_in = 1; 1.1779 + else 1.1780 + fprintf(stderr, "Unable to open input file '%s'\n", argv[1]); 1.1781 + } 1.1782 + if (infile && argc > 2) 1.1783 + { 1.1784 + outfile = stdout; 1.1785 + if (strcmp(argv[2], "-") == 0) 1.1786 + need_close_out = 0; 1.1787 + else if ((outfile = fopen(argv[2], "w")) != ((FILE *)0)) 1.1788 + need_close_out = 1; 1.1789 + else 1.1790 + { 1.1791 + fprintf(stderr, "Unable to open output file '%s'\n", argv[2]); 1.1792 + fclose(infile); 1.1793 + infile = (FILE *)0; 1.1794 + } 1.1795 + } 1.1796 + 1.1797 + if (!infile) 1.1798 + { 1.1799 + char *appname = &(argv[0][strlen(argv[0])]); 1.1800 + while (appname > argv[0]) 1.1801 + { 1.1802 + appname--; 1.1803 + if (*appname == '/' || *appname == '\\' || *appname == ':') 1.1804 + { 1.1805 + appname++; 1.1806 + break; 1.1807 + } 1.1808 + } 1.1809 + fprintf(stderr, 1.1810 + "Usage: %s <inputfilename> [<outputfilename>]\n" 1.1811 + "\nIf an outout file is specified the results will be" 1.1812 + "\nbe post-processed, and only the file entries will appear" 1.1813 + "\n(or all comments if there are no file entries)." 1.1814 + "\nNot specifying an output file causes %s to run in \"debug\"" 1.1815 + "\nmode, ie results are printed as lines are parsed." 1.1816 + "\nIf a filename is a single dash ('-'), stdin/stdout is used." 1.1817 + "\n", appname, appname ); 1.1818 + } 1.1819 + else 1.1820 + { 1.1821 + char *cmnt_buf = (char *)0; 1.1822 + unsigned int cmnt_buf_sz = 0; 1.1823 + char *list_buf = (char *)0; 1.1824 + unsigned int list_buf_sz = 0; 1.1825 + 1.1826 + struct list_state state; 1.1827 + char line[512]; 1.1828 + 1.1829 + memset( &state, 0, sizeof(state) ); 1.1830 + while (fgets(line, sizeof(line), infile)) 1.1831 + { 1.1832 + size_t linelen = strlen(line); 1.1833 + if (linelen < (sizeof(line)-1)) 1.1834 + { 1.1835 + if (linelen > 0 && line[linelen-1] == '\n') 1.1836 + linelen--; 1.1837 + if (do_it( outfile, line, linelen, &state, 1.1838 + &cmnt_buf, &cmnt_buf_sz, &list_buf, &list_buf_sz) != 0) 1.1839 + { 1.1840 + fprintf(stderr, "Insufficient memory. Listing may be incomplete.\n"); 1.1841 + break; 1.1842 + } 1.1843 + } 1.1844 + else 1.1845 + { 1.1846 + /* no '\n' found. drop this and everything up to the next '\n' */ 1.1847 + fprintf(stderr, "drop: %.*s", (int)linelen, line ); 1.1848 + while (linelen == sizeof(line)) 1.1849 + { 1.1850 + if (!fgets(line, sizeof(line), infile)) 1.1851 + break; 1.1852 + linelen = 0; 1.1853 + while (linelen < sizeof(line) && line[linelen] != '\n') 1.1854 + linelen++; 1.1855 + fprintf(stderr, "%.*s", (int)linelen, line ); 1.1856 + } 1.1857 + fprintf(stderr, "\n"); 1.1858 + } 1.1859 + } 1.1860 + if (outfile) 1.1861 + { 1.1862 + if (list_buf) 1.1863 + fwrite( list_buf, 1, list_buf_sz, outfile ); 1.1864 + else if (cmnt_buf) 1.1865 + fwrite( cmnt_buf, 1, cmnt_buf_sz, outfile ); 1.1866 + } 1.1867 + if (list_buf) 1.1868 + free(list_buf); 1.1869 + if (cmnt_buf) 1.1870 + free(cmnt_buf); 1.1871 + 1.1872 + if (need_close_in) 1.1873 + fclose(infile); 1.1874 + if (outfile && need_close_out) 1.1875 + fclose(outfile); 1.1876 + } 1.1877 + 1.1878 + return 0; 1.1879 +} 1.1880 +#endif