Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(408)

Side by Side Diff: net/third_party/parseftp/ParseFTPList.cpp

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

Powered by Google App Engine
This is Rietveld 408576698