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