Ticket #7831: directorylistingparser.cpp

File directorylistingparser.cpp, 65.0 KB (added by henri, 11 years ago)

ParseAsVms method fixed to support Vms filenames

Line 
1#include <filezilla.h>
2#include "directorylistingparser.h"
3#include "ControlSocket.h"
4#include "../interface/Options.h"
5
6#ifdef _DEBUG
7#define new DEBUG_NEW
8#endif
9
10std::map<wxString, int> CDirectoryListingParser::m_MonthNamesMap;
11
12//#define LISTDEBUG_MVS
13//#define LISTDEBUG
14#ifdef LISTDEBUG
15static char data[][150]={
16 "" // Has to be terminated with empty string
17};
18
19#endif
20
21extern wxString StripVMSRevision(const wxString& name);
22
23class CToken
24{
25protected:
26 enum TokenInformation
27 {
28 Unknown,
29 Yes,
30 No
31 };
32
33public:
34 CToken()
35 : m_pToken()
36 , m_len()
37 , m_numeric(Unknown)
38 , m_leftNumeric(Unknown)
39 , m_rightNumeric(Unknown)
40 , m_number(-1)
41 {
42 }
43
44 enum t_numberBase
45 {
46 decimal,
47 hex
48 };
49
50 CToken(const wxChar* p, unsigned int len)
51 : m_pToken(p)
52 , m_len(len)
53 , m_numeric(Unknown)
54 , m_leftNumeric(Unknown)
55 , m_rightNumeric(Unknown)
56 , m_number(-1)
57 {}
58
59 const wxChar* GetToken() const
60 {
61 return m_pToken;
62 }
63
64 unsigned int GetLength() const
65 {
66 return m_len;
67 }
68
69 wxString GetString(unsigned int type = 0)
70 {
71 if (!m_pToken)
72 return _T("");
73
74 if (type > 2)
75 return _T("");
76
77 if (!m_str[type].IsEmpty())
78 return m_str[type];
79
80 if (!type)
81 {
82 wxString str(m_pToken, m_len);
83 m_str[type] = str;
84 return str;
85 }
86 else if (type == 1)
87 {
88 if (!IsRightNumeric() || IsNumeric())
89 return _T("");
90
91 int pos = m_len - 1;
92 while (m_pToken[pos] >= '0' && m_pToken[pos] <= '9')
93 --pos;
94
95 wxString str(m_pToken, pos + 1);
96 m_str[type] = str;
97 return str;
98 }
99 else if (type == 2)
100 {
101 if (!IsLeftNumeric() || IsNumeric())
102 return _T("");
103
104 int pos = 0;
105 while (m_pToken[pos] >= '0' && m_pToken[pos] <= '9')
106 ++pos;
107
108 wxString str(m_pToken + pos, m_len - pos);
109 m_str[type] = str;
110 return str;
111 }
112
113 return _T("");
114 }
115
116 bool IsNumeric(t_numberBase base = decimal)
117 {
118 switch (base)
119 {
120 case decimal:
121 default:
122 if (m_numeric == Unknown)
123 {
124 m_numeric = Yes;
125 for (unsigned int i = 0; i < m_len; ++i)
126 if (m_pToken[i] < '0' || m_pToken[i] > '9')
127 {
128 m_numeric = No;
129 break;
130 }
131 }
132 return m_numeric == Yes;
133 case hex:
134 for (unsigned int i = 0; i < m_len; ++i)
135 {
136 const char c = m_pToken[i];
137 if ((c < '0' || c > '9') && (c < 'A' || c > 'F') && (c < 'a' || c > 'f'))
138 return false;
139 }
140 return true;
141 }
142 }
143
144 bool IsNumeric(unsigned int start, unsigned int len)
145 {
146 for (unsigned int i = start; i < wxMin(start + len, m_len); ++i)
147 if (m_pToken[i] < '0' || m_pToken[i] > '9')
148 return false;
149 return true;
150 }
151
152 bool IsLeftNumeric()
153 {
154 if (m_leftNumeric == Unknown)
155 {
156 if (m_len < 2)
157 m_leftNumeric = No;
158 else if (m_pToken[0] < '0' || m_pToken[0] > '9')
159 m_leftNumeric = No;
160 else
161 m_leftNumeric = Yes;
162 }
163 return m_leftNumeric == Yes;
164 }
165
166 bool IsRightNumeric()
167 {
168 if (m_rightNumeric == Unknown)
169 {
170 if (m_len < 2)
171 m_rightNumeric = No;
172 else if (m_pToken[m_len - 1] < '0' || m_pToken[m_len - 1] > '9')
173 m_rightNumeric = No;
174 else
175 m_rightNumeric = Yes;
176 }
177 return m_rightNumeric == Yes;
178 }
179
180 int Find(const wxChar* chr, int start = 0) const
181 {
182 if (!chr)
183 return -1;
184
185 for (unsigned int i = start; i < m_len; ++i)
186 {
187 for (int c = 0; chr[c]; ++c)
188 {
189 if (m_pToken[i] == chr[c])
190 return i;
191 }
192 }
193 return -1;
194 }
195
196 int Find(wxChar chr, int start = 0) const
197 {
198 if (!m_pToken)
199 return -1;
200
201 for (unsigned int i = start; i < m_len; ++i)
202 if (m_pToken[i] == chr)
203 return i;
204
205 return -1;
206 }
207
208 wxLongLong GetNumber(unsigned int start, int len)
209 {
210 if (len == -1)
211 len = m_len - start;
212 if (len < 1)
213 return -1;
214
215 if (start + static_cast<unsigned int>(len) > m_len)
216 return -1;
217
218 if (m_pToken[start] < '0' || m_pToken[start] > '9')
219 return -1;
220
221 wxLongLong number = 0;
222 for (unsigned int i = start; i < (start + len); ++i)
223 {
224 if (m_pToken[i] < '0' || m_pToken[i] > '9')
225 break;
226 number *= 10;
227 number += m_pToken[i] - '0';
228 }
229 return number;
230 }
231
232 wxLongLong GetNumber(t_numberBase base = decimal)
233 {
234 switch (base)
235 {
236 default:
237 case decimal:
238 if (m_number == -1)
239 {
240 if (IsNumeric() || IsLeftNumeric())
241 {
242 m_number = 0;
243 for (unsigned int i = 0; i < m_len; ++i)
244 {
245 if (m_pToken[i] < '0' || m_pToken[i] > '9')
246 break;
247 m_number *= 10;
248 m_number += m_pToken[i] - '0';
249 }
250 }
251 else if (IsRightNumeric())
252 {
253 m_number = 0;
254 int start = m_len - 1;
255 while (m_pToken[start - 1] >= '0' && m_pToken[start - 1] <= '9')
256 --start;
257 for (unsigned int i = start; i < m_len; ++i)
258 {
259 m_number *= 10;
260 m_number += m_pToken[i] - '0';
261 }
262 }
263 }
264 return m_number;
265 case hex:
266 {
267 wxLongLong number = 0;
268 for (unsigned int i = 0; i < m_len; ++i)
269 {
270 const wxChar& c = m_pToken[i];
271 if (c >= '0' && c <= '9')
272 {
273 number *= 16;
274 number += c - '0';
275 }
276 else if (c >= 'a' && c <= 'f')
277 {
278 number *= 16;
279 number += c - '0' + 10;
280 }
281 else if (c >= 'A' && c <= 'F')
282 {
283 number *= 16;
284 number += c - 'A' + 10;
285 }
286 else
287 return -1;
288 }
289 return number;
290 }
291 }
292 }
293
294 wxChar operator[](unsigned int n) const
295 {
296 if (n >= m_len)
297 return 0;
298
299 return m_pToken[n];
300 }
301
302protected:
303 const wxChar* m_pToken;
304 unsigned int m_len;
305
306 TokenInformation m_numeric;
307 TokenInformation m_leftNumeric;
308 TokenInformation m_rightNumeric;
309 wxLongLong m_number;
310 wxString m_str[3];
311};
312
313class CLine
314{
315public:
316 CLine(wxChar* p, int len = -1, int trailing_whitespace = 0)
317 {
318 m_pLine = p;
319 if (len != -1)
320 m_len = len;
321 else
322 m_len = wxStrlen(p);
323
324 m_parsePos = 0;
325
326 m_Tokens.reserve(10);
327 m_LineEndTokens.reserve(10);
328 m_trailing_whitespace = trailing_whitespace;
329 }
330
331 ~CLine()
332 {
333 delete [] m_pLine;
334
335 std::vector<CToken *>::iterator iter;
336 for (iter = m_Tokens.begin(); iter != m_Tokens.end(); ++iter)
337 delete *iter;
338 for (iter = m_LineEndTokens.begin(); iter != m_LineEndTokens.end(); ++iter)
339 delete *iter;
340 }
341
342 bool GetToken(unsigned int n, CToken &token, bool toEnd = false, bool include_whitespace = false)
343 {
344 if (!toEnd)
345 {
346 if (m_Tokens.size() > n)
347 {
348 token = *(m_Tokens[n]);
349 return true;
350 }
351
352 int start = m_parsePos;
353 while (m_parsePos < m_len)
354 {
355 if (m_pLine[m_parsePos] == ' ' || m_pLine[m_parsePos] == '\t')
356 {
357 CToken *pToken = new CToken(m_pLine + start, m_parsePos - start);
358 m_Tokens.push_back(pToken);
359
360 while ((m_pLine[m_parsePos] == ' ' || m_pLine[m_parsePos] == '\t') && m_parsePos < m_len)
361 ++m_parsePos;
362
363 if (m_Tokens.size() > n)
364 {
365 token = *(m_Tokens[n]);
366 return true;
367 }
368
369 start = m_parsePos;
370 }
371 ++m_parsePos;
372 }
373 if (m_parsePos != start)
374 {
375 CToken *pToken = new CToken(m_pLine + start, m_parsePos - start);
376 m_Tokens.push_back(pToken);
377 }
378
379 if (m_Tokens.size() > n)
380 {
381 token = *(m_Tokens[n]);
382 return true;
383 }
384
385 return false;
386 }
387 else
388 {
389 if (include_whitespace)
390 {
391 const wxChar* p;
392 if (!n)
393 {
394 CToken ref;
395 if (!GetToken(n, ref))
396 return false;
397 p = ref.GetToken() + ref.GetLength() + 1;
398 }
399 else
400 {
401 CToken ref;
402 if (!GetToken(n - 1, ref))
403 return false;
404 p = ref.GetToken() + ref.GetLength() + 1;
405 }
406
407 token = CToken(p, m_len - (p - m_pLine));
408 return true;
409 }
410
411 if (m_LineEndTokens.size() > n)
412 {
413 token = *(m_LineEndTokens[n]);
414 return true;
415 }
416
417 if (m_Tokens.size() <= n)
418 if (!GetToken(n, token))
419 return false;
420
421 for (unsigned int i = static_cast<unsigned int>(m_LineEndTokens.size()); i <= n; ++i)
422 {
423 const CToken *refToken = m_Tokens[i];
424 const wxChar* p = refToken->GetToken();
425 CToken *pToken = new CToken(p, m_len - (p - m_pLine) - m_trailing_whitespace);
426 m_LineEndTokens.push_back(pToken);
427 }
428 token = *(m_LineEndTokens[n]);
429 return true;
430 }
431 };
432
433 CLine *Concat(const CLine *pLine) const
434 {
435 int newLen = m_len + pLine->m_len + 1;
436 wxChar* p = new wxChar[newLen];
437 memcpy(p, m_pLine, m_len * sizeof(wxChar));
438 p[m_len] = ' ';
439 memcpy(p + m_len + 1, pLine->m_pLine, pLine->m_len * sizeof(wxChar));
440
441 return new CLine(p, m_len + pLine->m_len + 1, pLine->m_trailing_whitespace);
442 }
443
444protected:
445 std::vector<CToken *> m_Tokens;
446 std::vector<CToken *> m_LineEndTokens;
447 int m_parsePos;
448 int m_len;
449 int m_trailing_whitespace;
450 wxChar* m_pLine;
451};
452
453CDirectoryListingParser::CDirectoryListingParser(CControlSocket* pControlSocket, const CServer& server)
454 : m_pControlSocket(pControlSocket), m_server(server)
455{
456 m_currentOffset = 0;
457 m_prevLine = 0;
458 m_fileListOnly = true;
459 m_maybeMultilineVms = false;
460
461 if (m_MonthNamesMap.empty())
462 {
463 //Fill the month names map
464
465 //English month names
466 m_MonthNamesMap[_T("jan")] = 1;
467 m_MonthNamesMap[_T("feb")] = 2;
468 m_MonthNamesMap[_T("mar")] = 3;
469 m_MonthNamesMap[_T("apr")] = 4;
470 m_MonthNamesMap[_T("may")] = 5;
471 m_MonthNamesMap[_T("jun")] = 6;
472 m_MonthNamesMap[_T("june")] = 6;
473 m_MonthNamesMap[_T("jul")] = 7;
474 m_MonthNamesMap[_T("july")] = 7;
475 m_MonthNamesMap[_T("aug")] = 8;
476 m_MonthNamesMap[_T("sep")] = 9;
477 m_MonthNamesMap[_T("sept")] = 9;
478 m_MonthNamesMap[_T("oct")] = 10;
479 m_MonthNamesMap[_T("nov")] = 11;
480 m_MonthNamesMap[_T("dec")] = 12;
481
482 //Numerical values for the month
483 m_MonthNamesMap[_T("1")] = 1;
484 m_MonthNamesMap[_T("01")] = 1;
485 m_MonthNamesMap[_T("2")] = 2;
486 m_MonthNamesMap[_T("02")] = 2;
487 m_MonthNamesMap[_T("3")] = 3;
488 m_MonthNamesMap[_T("03")] = 3;
489 m_MonthNamesMap[_T("4")] = 4;
490 m_MonthNamesMap[_T("04")] = 4;
491 m_MonthNamesMap[_T("5")] = 5;
492 m_MonthNamesMap[_T("05")] = 5;
493 m_MonthNamesMap[_T("6")] = 6;
494 m_MonthNamesMap[_T("06")] = 6;
495 m_MonthNamesMap[_T("7")] = 7;
496 m_MonthNamesMap[_T("07")] = 7;
497 m_MonthNamesMap[_T("8")] = 8;
498 m_MonthNamesMap[_T("08")] = 8;
499 m_MonthNamesMap[_T("9")] = 9;
500 m_MonthNamesMap[_T("09")] = 9;
501 m_MonthNamesMap[_T("10")] = 10;
502 m_MonthNamesMap[_T("11")] = 11;
503 m_MonthNamesMap[_T("12")] = 12;
504
505 //German month names
506 m_MonthNamesMap[_T("mrz")] = 3;
507 m_MonthNamesMap[_T("m\xe4r")] = 3;
508 m_MonthNamesMap[_T("m\xe4rz")] = 3;
509 m_MonthNamesMap[_T("mai")] = 5;
510 m_MonthNamesMap[_T("juni")] = 6;
511 m_MonthNamesMap[_T("juli")] = 7;
512 m_MonthNamesMap[_T("okt")] = 10;
513 m_MonthNamesMap[_T("dez")] = 12;
514
515 //Austrian month names
516 m_MonthNamesMap[_T("j\xe4n")] = 1;
517
518 //French month names
519 m_MonthNamesMap[_T("janv")] = 1;
520 m_MonthNamesMap[_T("f\xe9")_T("b")] = 1;
521 m_MonthNamesMap[_T("f\xe9v")] = 2;
522 m_MonthNamesMap[_T("fev")] = 2;
523 m_MonthNamesMap[_T("f\xe9vr")] = 2;
524 m_MonthNamesMap[_T("fevr")] = 2;
525 m_MonthNamesMap[_T("mars")] = 3;
526 m_MonthNamesMap[_T("mrs")] = 3;
527 m_MonthNamesMap[_T("avr")] = 4;
528 m_MonthNamesMap[_T("juin")] = 6;
529 m_MonthNamesMap[_T("juil")] = 7;
530 m_MonthNamesMap[_T("jui")] = 7;
531 m_MonthNamesMap[_T("ao\xfb")] = 8;
532 m_MonthNamesMap[_T("ao\xfbt")] = 8;
533 m_MonthNamesMap[_T("aout")] = 8;
534 m_MonthNamesMap[_T("d\xe9")_T("c")] = 12;
535 m_MonthNamesMap[_T("dec")] = 12;
536
537 //Italian month names
538 m_MonthNamesMap[_T("gen")] = 1;
539 m_MonthNamesMap[_T("mag")] = 5;
540 m_MonthNamesMap[_T("giu")] = 6;
541 m_MonthNamesMap[_T("lug")] = 7;
542 m_MonthNamesMap[_T("ago")] = 8;
543 m_MonthNamesMap[_T("set")] = 9;
544 m_MonthNamesMap[_T("ott")] = 10;
545 m_MonthNamesMap[_T("dic")] = 12;
546
547 //Spanish month names
548 m_MonthNamesMap[_T("ene")] = 1;
549 m_MonthNamesMap[_T("fbro")] = 2;
550 m_MonthNamesMap[_T("mzo")] = 3;
551 m_MonthNamesMap[_T("ab")] = 4;
552 m_MonthNamesMap[_T("abr")] = 4;
553 m_MonthNamesMap[_T("agto")] = 8;
554 m_MonthNamesMap[_T("sbre")] = 9;
555 m_MonthNamesMap[_T("obre")] = 9;
556 m_MonthNamesMap[_T("nbre")] = 9;
557 m_MonthNamesMap[_T("dbre")] = 9;
558
559 //Polish month names
560 m_MonthNamesMap[_T("sty")] = 1;
561 m_MonthNamesMap[_T("lut")] = 2;
562 m_MonthNamesMap[_T("kwi")] = 4;
563 m_MonthNamesMap[_T("maj")] = 5;
564 m_MonthNamesMap[_T("cze")] = 6;
565 m_MonthNamesMap[_T("lip")] = 7;
566 m_MonthNamesMap[_T("sie")] = 8;
567 m_MonthNamesMap[_T("wrz")] = 9;
568 m_MonthNamesMap[_T("pa\x9f")] = 10;
569 m_MonthNamesMap[_T("lis")] = 11;
570 m_MonthNamesMap[_T("gru")] = 12;
571
572 //Russian month names
573 m_MonthNamesMap[_T("\xff\xed\xe2")] = 1;
574 m_MonthNamesMap[_T("\xf4\xe5\xe2")] = 2;
575 m_MonthNamesMap[_T("\xec\xe0\xf0")] = 3;
576 m_MonthNamesMap[_T("\xe0\xef\xf0")] = 4;
577 m_MonthNamesMap[_T("\xec\xe0\xe9")] = 5;
578 m_MonthNamesMap[_T("\xe8\xfe\xed")] = 6;
579 m_MonthNamesMap[_T("\xe8\xfe\xeb")] = 7;
580 m_MonthNamesMap[_T("\xe0\xe2\xe3")] = 8;
581 m_MonthNamesMap[_T("\xf1\xe5\xed")] = 9;
582 m_MonthNamesMap[_T("\xee\xea\xf2")] = 10;
583 m_MonthNamesMap[_T("\xed\xee\xff")] = 11;
584 m_MonthNamesMap[_T("\xe4\xe5\xea")] = 12;
585
586 //Dutch month names
587 m_MonthNamesMap[_T("mrt")] = 3;
588 m_MonthNamesMap[_T("mei")] = 5;
589
590 //Portuguese month names
591 m_MonthNamesMap[_T("out")] = 10;
592
593 //Finnish month names
594 m_MonthNamesMap[_T("tammi")] = 1;
595 m_MonthNamesMap[_T("helmi")] = 2;
596 m_MonthNamesMap[_T("maalis")] = 3;
597 m_MonthNamesMap[_T("huhti")] = 4;
598 m_MonthNamesMap[_T("touko")] = 5;
599 m_MonthNamesMap[_T("kes\xe4")] = 6;
600 m_MonthNamesMap[_T("hein\xe4")] = 7;
601 m_MonthNamesMap[_T("elo")] = 8;
602 m_MonthNamesMap[_T("syys")] = 9;
603 m_MonthNamesMap[_T("loka")] = 10;
604 m_MonthNamesMap[_T("marras")] = 11;
605 m_MonthNamesMap[_T("joulu")] = 12;
606
607 //Slovenian month names
608 m_MonthNamesMap[_T("avg")] = 8;
609
610 //Icelandic
611#if wxUSE_UNICODE
612 m_MonthNamesMap[_T("ma\x00ed")] = 5;
613 m_MonthNamesMap[_T("j\x00fan")] = 6;
614 m_MonthNamesMap[_T("j\x00fal")] = 7;
615 m_MonthNamesMap[_T("\x00e1g")] = 8;
616 m_MonthNamesMap[_T("n\x00f3v")] = 11;
617#endif
618 m_MonthNamesMap[_T("des")] = 12;
619
620 //Lithuanian
621 m_MonthNamesMap[_T("sau")] = 1;
622 m_MonthNamesMap[_T("vas")] = 2;
623 m_MonthNamesMap[_T("kov")] = 3;
624 m_MonthNamesMap[_T("bal")] = 4;
625 m_MonthNamesMap[_T("geg")] = 5;
626 m_MonthNamesMap[_T("bir")] = 6;
627 m_MonthNamesMap[_T("lie")] = 7;
628 m_MonthNamesMap[_T("rgp")] = 8;
629 m_MonthNamesMap[_T("rgs")] = 9;
630 m_MonthNamesMap[_T("spa")] = 10;
631 m_MonthNamesMap[_T("lap")] = 11;
632 m_MonthNamesMap[_T("grd")] = 12;
633
634 //There are more languages and thus month
635 //names, but as long as nobody reports a
636 //problem, I won't add them, there are way
637 //too many languages
638
639 // Some servers send a combination of month name and number,
640 // Add corresponding numbers to the month names.
641 std::map<wxString, int> combo;
642 for (std::map<wxString, int>::iterator iter = m_MonthNamesMap.begin(); iter != m_MonthNamesMap.end(); ++iter)
643 {
644 // January could be 1 or 0, depends how the server counts
645 combo[wxString::Format(_T("%s%02d"), iter->first.c_str(), iter->second)] = iter->second;
646 combo[wxString::Format(_T("%s%02d"), iter->first.c_str(), iter->second - 1)] = iter->second;
647 if (iter->second < 10)
648 combo[wxString::Format(_T("%s%d"), iter->first.c_str(), iter->second)] = iter->second;
649 else
650 combo[wxString::Format(_T("%s%d"), iter->first.c_str(), iter->second % 10)] = iter->second;
651 if (iter->second <= 10)
652 combo[wxString::Format(_T("%s%d"), iter->first.c_str(), iter->second - 1)] = iter->second;
653 else
654 combo[wxString::Format(_T("%s%d"), iter->first.c_str(), (iter->second - 1) % 10)] = iter->second;
655 }
656 m_MonthNamesMap.insert(combo.begin(), combo.end());
657
658 m_MonthNamesMap[_T("1")] = 1;
659 m_MonthNamesMap[_T("2")] = 2;
660 m_MonthNamesMap[_T("3")] = 3;
661 m_MonthNamesMap[_T("4")] = 4;
662 m_MonthNamesMap[_T("5")] = 5;
663 m_MonthNamesMap[_T("6")] = 6;
664 m_MonthNamesMap[_T("7")] = 7;
665 m_MonthNamesMap[_T("8")] = 8;
666 m_MonthNamesMap[_T("9")] = 9;
667 m_MonthNamesMap[_T("10")] = 10;
668 m_MonthNamesMap[_T("11")] = 11;
669 m_MonthNamesMap[_T("12")] = 12;
670 }
671
672#ifdef LISTDEBUG
673 for (unsigned int i = 0; data[i][0]; ++i)
674 {
675 unsigned int len = (unsigned int)strlen(data[i]);
676 char *pData = new char[len + 3];
677 strcpy(pData, data[i]);
678 strcat(pData, "\r\n");
679 AddData(pData, len + 2);
680 }
681#endif
682}
683
684CDirectoryListingParser::~CDirectoryListingParser()
685{
686 for (std::list<t_list>::iterator iter = m_DataList.begin(); iter != m_DataList.end(); ++iter)
687 delete [] iter->p;
688
689 delete m_prevLine;
690}
691
692bool CDirectoryListingParser::ParseData(bool partial)
693{
694 bool error = false;
695 CLine *pLine = GetLine(partial, error);
696 while (pLine)
697 {
698 bool res = ParseLine(pLine, m_server.GetType(), false);
699 if (!res)
700 {
701 if (m_prevLine)
702 {
703 CLine* pConcatenatedLine = m_prevLine->Concat(pLine);
704 bool res = ParseLine(pConcatenatedLine, m_server.GetType(), true);
705 delete pConcatenatedLine;
706 delete m_prevLine;
707
708 if (res)
709 {
710 delete pLine;
711 m_prevLine = 0;
712 }
713 else
714 m_prevLine = pLine;
715 }
716 else
717 m_prevLine = pLine;
718 }
719 else
720 {
721 delete m_prevLine;
722 m_prevLine = 0;
723 delete pLine;
724 }
725 pLine = GetLine(partial, error);
726 };
727
728 return !error;
729}
730
731CDirectoryListing CDirectoryListingParser::Parse(const CServerPath &path)
732{
733 CDirectoryListing listing;
734 listing.path = path;
735 listing.m_firstListTime = CTimeEx::Now();
736
737 if (!ParseData(false))
738 {
739 listing.m_failed = true;
740 return listing;
741 }
742
743 if (!m_fileList.empty())
744 {
745 wxASSERT(m_entryList.empty());
746
747 listing.SetCount(m_fileList.size());
748 unsigned int i = 0;
749 for (std::list<wxString>::const_iterator iter = m_fileList.begin(); iter != m_fileList.end(); ++iter, ++i)
750 {
751 CDirentry entry;
752 entry.name = *iter;
753 entry.flags = 0;
754 entry.size = -1;
755 listing[i] = entry;
756 }
757 }
758 else
759 {
760 listing.Assign(m_entryList);
761 }
762
763 return listing;
764}
765
766bool CDirectoryListingParser::ParseLine(CLine *pLine, const enum ServerType serverType, bool concatenated)
767{
768 CDirentry entry;
769 bool res;
770 int ires;
771
772 if (serverType == ZVM)
773 {
774 res = ParseAsZVM(pLine, entry);
775 if (res)
776 goto done;
777 }
778 else if (serverType == HPNONSTOP)
779 {
780 res = ParseAsHPNonstop(pLine, entry);
781 if (res)
782 goto done;
783 }
784 /*else if (serverType == OPENVMS)
785 {
786 res = ParseAsOpenVms(pLine, entry);
787 if (res)
788 goto done;
789 }*/
790
791
792 ires = ParseAsMlsd(pLine, entry);
793 if (ires == 1)
794 goto done;
795 else if (ires == 2)
796 goto skip;
797 res = ParseAsUnix(pLine, entry, true); // Common 'ls -l'
798 if (res)
799 goto done;
800 res = ParseAsDos(pLine, entry);
801 if (res)
802 goto done;
803 res = ParseAsEplf(pLine, entry);
804 if (res)
805 goto done;
806 res = ParseAsVms(pLine, entry);
807 if (res)
808 goto done;
809 res = ParseOther(pLine, entry);
810 if (res)
811 goto done;
812 res = ParseAsIbm(pLine, entry);
813 if (res)
814 goto done;
815 res = ParseAsWfFtp(pLine, entry);
816 if (res)
817 goto done;
818 res = ParseAsIBM_MVS(pLine, entry);
819 if (res)
820 goto done;
821 res = ParseAsIBM_MVS_PDS(pLine, entry);
822 if (res)
823 goto done;
824 res = ParseAsOS9(pLine, entry);
825 if (res)
826 goto done;
827#ifndef LISTDEBUG_MVS
828 if (serverType == MVS)
829#endif //LISTDEBUG_MVS
830 {
831 res = ParseAsIBM_MVS_Migrated(pLine, entry);
832 if (res)
833 goto done;
834 res = ParseAsIBM_MVS_PDS2(pLine, entry);
835 if (res)
836 goto done;
837 res = ParseAsIBM_MVS_Tape(pLine, entry);
838 if (res)
839 goto done;
840 }
841 res = ParseAsUnix(pLine, entry, false); // 'ls -l' but without the date/time
842 if (res)
843 goto done;
844
845 // Some servers just send a list of filenames. If a line could not be parsed,
846 // check if it's a filename. If that's the case, store it for later, else clear
847 // list of stored files.
848 // If parsing finishes and no entries could be parsed and none of the lines
849 // contained a space, assume it's a raw filelisting.
850
851 if (!concatenated)
852 {
853 CToken token;
854 if (!pLine->GetToken(0, token, true) || token.Find(' ') != -1)
855 {
856 m_maybeMultilineVms = false;
857 m_fileList.clear();
858 m_fileListOnly = false;
859 }
860 else
861 {
862 m_maybeMultilineVms = token.Find(';') != -1;
863 if (m_fileListOnly)
864 m_fileList.push_back(token.GetString());
865 }
866 }
867 else
868 m_maybeMultilineVms = false;
869
870 return false;
871done:
872
873 m_maybeMultilineVms = false;
874 m_fileList.clear();
875 m_fileListOnly = false;
876
877 // Don't add . or ..
878 if (entry.name == _T(".") || entry.name == _T(".."))
879 return true;
880
881 if (serverType == VMS && entry.is_dir())
882 {
883 // Trim version information from directories
884 int pos = entry.name.Find(';', true);
885 if (pos > 0)
886 entry.name = entry.name.Left(pos);
887 }
888
889 {
890 int offset = m_server.GetTimezoneOffset();
891 if (offset && entry.has_time())
892 {
893 // Apply timezone offset
894 wxTimeSpan span(0, offset, 0, 0);
895 entry.time.Add(span);
896 }
897 }
898
899 m_entryList.push_back(entry);
900
901skip:
902 m_maybeMultilineVms = false;
903 m_fileList.clear();
904 m_fileListOnly = false;
905
906 return true;
907}
908
909bool CDirectoryListingParser::ParseAsUnix(CLine *pLine, CDirentry &entry, bool expect_date)
910{
911 int index = 0;
912 CToken token;
913 if (!pLine->GetToken(index, token))
914 return false;
915
916 wxChar chr = token[0];
917 if (chr != 'b' &&
918 chr != 'c' &&
919 chr != 'd' &&
920 chr != 'l' &&
921 chr != 'p' &&
922 chr != 's' &&
923 chr != '-')
924 return false;
925
926 entry.permissions = token.GetString();
927
928 entry.flags = 0;
929
930 if (chr == 'd' || chr == 'l')
931 entry.flags |= CDirentry::flag_dir;
932
933 if (chr == 'l')
934 entry.flags |= CDirentry::flag_link;
935
936 // Check for netware servers, which split the permissions into two parts
937 bool netware = false;
938 if (token.GetLength() == 1)
939 {
940 if (!pLine->GetToken(++index, token))
941 return false;
942 entry.permissions += _T(" ") + token.GetString();
943 netware = true;
944 }
945
946 int numOwnerGroup = 3;
947 if (!netware)
948 {
949 // Filter out link count, we don't need it
950 if (!pLine->GetToken(++index, token))
951 return false;
952
953 if (!token.IsNumeric())
954 --index;
955 }
956
957 // Repeat until numOwnerGroup is 0 since not all servers send every possible field
958 int startindex = index;
959 do
960 {
961 // Reset index
962 index = startindex;
963
964 entry.ownerGroup.clear();
965 for (int i = 0; i < numOwnerGroup; ++i)
966 {
967 if (!pLine->GetToken(++index, token))
968 return false;
969 if (i)
970 entry.ownerGroup += _T(" ");
971 entry.ownerGroup += token.GetString();
972 }
973
974 if (!pLine->GetToken(++index, token))
975 return false;
976
977 // Check for concatenated groupname and size fields
978 if (!ParseComplexFileSize(token, entry.size))
979 {
980 if (!token.IsRightNumeric())
981 continue;
982 entry.size = token.GetNumber();
983 }
984
985 // Append missing group to ownerGroup
986 if (!token.IsNumeric() && token.IsRightNumeric())
987 {
988 if (!entry.ownerGroup.IsEmpty())
989 entry.ownerGroup += _T(" ");
990 entry.ownerGroup += token.GetString(1);
991 }
992
993 if (expect_date)
994 {
995 entry.flags &= ~CDirentry::flag_timestamp_mask;
996 if (!ParseUnixDateTime(pLine, index, entry))
997 continue;
998 }
999
1000 // Get the filename
1001 if (!pLine->GetToken(++index, token, 1))
1002 continue;
1003
1004 entry.name = token.GetString();
1005
1006 // Filter out cpecial chars at the end of the filenames
1007 chr = token[token.GetLength() - 1];
1008 if (chr == '/' ||
1009 chr == '|' ||
1010 chr == '*')
1011 entry.name.RemoveLast();
1012
1013 if (entry.is_link())
1014 {
1015 int pos;
1016 if ((pos = entry.name.Find(_T(" -> "))) != -1)
1017 {
1018 entry.target = entry.name.Mid(pos + 4);
1019 entry.name = entry.name.Left(pos);
1020 }
1021 }
1022
1023 if (entry.has_time())
1024 entry.time.Add(m_timezoneOffset);
1025
1026 return true;
1027 }
1028 while (numOwnerGroup--);
1029
1030 return false;
1031}
1032
1033bool CDirectoryListingParser::ParseUnixDateTime(CLine *pLine, int &index, CDirentry &entry)
1034{
1035 bool mayHaveTime = true;
1036 bool bHasYearAndTime = false;
1037 bool hasTime = false;
1038
1039 CToken token;
1040
1041 // Get the month date field
1042 CToken dateMonth;
1043 if (!pLine->GetToken(++index, token))
1044 return false;
1045
1046 int year = 0;
1047 int month = 0;
1048 int day = 0;
1049 long hour = 0;
1050 long minute = 0;
1051
1052 // Some servers use the following date formats:
1053 // 26-05 2002, 2002-10-14, 01-jun-99 or 2004.07.15
1054 // slashes instead of dashes are also possible
1055 int pos = token.Find(_T("-/."));
1056 if (pos != -1)
1057 {
1058 int pos2 = token.Find(_T("-/."), pos + 1);
1059 if (pos2 == -1)
1060 {
1061 if (token[pos] != '.')
1062 {
1063 // something like 26-05 2002
1064 day = token.GetNumber(pos + 1, token.GetLength() - pos - 1).GetLo();
1065 if (day < 1 || day > 31)
1066 return false;
1067 dateMonth = CToken(token.GetToken(), pos);
1068 }
1069 else
1070 dateMonth = token;
1071 }
1072 else if (token[pos] != token[pos2])
1073 return false;
1074 else
1075 {
1076 if (!ParseShortDate(token, entry))
1077 return false;
1078
1079 if (token[pos] == '.')
1080 return true;
1081
1082 year = entry.time.GetYear();
1083 month = entry.time.GetMonth() - wxDateTime::Jan + 1;
1084 day = entry.time.GetDay();
1085 }
1086 }
1087 else if (token.IsNumeric())
1088 {
1089 if (token.GetNumber() > 1000 && token.GetNumber() < 10000)
1090 {
1091 // Two possible variants:
1092 // 1) 2005 3 13
1093 // 2) 2005 13 3
1094 // assume first one.
1095 year = token.GetNumber().GetLo();
1096 if (!pLine->GetToken(++index, dateMonth))
1097 return false;
1098 mayHaveTime = false;
1099 }
1100 else
1101 dateMonth = token;
1102 }
1103 else
1104 {
1105 if (token.IsLeftNumeric() && (unsigned int)token[token.GetLength() - 1] > 127 &&
1106 token.GetNumber() > 1000)
1107 {
1108 if (token.GetNumber() > 10000)
1109 return false;
1110
1111 // Asian date format: 2005xxx 5xx 20xxx with some non-ascii characters following
1112 year = token.GetNumber().GetLo();
1113 if (!pLine->GetToken(++index, dateMonth))
1114 return false;
1115 mayHaveTime = false;
1116 }
1117 else
1118 dateMonth = token;
1119 }
1120
1121 if (!day)
1122 {
1123 // Get day field
1124 if (!pLine->GetToken(++index, token))
1125 return false;
1126
1127 int dateDay;
1128
1129 // Check for non-numeric day
1130 if (!token.IsNumeric() && !token.IsLeftNumeric())
1131 {
1132 int offset = 0;
1133 if (dateMonth.GetString().Right(1) == _T("."))
1134 ++offset;
1135 if (!dateMonth.IsNumeric(0, dateMonth.GetLength() - offset))
1136 return false;
1137 dateDay = dateMonth.GetNumber(0, dateMonth.GetLength() - offset).GetLo();
1138 dateMonth = token;
1139 }
1140 else
1141 {
1142 dateDay = token.GetNumber().GetLo();
1143 if (token[token.GetLength() - 1] == ',')
1144 bHasYearAndTime = true;
1145 }
1146
1147 if (dateDay < 1 || dateDay > 31)
1148 return false;
1149 day = dateDay;
1150 }
1151
1152 if (!month)
1153 {
1154 wxString strMonth = dateMonth.GetString();
1155 if (dateMonth.IsLeftNumeric() && (unsigned int)strMonth[strMonth.Length() - 1] > 127)
1156 {
1157 // Most likely an Asian server sending some unknown language specific
1158 // suffix at the end of the monthname. Filter it out.
1159 int i;
1160 for (i = strMonth.Length() - 1; i > 0; --i)
1161 {
1162 if (strMonth[i] >= '0' && strMonth[i] <= '9')
1163 break;
1164 }
1165 strMonth = strMonth.Left(i + 1);
1166 }
1167 // Check month name
1168 while (strMonth.Right(1) == _T(",") || strMonth.Right(1) == _T("."))
1169 strMonth.RemoveLast();
1170 if (!GetMonthFromName(strMonth, month))
1171 return false;
1172 }
1173
1174 // Get time/year field
1175 if (!pLine->GetToken(++index, token))
1176 return false;
1177
1178 pos = token.Find(_T(":.-"));
1179 if (pos != -1 && mayHaveTime)
1180 {
1181 // token is a time
1182 if (!pos || static_cast<size_t>(pos) == (token.GetLength() - 1))
1183 return false;
1184
1185 wxString str = token.GetString();
1186 if (!str.Left(pos).ToLong(&hour))
1187 return false;
1188 if (!str.Mid(pos + 1).ToLong(&minute))
1189 return false;
1190
1191 if (hour < 0 || hour > 23)
1192 return false;
1193 if (minute < 0 || minute > 59)
1194 return false;
1195
1196 hasTime = true;
1197
1198 // Some servers use times only for files newer than 6 months
1199 if (!year)
1200 {
1201 year = wxDateTime::Now().GetYear();
1202 int currentDayOfYear = wxDateTime::Now().GetDay() + 31 * (wxDateTime::Now().GetMonth() - wxDateTime::Jan);
1203 int fileDayOfYear = (month - 1) * 31 + day;
1204
1205 // We have to compare with an offset of one. In the worst case,
1206 // the server's timezone might be up to 24 hours ahead of the
1207 // client.
1208 // Problem: Servers which do send the time but not the year even
1209 // one day away from getting 1 year old. This is far more uncommon
1210 // however.
1211 if ((currentDayOfYear + 1) < fileDayOfYear)
1212 year -= 1;
1213 }
1214 }
1215 else if (!year)
1216 {
1217 // token is a year
1218 if (!token.IsNumeric() && !token.IsLeftNumeric())
1219 return false;
1220
1221 year = token.GetNumber().GetLo();
1222
1223 if (year > 3000)
1224 return false;
1225 if (year < 1000)
1226 year += 1900;
1227
1228 if (bHasYearAndTime)
1229 {
1230 if (!pLine->GetToken(++index, token))
1231 return false;
1232
1233 if (token.Find(':') == 2 && token.GetLength() == 5 && token.IsLeftNumeric() && token.IsRightNumeric())
1234 {
1235 int pos = token.Find(':');
1236 // token is a time
1237 if (!pos || static_cast<size_t>(pos) == (token.GetLength() - 1))
1238 return false;
1239
1240 wxString str = token.GetString();
1241
1242 if (!str.Left(pos).ToLong(&hour))
1243 return false;
1244 if (!str.Mid(pos + 1).ToLong(&minute))
1245 return false;
1246
1247 if (hour < 0 || hour > 23)
1248 return false;
1249 if (minute < 0 || minute > 59)
1250 return false;
1251
1252 hasTime = true;
1253 }
1254 else
1255 --index;
1256 }
1257 }
1258 else
1259 --index;
1260
1261 entry.time = wxDateTime();
1262 if (!VerifySetDate(entry.time, year, (wxDateTime::Month)(wxDateTime::Jan + month - 1), day, hour, minute))
1263 return false;
1264
1265 entry.flags |= CDirentry::flag_timestamp_date;
1266 if (hasTime)
1267 entry.flags |= CDirentry::flag_timestamp_time;
1268
1269 return true;
1270}
1271
1272bool CDirectoryListingParser::ParseShortDate(CToken &token, CDirentry &entry, bool saneFieldOrder /*=false*/)
1273{
1274 if (token.GetLength() < 1)
1275 return false;
1276
1277 bool gotYear = false;
1278 bool gotMonth = false;
1279 bool gotDay = false;
1280 bool gotMonthName = false;
1281
1282 int year = 0;
1283 int month = 0;
1284 int day = 0;
1285
1286 int value = 0;
1287
1288 int pos = token.Find(_T("-./"));
1289 if (pos < 1)
1290 return false;
1291
1292 if (!token.IsNumeric(0, pos))
1293 {
1294 // Seems to be monthname-dd-yy
1295
1296 // Check month name
1297 wxString dateMonth = token.GetString().Mid(0, pos);
1298 if (!GetMonthFromName(dateMonth, month))
1299 return false;
1300 gotMonth = true;
1301 gotMonthName = true;
1302 }
1303 else if (pos == 4)
1304 {
1305 // Seems to be yyyy-mm-dd
1306 year = token.GetNumber(0, pos).GetLo();
1307 if (year < 1900 || year > 3000)
1308 return false;
1309 gotYear = true;
1310 }
1311 else if (pos <= 2)
1312 {
1313 wxLongLong value = token.GetNumber(0, pos);
1314 if (token[pos] == '.')
1315 {
1316 // Maybe dd.mm.yyyy
1317 if (value < 1 || value > 31)
1318 return false;
1319 day = value.GetLo();
1320 gotDay = true;
1321 }
1322 else
1323 {
1324 if (saneFieldOrder)
1325 {
1326 year = value.GetLo();
1327 if (year < 50)
1328 year += 2000;
1329 else
1330 year += 1900;
1331 gotYear = true;
1332 }
1333 else
1334 {
1335 // Detect mm-dd-yyyy or mm/dd/yyyy and
1336 // dd-mm-yyyy or dd/mm/yyyy
1337 if (value < 1)
1338 return false;
1339 if (value > 12)
1340 {
1341 if (value > 31)
1342 return false;
1343
1344 day = value.GetLo();
1345 gotDay = true;
1346 }
1347 else
1348 {
1349 month = value.GetLo();
1350 gotMonth = true;
1351 }
1352 }
1353 }
1354 }
1355 else
1356 return false;
1357
1358 int pos2 = token.Find(_T("-./"), pos + 1);
1359 if (pos2 == -1 || (pos2 - pos) == 1)
1360 return false;
1361 if (static_cast<size_t>(pos2) == (token.GetLength() - 1))
1362 return false;
1363
1364 // If we already got the month and the second field is not numeric,
1365 // change old month into day and use new token as month
1366 if (!token.IsNumeric(pos + 1, pos2 - pos - 1) && gotMonth)
1367 {
1368 if (gotMonthName)
1369 return false;
1370
1371 if (gotDay)
1372 return false;
1373
1374 gotDay = true;
1375 gotMonth = false;
1376 day = month;
1377 }
1378
1379 if (gotYear || gotDay)
1380 {
1381 // Month field in yyyy-mm-dd or dd-mm-yyyy
1382 // Check month name
1383 wxString dateMonth = token.GetString().Mid(pos + 1, pos2 - pos - 1);
1384 if (!GetMonthFromName(dateMonth, month))
1385 return false;
1386 gotMonth = true;
1387 }
1388 else
1389 {
1390 wxLongLong value = token.GetNumber(pos + 1, pos2 - pos - 1);
1391 // Day field in mm-dd-yyyy
1392 if (value < 1 || value > 31)
1393 return false;
1394 day = value.GetLo();
1395 gotDay = true;
1396 }
1397
1398 value = token.GetNumber(pos2 + 1, token.GetLength() - pos2 - 1).GetLo();
1399 if (gotYear)
1400 {
1401 // Day field in yyy-mm-dd
1402 if (!value || value > 31)
1403 return false;
1404 day = value;
1405 gotDay = true;
1406 }
1407 else
1408 {
1409 if (value < 0)
1410 return false;
1411
1412 if (value < 50)
1413 value += 2000;
1414 else if (value < 1000)
1415 value += 1900;
1416 year = value;
1417
1418 gotYear = true;
1419 }
1420
1421 if (!gotMonth || !gotDay || !gotYear)
1422 return false;
1423
1424 entry.time = wxDateTime();
1425 if (!VerifySetDate(entry.time, year, (wxDateTime::Month)(wxDateTime::Jan + month - 1), day))
1426 return false;
1427 entry.flags |= CDirentry::flag_timestamp_date;
1428
1429 return true;
1430}
1431
1432bool CDirectoryListingParser::ParseAsDos(CLine *pLine, CDirentry &entry)
1433{
1434 int index = 0;
1435 CToken token;
1436
1437 // Get first token, has to be a valid date
1438 if (!pLine->GetToken(index, token))
1439 return false;
1440
1441 entry.flags = 0;
1442
1443 if (!ParseShortDate(token, entry))
1444 return false;
1445
1446 // Extract time
1447 if (!pLine->GetToken(++index, token))
1448 return false;
1449
1450 if (!ParseTime(token, entry))
1451 return false;
1452
1453 // If next token is <DIR>, entry is a directory
1454 // else, it should be the filesize.
1455 if (!pLine->GetToken(++index, token))
1456 return false;
1457
1458 if (token.GetString() == _T("<DIR>"))
1459 {
1460 entry.flags |= CDirentry::flag_dir;
1461 entry.size = -1;
1462 }
1463 else if (token.IsNumeric() || token.IsLeftNumeric())
1464 {
1465 // Convert size, filter out separators
1466 wxLongLong size = 0;
1467 int len = token.GetLength();
1468 for (int i = 0; i < len; ++i)
1469 {
1470 char chr = token[i];
1471 if (chr == ',' || chr == '.')
1472 continue;
1473 if (chr < '0' || chr > '9')
1474 return false;
1475
1476 size *= 10;
1477 size += chr - '0';
1478 }
1479 entry.size = size;
1480 }
1481 else
1482 return false;
1483
1484 // Extract filename
1485 if (!pLine->GetToken(++index, token, true))
1486 return false;
1487 entry.name = token.GetString();
1488
1489 entry.target = _T("");
1490 entry.ownerGroup = _T("");
1491 entry.permissions = _T("");
1492
1493 if (entry.has_time())
1494 entry.time.Add(m_timezoneOffset);
1495
1496 return true;
1497}
1498
1499bool CDirectoryListingParser::ParseTime(CToken &token, CDirentry &entry)
1500{
1501 if (!entry.has_date())
1502 return false;
1503
1504 int pos = token.Find(':');
1505 if (pos < 1 || static_cast<unsigned int>(pos) >= (token.GetLength() - 1))
1506 return false;
1507
1508 wxLongLong hour = token.GetNumber(0, pos);
1509 if (hour < 0 || hour > 23)
1510 return false;
1511
1512 // See if we got seconds
1513 int pos2 = token.Find(':', pos + 1);
1514 int len;
1515 if (pos2 == -1)
1516 len = -1;
1517 else
1518 len = pos2 - pos - 1;
1519
1520 if (!len)
1521 return false;
1522
1523 wxLongLong minute = token.GetNumber(pos + 1, len);
1524 if (minute < 0 || minute > 59)
1525 return false;
1526
1527 wxLongLong seconds;
1528 bool hasSeconds = false;
1529 if (pos2 == -1)
1530 seconds = 0;
1531 else
1532 {
1533 // Parse seconds
1534 seconds = token.GetNumber(pos2 + 1, -1);
1535 if (seconds < 0 || seconds > 59)
1536 return false;
1537 hasSeconds = true;
1538 }
1539
1540 // Convert to 24h format
1541 if (!token.IsRightNumeric())
1542 {
1543 if (token[token.GetLength() - 2] == 'P')
1544 {
1545 if (hour < 12)
1546 hour += 12;
1547 }
1548 else
1549 if (hour == 12)
1550 hour = 0;
1551 }
1552
1553 wxTimeSpan span(hour.GetLo(), minute.GetLo(), seconds.GetLo());
1554 entry.time.Add(span);
1555
1556 entry.flags |= CDirentry::flag_timestamp_time;
1557 if (hasSeconds)
1558 entry.flags |= CDirentry::flag_timestamp_seconds;
1559
1560 return true;
1561}
1562
1563bool CDirectoryListingParser::ParseAsEplf(CLine *pLine, CDirentry &entry)
1564{
1565 CToken token;
1566 if (!pLine->GetToken(0, token, true))
1567 return false;
1568
1569 if (token[0] != '+')
1570 return false;
1571
1572 int pos = token.Find('\t');
1573 if (pos == -1 || static_cast<size_t>(pos) == (token.GetLength() - 1))
1574 return false;
1575
1576 entry.name = token.GetString().Mid(pos + 1);
1577
1578 entry.flags = 0;
1579 entry.ownerGroup = _T("");
1580 entry.permissions = _T("");
1581 entry.size = -1;
1582
1583 int fact = 1;
1584 while (fact < pos)
1585 {
1586 int separator = token.Find(',', fact);
1587 int len;
1588 if (separator == -1)
1589 len = pos - fact;
1590 else
1591 len = separator - fact;
1592
1593 if (!len)
1594 {
1595 ++fact;
1596 continue;
1597 }
1598
1599 char type = token[fact];
1600
1601 if (type == '/')
1602 entry.flags |= CDirentry::flag_dir;
1603 else if (type == 's')
1604 entry.size = token.GetNumber(fact + 1, len - 1);
1605 else if (type == 'm')
1606 {
1607 wxLongLong number = token.GetNumber(fact + 1, len - 1);
1608 if (number < 0)
1609 return false;
1610 entry.time = wxDateTime((time_t)number.GetValue());
1611
1612 entry.flags |= CDirentry::flag_timestamp_date | CDirentry::flag_timestamp_time | CDirentry::flag_timestamp_seconds;
1613 }
1614 else if (type == 'u' && len > 2 && token[fact + 1] == 'p')
1615 entry.permissions = token.GetString().Mid(fact + 2, len - 2);
1616
1617 fact += len + 1;
1618 }
1619
1620 return true;
1621}
1622
1623wxString Unescape(const wxString& str, wxChar escape)
1624{
1625 wxString res;
1626 for (unsigned int i = 0; i < str.Len(); ++i)
1627 {
1628 wxChar c = str[i];
1629 if (c == escape)
1630 {
1631 c = str[++i];
1632 if (!c)
1633 break;
1634 }
1635 res += c;
1636 }
1637
1638 return res;
1639}
1640
1641bool CDirectoryListingParser::ParseAsVms(CLine *pLine, CDirentry &entry)
1642{
1643 CToken token;
1644 int index = 0;
1645
1646 if (!pLine->GetToken(index, token))
1647 return false;
1648
1649 int pos = token.Find(';');
1650 if (pos == -1)
1651 return false;
1652
1653 entry.flags = 0;
1654
1655 if (pos > 4 && token.GetString().Mid(pos - 4, 4) == _T(".DIR"))
1656 {
1657 entry.flags |= CDirentry::flag_dir;
1658 if (token.GetString().Mid(pos) == _T(";1"))
1659 entry.name = token.GetString().Left(pos - 4);
1660 else
1661 entry.name = token.GetString().Left(pos - 4) + token.GetString().Mid(pos);
1662 }
1663 else //files
1664 {
1665 if (COptions::Get()->GetOptionVal(OPTION_STRIP_VMS_REVISION))
1666 entry.name = StripVMSRevision(token.GetString());
1667 else
1668 entry.name = token.GetString();
1669 }
1670 // Some VMS servers escape special characters like additional dots with ^
1671 entry.name = Unescape(entry.name, '^');
1672
1673 if (!pLine->GetToken(++index, token))
1674 return false;
1675
1676 entry.ownerGroup = _T("");
1677
1678 // This field can either be the filesize, a username (at least that's what I think) enclosed in [] or a date.
1679 if (!token.IsNumeric() && !token.IsLeftNumeric())
1680 {
1681 // Must be username
1682
1683 const int len = token.GetLength();
1684 if (len < 3 || token[0] != '[' || token[len - 1] != ']')
1685 return false;
1686 entry.ownerGroup = token.GetString().Mid(1, len - 2);
1687
1688 if (!pLine->GetToken(++index, token))
1689 return false;
1690 if (!token.IsNumeric() && !token.IsLeftNumeric())
1691 return false;
1692 }
1693
1694 // Current token is either size or date
1695 bool gotSize = false;
1696 pos = token.Find('/');
1697
1698 if (!pos)
1699 return false;
1700
1701 if (token.IsNumeric() || (pos != -1 && token.Find('/', pos + 1) == -1))
1702 {
1703 // Definitely size
1704
1705 CToken sizeToken;
1706 if (pos == -1)
1707 sizeToken = token;
1708 else
1709 sizeToken = CToken(token.GetToken(), pos);
1710 if (!ParseComplexFileSize(sizeToken, entry.size, 512))
1711 return false;
1712 gotSize = true;
1713
1714 if (!pLine->GetToken(++index, token))
1715 return false;
1716 }
1717 else if (pos == -1 && token.IsLeftNumeric())
1718 {
1719 // Perhaps size
1720 CToken sizeToken;
1721 if (pos == -1)
1722 sizeToken = token;
1723 else
1724 sizeToken = CToken(token.GetToken(), pos);
1725 if (ParseComplexFileSize(sizeToken, entry.size, 512))
1726 {
1727 gotSize = true;
1728
1729 if (!pLine->GetToken(++index, token))
1730 return false;
1731 }
1732 }
1733
1734 // Get date
1735 if (!ParseShortDate(token, entry))
1736 return false;
1737
1738 // Get time
1739 if (!pLine->GetToken(++index, token))
1740 return true;
1741
1742 if (!ParseTime(token, entry))
1743 {
1744 int len = token.GetLength();
1745 if (token[0] == '[' && token[len - 1] != ']')
1746 return false;
1747 if (token[0] == '(' && token[len - 1] != ')')
1748 return false;
1749 if (token[0] != '[' && token[len - 1] == ']')
1750 return false;
1751 if (token[0] != '(' && token[len - 1] == ')')
1752 return false;
1753 --index;
1754 }
1755
1756 if (!gotSize)
1757 {
1758 // Get size
1759 if (!pLine->GetToken(++index, token))
1760 return false;
1761
1762 if (!token.IsNumeric() && !token.IsLeftNumeric())
1763 return false;
1764
1765 int pos = token.Find('/');
1766 if (!pos)
1767 return false;
1768
1769 CToken sizeToken;
1770 if (pos == -1)
1771 sizeToken = token;
1772 else
1773 sizeToken = CToken(token.GetToken(), pos);
1774 if (!ParseComplexFileSize(sizeToken, entry.size, 512))
1775 return false;
1776 }
1777
1778 // Owner / group and permissions
1779 entry.permissions = _T("");
1780 while (pLine->GetToken(++index, token))
1781 {
1782 const int len = token.GetLength();
1783 if (len > 2 && token[0] == '(' && token[len - 1] == ')')
1784 {
1785 if (!entry.permissions.IsEmpty())
1786 entry.permissions += _T(" ");
1787 entry.permissions += token.GetString().Mid(1, len - 2);
1788 }
1789 else if (len > 2 && token[0] == '[' && token[len - 1] == ']')
1790 {
1791 if (!entry.ownerGroup.IsEmpty())
1792 entry.ownerGroup += _T(" ");
1793 entry.ownerGroup += token.GetString().Mid(1, len - 2);
1794 }
1795 else
1796 {
1797 if (!entry.permissions.IsEmpty())
1798 entry.permissions += _T(" ");
1799 entry.ownerGroup += token.GetString();
1800 }
1801 }
1802
1803 if (entry.has_time())
1804 entry.time.Add(m_timezoneOffset);
1805
1806 return true;
1807}
1808
1809bool CDirectoryListingParser::ParseAsIbm(CLine *pLine, CDirentry &entry)
1810{
1811 int index = 0;
1812 CToken token;
1813
1814 // Get owner
1815 if (!pLine->GetToken(index, token))
1816 return false;
1817
1818 entry.ownerGroup = token.GetString();
1819
1820 // Get size
1821 if (!pLine->GetToken(++index, token))
1822 return false;
1823
1824 if (!token.IsNumeric())
1825 return false;
1826
1827 entry.size = token.GetNumber();
1828
1829 // Get date
1830 if (!pLine->GetToken(++index, token))
1831 return false;
1832
1833 entry.flags = 0;
1834
1835 if (!ParseShortDate(token, entry))
1836 return false;
1837
1838 // Get time
1839 if (!pLine->GetToken(++index, token))
1840 return false;
1841
1842 if (!ParseTime(token, entry))
1843 return false;
1844
1845 // Get filename
1846 if (!pLine->GetToken(index + 2, token, 1))
1847 return false;
1848
1849 entry.name = token.GetString();
1850 if (token[token.GetLength() - 1] == '/')
1851 {
1852 entry.name.RemoveLast();
1853 entry.flags |= CDirentry::flag_dir;
1854 }
1855
1856 if (entry.has_time())
1857 entry.time.Add(m_timezoneOffset);
1858
1859 return true;
1860}
1861
1862bool CDirectoryListingParser::ParseOther(CLine *pLine, CDirentry &entry)
1863{
1864 int index = 0;
1865 CToken firstToken;
1866
1867 if (!pLine->GetToken(index, firstToken))
1868 return false;
1869
1870 if (!firstToken.IsNumeric())
1871 return false;
1872
1873 // Possible formats: Numerical unix, VShell or OS/2
1874
1875 CToken token;
1876 if (!pLine->GetToken(++index, token))
1877 return false;
1878
1879 entry.flags = 0;
1880
1881 // If token is a number, than it's the numerical Unix style format,
1882 // else it's the VShell, OS/2 or nortel.VxWorks format
1883 if (token.IsNumeric())
1884 {
1885 entry.permissions = firstToken.GetString();
1886 if (firstToken.GetLength() >= 2 && firstToken[1] == '4')
1887 entry.flags |= CDirentry::flag_dir;
1888
1889 entry.ownerGroup += token.GetString();
1890
1891 if (!pLine->GetToken(++index, token))
1892 return false;
1893
1894 entry.ownerGroup += _T(" ") + token.GetString();
1895
1896 // Get size
1897 if (!pLine->GetToken(++index, token))
1898 return false;
1899
1900 if (!token.IsNumeric())
1901 return false;
1902
1903 entry.size = token.GetNumber();
1904
1905 // Get date/time
1906 if (!pLine->GetToken(++index, token))
1907 return false;
1908
1909 wxLongLong number = token.GetNumber();
1910 if (number < 0)
1911 return false;
1912 entry.time = wxDateTime((time_t)number.GetValue());
1913
1914 entry.flags |= CDirentry::flag_timestamp_date | CDirentry::flag_timestamp_time | CDirentry::flag_timestamp_seconds;
1915
1916 // Get filename
1917 if (!pLine->GetToken(++index, token, true))
1918 return false;
1919
1920 entry.name = token.GetString();
1921
1922 entry.target = _T("");
1923 }
1924 else
1925 {
1926 // Possible conflict with multiline VMS listings
1927 if (m_maybeMultilineVms)
1928 return false;
1929
1930 // VShell, OS/2 or nortel.VxWorks style format
1931 entry.size = firstToken.GetNumber();
1932
1933 // Get date
1934 wxString dateMonth = token.GetString();
1935 int month = 0;
1936 if (!GetMonthFromName(dateMonth, month))
1937 {
1938 // OS/2 or nortel.VxWorks
1939 int skippedCount = 0;
1940 do
1941 {
1942 if (token.GetString() == _T("DIR"))
1943 entry.flags |= CDirentry::flag_dir;
1944 else if (token.Find(_T("-/.")) != -1)
1945 break;
1946
1947 ++skippedCount;
1948
1949 if (!pLine->GetToken(++index, token))
1950 return false;
1951 } while (true);
1952
1953 if (!ParseShortDate(token, entry))
1954 return false;
1955
1956 // Get time
1957 if (!pLine->GetToken(++index, token))
1958 return false;
1959
1960 if (!ParseTime(token, entry))
1961 return false;
1962
1963 // Get filename
1964 if (!pLine->GetToken(++index, token, true))
1965 return false;
1966
1967 entry.name = token.GetString();
1968 wxString type = entry.name.Right(5);
1969 MakeLowerAscii(type);
1970 if (!skippedCount && type == _T("<dir>"))
1971 {
1972 entry.flags |= CDirentry::flag_dir;
1973 entry.name = entry.name.Left(entry.name.Length() - 5);
1974 while (entry.name.Last() == ' ')
1975 entry.name.RemoveLast();
1976 }
1977 }
1978 else
1979 {
1980 // Get day
1981 if (!pLine->GetToken(++index, token))
1982 return false;
1983
1984 if (!token.IsNumeric() && !token.IsLeftNumeric())
1985 return false;
1986
1987 wxLongLong day = token.GetNumber();
1988 if (day < 0 || day > 31)
1989 return false;
1990
1991 // Get Year
1992 if (!pLine->GetToken(++index, token))
1993 return false;
1994
1995 if (!token.IsNumeric())
1996 return false;
1997
1998 wxLongLong year = token.GetNumber();
1999 if (year < 50)
2000 year += 2000;
2001 else if (year < 1000)
2002 year += 1900;
2003
2004 entry.time = wxDateTime();
2005 if (!VerifySetDate(entry.time, year.GetLo(), (wxDateTime::Month)(month - 1), day.GetLo()))
2006 return false;
2007
2008 entry.flags |= CDirentry::flag_timestamp_date;
2009
2010 // Get time
2011 if (!pLine->GetToken(++index, token))
2012 return false;
2013
2014 if (!ParseTime(token, entry))
2015 return false;
2016
2017 // Get filename
2018 if (!pLine->GetToken(++index, token, 1))
2019 return false;
2020
2021 entry.name = token.GetString();
2022 char chr = token[token.GetLength() - 1];
2023 if (chr == '/' || chr == '\\')
2024 {
2025 entry.flags |= CDirentry::flag_dir;
2026 entry.name.RemoveLast();
2027 }
2028 }
2029 entry.target = _T("");
2030 entry.ownerGroup = _T("");
2031 entry.permissions = _T("");
2032
2033 if (entry.has_time())
2034 entry.time.Add(m_timezoneOffset);
2035 }
2036
2037 return true;
2038}
2039
2040bool CDirectoryListingParser::AddData(char *pData, int len)
2041{
2042 t_list item;
2043 item.p = pData;
2044 item.len = len;
2045
2046 m_DataList.push_back(item);
2047
2048 return ParseData(true);
2049}
2050
2051bool CDirectoryListingParser::AddLine(const wxChar* pLine)
2052{
2053 if (m_pControlSocket)
2054 m_pControlSocket->LogMessageRaw(RawList, pLine);
2055
2056 while (*pLine == ' ' || *pLine == '\t')
2057 ++pLine;
2058
2059 if (!*pLine)
2060 return false;
2061
2062 const int len = wxStrlen(pLine);
2063
2064 wxChar* p = new wxChar[len + 1];
2065
2066 wxStrcpy(p, pLine);
2067
2068 CLine line(p, len);
2069
2070 ParseLine(&line, m_server.GetType(), false);
2071
2072 return true;
2073}
2074
2075CLine *CDirectoryListingParser::GetLine(bool breakAtEnd /*=false*/, bool &error)
2076{
2077 while (!m_DataList.empty())
2078 {
2079 // Trim empty lines and spaces
2080 std::list<t_list>::iterator iter = m_DataList.begin();
2081 int len = iter->len;
2082 while (iter->p[m_currentOffset]=='\r' || iter->p[m_currentOffset]=='\n' || iter->p[m_currentOffset]==' ' || iter->p[m_currentOffset]=='\t')
2083 {
2084 ++m_currentOffset;
2085 if (m_currentOffset >= len)
2086 {
2087 delete [] iter->p;
2088 ++iter;
2089 m_currentOffset = 0;
2090 if (iter == m_DataList.end())
2091 {
2092 m_DataList.clear();
2093 return 0;
2094 }
2095 len = iter->len;
2096 }
2097 }
2098 m_DataList.erase(m_DataList.begin(), iter);
2099 iter = m_DataList.begin();
2100
2101 // Remember start offset and find next linebreak
2102 int startpos = m_currentOffset;
2103 int reslen = 0;
2104
2105 int emptylen = 0;
2106
2107 int currentOffset = m_currentOffset;
2108 while ((iter->p[currentOffset] != '\n') && (iter->p[currentOffset] != '\r'))
2109 {
2110 if (iter->p[currentOffset] == ' ' || iter->p[currentOffset] == '\t')
2111 ++emptylen;
2112 else
2113 emptylen = 0;
2114 ++reslen;
2115
2116 ++currentOffset;
2117 if (currentOffset >= len)
2118 {
2119 ++iter;
2120 if (iter == m_DataList.end())
2121 {
2122 if (reslen > 10000)
2123 {
2124 m_pControlSocket->LogMessage(::Error, _("Received a line exceeding 10000 characters, aborting."));
2125 error = true;
2126 return 0;
2127 }
2128 if (breakAtEnd)
2129 return 0;
2130 break;
2131 }
2132 len = iter->len;
2133 currentOffset = 0;
2134 }
2135 }
2136
2137 if (reslen > 10000)
2138 {
2139 m_pControlSocket->LogMessage(::Error, _("Received a line exceeding 10000 characters, aborting."));
2140 error = true;
2141 return 0;
2142 }
2143 m_currentOffset = currentOffset;
2144
2145 // Reslen is now the length of the line, including any terminating whitespace
2146 char *res = new char[reslen + 1];
2147 res[reslen] = 0;
2148
2149 int respos = 0;
2150
2151 // Copy line data
2152 std::list<t_list>::iterator i = m_DataList.begin();
2153 while (i != iter && reslen)
2154 {
2155 int copylen = i->len - startpos;
2156 if (copylen > reslen)
2157 copylen = reslen;
2158 memcpy(&res[respos], &i->p[startpos], copylen);
2159 reslen -= copylen;
2160 respos += i->len - startpos;
2161 startpos = 0;
2162
2163 delete [] i->p;
2164 ++i;
2165 }
2166
2167 // Copy last chunk
2168 if (iter != m_DataList.end() && reslen)
2169 {
2170 int copylen = m_currentOffset-startpos;
2171 if (copylen > reslen)
2172 copylen = reslen;
2173 memcpy(&res[respos], &iter->p[startpos], copylen);
2174 if (reslen >= iter->len)
2175 {
2176 delete [] iter->p;
2177 m_DataList.erase(m_DataList.begin(), ++iter);
2178 }
2179 else
2180 m_DataList.erase(m_DataList.begin(), iter);
2181 }
2182 else
2183 m_DataList.erase(m_DataList.begin(), iter);
2184
2185 wxChar* buffer;
2186 if (m_pControlSocket)
2187 {
2188 buffer = m_pControlSocket->ConvToLocalBuffer(res);
2189 m_pControlSocket->LogMessageRaw(RawList, buffer);
2190 }
2191 else
2192 {
2193 wxString str(res, wxConvUTF8);
2194 if (str.IsEmpty())
2195 {
2196 str = wxString(res, wxConvLocal);
2197 if (str.IsEmpty())
2198 str = wxString(res, wxConvISO8859_1);
2199 }
2200 buffer = new wxChar[str.Len() + 1];
2201 wxStrcpy(buffer, str.c_str());
2202 }
2203 delete [] res;
2204
2205 if (!buffer)
2206 {
2207 // Line contained no usable data, start over
2208 continue;
2209 }
2210
2211 return new CLine(buffer, -1, emptylen);
2212 }
2213
2214 return 0;
2215}
2216
2217bool CDirectoryListingParser::ParseAsWfFtp(CLine *pLine, CDirentry &entry)
2218{
2219 int index = 0;
2220 CToken token;
2221
2222 // Get filename
2223 if (!pLine->GetToken(index++, token))
2224 return false;
2225
2226 entry.name = token.GetString();
2227
2228 // Get filesize
2229 if (!pLine->GetToken(index++, token))
2230 return false;
2231
2232 if (!token.IsNumeric())
2233 return false;
2234
2235 entry.size = token.GetNumber();
2236
2237 entry.flags = 0;
2238
2239 // Parse date
2240 if (!pLine->GetToken(index++, token))
2241 return false;
2242
2243 if (!ParseShortDate(token, entry))
2244 return false;
2245
2246 // Unused token
2247 if (!pLine->GetToken(index++, token))
2248 return false;
2249
2250 if (token.GetString().Right(1) != _T("."))
2251 return false;
2252
2253 // Parse time
2254 if (!pLine->GetToken(index++, token, true))
2255 return false;
2256
2257 if (!ParseTime(token, entry))
2258 return false;
2259
2260 entry.ownerGroup = _T("");
2261 entry.permissions = _T("");
2262
2263 if (entry.has_time())
2264 entry.time.Add(m_timezoneOffset);
2265
2266 return true;
2267}
2268
2269bool CDirectoryListingParser::ParseAsIBM_MVS(CLine *pLine, CDirentry &entry)
2270{
2271 int index = 0;
2272 CToken token;
2273
2274 // volume
2275 if (!pLine->GetToken(index++, token))
2276 return false;
2277
2278 // unit
2279 if (!pLine->GetToken(index++, token))
2280 return false;
2281
2282 // Referred date
2283 if (!pLine->GetToken(index++, token))
2284 return false;
2285
2286 entry.flags = 0;
2287 if (token.GetString() != _T("**NONE**") && !ParseShortDate(token, entry))
2288 {
2289 // Perhaps of the following type:
2290 // TSO004 3390 VSAM FOO.BAR
2291 if (token.GetString() != _T("VSAM"))
2292 return false;
2293
2294 if (!pLine->GetToken(index++, token))
2295 return false;
2296
2297 entry.name = token.GetString();
2298 if (entry.name.Find(' ') != -1)
2299 return false;
2300
2301 entry.size = -1;
2302 entry.ownerGroup = _T("");
2303 entry.permissions = _T("");
2304
2305 return true;
2306 }
2307
2308 // ext
2309 if (!pLine->GetToken(index++, token))
2310 return false;
2311 if (!token.IsNumeric())
2312 return false;
2313
2314 int prevLen = token.GetLength();
2315
2316 // used
2317 if (!pLine->GetToken(index++, token))
2318 return false;
2319 if (token.IsNumeric() || token.GetString() == _T("????") || token.GetString() == _T("++++") )
2320 {
2321 // recfm
2322 if (!pLine->GetToken(index++, token))
2323 return false;
2324 if (token.IsNumeric())
2325 return false;
2326 }
2327 else
2328 {
2329 if (prevLen < 6)
2330 return false;
2331 }
2332
2333 // lrecl
2334 if (!pLine->GetToken(index++, token))
2335 return false;
2336 if (!token.IsNumeric())
2337 return false;
2338
2339 // blksize
2340 if (!pLine->GetToken(index++, token))
2341 return false;
2342 if (!token.IsNumeric())
2343 return false;
2344
2345 // dsorg
2346 if (!pLine->GetToken(index++, token))
2347 return false;
2348
2349 if (token.GetString() == _T("PO") || token.GetString() == _T("PO-E"))
2350 {
2351 entry.flags |= CDirentry::flag_dir;
2352 entry.size = -1;
2353 }
2354 else
2355 entry.size = 100;
2356
2357 // name of dataset or sequential file
2358 if (!pLine->GetToken(index++, token, true))
2359 return false;
2360
2361 entry.name = token.GetString();
2362
2363 entry.ownerGroup = _T("");
2364 entry.permissions = _T("");
2365
2366 return true;
2367}
2368
2369bool CDirectoryListingParser::ParseAsIBM_MVS_PDS(CLine *pLine, CDirentry &entry)
2370{
2371 int index = 0;
2372 CToken token;
2373
2374 // pds member name
2375 if (!pLine->GetToken(index++, token))
2376 return false;
2377 entry.name = token.GetString();
2378
2379 // vv.mm
2380 if (!pLine->GetToken(index++, token))
2381 return false;
2382
2383 entry.flags = 0;
2384
2385 // creation date
2386 if (!pLine->GetToken(index++, token))
2387 return false;
2388 if (!ParseShortDate(token, entry))
2389 return false;
2390
2391 // modification date
2392 if (!pLine->GetToken(index++, token))
2393 return false;
2394 if (!ParseShortDate(token, entry))
2395 return false;
2396
2397 // modification time
2398 if (!pLine->GetToken(index++, token))
2399 return false;
2400 if (!ParseTime(token, entry))
2401 return false;
2402
2403 // size
2404 if (!pLine->GetToken(index++, token))
2405 return false;
2406 if (!token.IsNumeric())
2407 return false;
2408 entry.size = token.GetNumber();
2409
2410 // init
2411 if (!pLine->GetToken(index++, token))
2412 return false;
2413 if (!token.IsNumeric())
2414 return false;
2415
2416 // mod
2417 if (!pLine->GetToken(index++, token))
2418 return false;
2419 if (!token.IsNumeric())
2420 return false;
2421
2422 // id
2423 if (!pLine->GetToken(index++, token, true))
2424 return false;
2425
2426 entry.ownerGroup = _T("");
2427 entry.permissions = _T("");
2428
2429 if (entry.has_time())
2430 entry.time.Add(m_timezoneOffset);
2431
2432 return true;
2433}
2434
2435bool CDirectoryListingParser::ParseAsIBM_MVS_Migrated(CLine *pLine, CDirentry &entry)
2436{
2437 // Migrated MVS file
2438 // "Migrated SOME.NAME"
2439
2440 int index = 0;
2441 CToken token;
2442 if (!pLine->GetToken(index, token))
2443 return false;
2444
2445 if (token.GetString().CmpNoCase(_T("Migrated")))
2446 return false;
2447
2448 if (!pLine->GetToken(++index, token))
2449 return false;
2450
2451 entry.name = token.GetString();
2452
2453 entry.flags = 0;
2454 entry.ownerGroup = _T("");
2455 entry.permissions = _T("");
2456 entry.size = -1;
2457
2458 if (pLine->GetToken(++index, token))
2459 return false;
2460
2461 return true;
2462}
2463
2464bool CDirectoryListingParser::ParseAsIBM_MVS_PDS2(CLine *pLine, CDirentry &entry)
2465{
2466 int index = 0;
2467 CToken token;
2468 if (!pLine->GetToken(index, token))
2469 return false;
2470
2471 entry.name = token.GetString();
2472
2473 entry.flags = 0;
2474 entry.ownerGroup = _T("");
2475 entry.permissions = _T("");
2476 entry.size = -1;
2477
2478 if (!pLine->GetToken(++index, token))
2479 return true;
2480
2481 entry.size = token.GetNumber(CToken::hex);
2482 if (entry.size == -1)
2483 return false;
2484
2485 // Unused hexadecimal token
2486 if (!pLine->GetToken(++index, token))
2487 return false;
2488 if (!token.IsNumeric(CToken::hex))
2489 return false;
2490
2491 // Unused numeric token
2492 if (!pLine->GetToken(++index, token))
2493 return false;
2494 if (!token.IsNumeric())
2495 return false;
2496
2497 int start = ++index;
2498 while (pLine->GetToken(index, token))
2499 {
2500 ++index;
2501 }
2502 if ((index - start < 2))
2503 return false;
2504 --index;
2505
2506 pLine->GetToken(index, token);
2507 if (!token.IsNumeric() && (token.GetString() != _T("ANY")))
2508 return false;
2509
2510 pLine->GetToken(index - 1, token);
2511 if (!token.IsNumeric() && (token.GetString() != _T("ANY")))
2512 return false;
2513
2514 for (int i = start; i < index - 1; ++i)
2515 {
2516 pLine->GetToken(i, token);
2517 int len = token.GetLength();
2518 for (int j = 0; j < len; ++j)
2519 if (token[j] < 'A' || token[j] > 'Z')
2520 return false;
2521 }
2522
2523 return true;
2524}
2525
2526bool CDirectoryListingParser::ParseAsIBM_MVS_Tape(CLine *pLine, CDirentry &entry)
2527{
2528 int index = 0;
2529 CToken token;
2530
2531 // volume
2532 if (!pLine->GetToken(index++, token))
2533 return false;
2534
2535 // unit
2536 if (!pLine->GetToken(index++, token))
2537 return false;
2538
2539 if (token.GetString().CmpNoCase(_T("Tape")))
2540 return false;
2541
2542 // dsname
2543 if (!pLine->GetToken(index++, token))
2544 return false;
2545
2546 entry.name = token.GetString();
2547 entry.flags = 0;
2548 entry.ownerGroup = _T("");
2549 entry.permissions = _T("");
2550 entry.size = -1;
2551
2552 if (pLine->GetToken(index++, token))
2553 return false;
2554
2555 return true;
2556}
2557
2558bool CDirectoryListingParser::ParseComplexFileSize(CToken& token, wxLongLong& size, int blocksize /*=-1*/)
2559{
2560 if (token.IsNumeric())
2561 {
2562 size = token.GetNumber();
2563 if (blocksize != -1)
2564 size *= blocksize;
2565
2566 return true;
2567 }
2568
2569 int len = token.GetLength();
2570
2571 char last = token[len - 1];
2572 if (last == 'B' || last == 'b')
2573 {
2574 if (len == 1)
2575 return false;
2576
2577 char c = token[--len - 1];
2578 if (c < '0' || c > '9')
2579 {
2580 --len;
2581 last = c;
2582 }
2583 else
2584 last = 0;
2585 }
2586 else if (last >= '0' && last <= '9')
2587 last = 0;
2588 else
2589 {
2590 if (--len == 0)
2591 return false;
2592 }
2593
2594 size = 0;
2595
2596 int dot = -1;
2597 for (int i = 0; i < len; ++i)
2598 {
2599 char c = token[i];
2600 if (c >= '0' && c <= '9')
2601 {
2602 size *= 10;
2603 size += c - '0';
2604 }
2605 else if (c == '.')
2606 {
2607 if (dot != -1)
2608 return false;
2609 dot = len - i - 1;
2610 }
2611 else
2612 return false;
2613 }
2614 switch (last)
2615 {
2616 case 'k':
2617 case 'K':
2618 size *= 1024;
2619 break;
2620 case 'm':
2621 case 'M':
2622 size *= 1024 * 1024;
2623 break;
2624 case 'g':
2625 case 'G':
2626 size *= 1024 * 1024 * 1024;
2627 break;
2628 case 't':
2629 case 'T':
2630 size *= 1024 * 1024;
2631 size *= 1024 * 1024;
2632 break;
2633 case 'b':
2634 case 'B':
2635 break;
2636 case 0:
2637 if (blocksize != -1)
2638 size *= blocksize;
2639 break;
2640 default:
2641 return false;
2642 }
2643 while (dot-- > 0)
2644 size /= 10;
2645
2646 return true;
2647}
2648
2649int CDirectoryListingParser::ParseAsMlsd(CLine *pLine, CDirentry &entry)
2650{
2651 // MLSD format as described here: http://www.ietf.org/internet-drafts/draft-ietf-ftpext-mlst-16.txt
2652
2653 // Parsing is done strict, abort on slightest error.
2654
2655 CToken token;
2656
2657 if (!pLine->GetToken(0, token))
2658 return 0;
2659
2660 wxString facts = token.GetString();
2661 if (facts.IsEmpty())
2662 return 0;
2663
2664 entry.flags = 0;
2665 entry.size = -1;
2666 entry.ownerGroup = _T("");
2667 entry.permissions = _T("");
2668
2669 wxString owner, group, uid, gid;
2670
2671 while (!facts.IsEmpty())
2672 {
2673 int delim = facts.Find(';');
2674 if (delim < 3)
2675 {
2676 if (delim != -1)
2677 return 0;
2678 else
2679 delim = facts.Len();
2680 }
2681
2682 int pos = facts.Find('=');
2683 if (pos < 1 || pos > delim)
2684 return 0;
2685
2686 wxString factname = facts.Left(pos);
2687 MakeLowerAscii(factname);
2688 wxString value = facts.Mid(pos + 1, delim - pos - 1);
2689 if (factname == _T("type"))
2690 {
2691 if (!value.CmpNoCase(_T("dir")))
2692 entry.flags |= CDirentry::flag_dir;
2693 else if (!value.Left(13).CmpNoCase(_T("OS.unix=slink")))
2694 {
2695 entry.flags |= CDirentry::flag_dir | CDirentry::flag_link;
2696 if (value[13] == ':' && value[14] != 0)
2697 entry.target = value.Mid(14);
2698 }
2699 else if (!value.CmpNoCase(_T("cdir")) ||
2700 !value.CmpNoCase(_T("pdir")))
2701 return 2;
2702 }
2703 else if (factname == _T("size"))
2704 {
2705 entry.size = 0;
2706
2707 for (unsigned int i = 0; i < value.Len(); ++i)
2708 {
2709 if (value[i] < '0' || value[i] > '9')
2710 return 0;
2711 entry.size *= 10;
2712 entry.size += value[i] - '0';
2713 }
2714 }
2715 else if (factname == _T("modify") ||
2716 (!entry.has_date() && factname == _T("create")))
2717 {
2718 wxDateTime dateTime;
2719 const wxChar* time = dateTime.ParseFormat(value, _T("%Y%m%d"));
2720
2721 if (!time)
2722 return 0;
2723
2724 if (*time)
2725 {
2726 if (!dateTime.ParseFormat(time, _T("%H%M%S"), dateTime))
2727 return 0;
2728 entry.flags |= CDirentry::flag_timestamp_date | CDirentry::flag_timestamp_time | CDirentry::flag_timestamp_seconds;
2729 }
2730 else
2731 entry.flags |= CDirentry::flag_timestamp_date;
2732
2733 entry.time = dateTime.FromTimezone(wxDateTime::GMT0);
2734 }
2735 else if (factname == _T("perm"))
2736 {
2737 if (!value.empty())
2738 {
2739 if (!entry.permissions.empty())
2740 entry.permissions = value + _T(" (") + entry.permissions + _T(")");
2741 else
2742 entry.permissions = value;
2743 }
2744 }
2745 else if (factname == _T("unix.mode"))
2746 {
2747 if (!entry.permissions.empty())
2748 entry.permissions = entry.permissions + _T(" (") + value + _T(")");
2749 else
2750 entry.permissions = value;
2751 }
2752 else if (factname == _T("unix.owner") || factname == _T("unix.user"))
2753 owner = value;
2754 else if (factname == _T("unix.group"))
2755 group = value;
2756 else if (factname == _T("unix.uid"))
2757 uid = value;
2758 else if (factname == _T("unix.gid"))
2759 gid = value;
2760
2761 facts = facts.Mid(delim + 1);
2762 }
2763
2764 // The order of the facts is undefined, so assemble ownerGroup in correct
2765 // order
2766 if (!owner.empty())
2767 entry.ownerGroup += owner;
2768 else if (!uid.empty())
2769 entry.ownerGroup += uid;
2770 if (!group.empty())
2771 entry.ownerGroup += _T(" ") + group;
2772 else if (!gid.empty())
2773 entry.ownerGroup += _T(" ") + gid;
2774
2775 if (!pLine->GetToken(1, token, true, true))
2776 return 0;
2777
2778 entry.name = token.GetString();
2779
2780 return 1;
2781}
2782
2783bool CDirectoryListingParser::ParseAsOS9(CLine *pLine, CDirentry &entry)
2784{
2785 int index = 0;
2786 CToken token;
2787
2788 // Get owner
2789 if (!pLine->GetToken(index++, token))
2790 return false;
2791
2792 // Make sure it's number.number
2793 int pos = token.Find('.');
2794 if (pos == -1 || !pos || pos == ((int)token.GetLength() - 1))
2795 return false;
2796
2797 if (!token.IsNumeric(0, pos))
2798 return false;
2799
2800 if (!token.IsNumeric(pos + 1, token.GetLength() - pos - 1))
2801 return false;
2802
2803 entry.ownerGroup = token.GetString();
2804
2805 entry.flags = 0;
2806
2807 // Get date
2808 if (!pLine->GetToken(index++, token))
2809 return false;
2810
2811 if (!ParseShortDate(token, entry, true))
2812 return false;
2813
2814 // Unused token
2815 if (!pLine->GetToken(index++, token))
2816 return false;
2817
2818 // Get perms
2819 if (!pLine->GetToken(index++, token))
2820 return false;
2821
2822 entry.permissions = token.GetString();
2823
2824 if (token[0] == 'd')
2825 entry.flags |= CDirentry::flag_dir;
2826
2827 // Unused token
2828 if (!pLine->GetToken(index++, token))
2829 return false;
2830
2831 // Get Size
2832 if (!pLine->GetToken(index++, token))
2833 return false;
2834
2835 if (!token.IsNumeric())
2836 return false;
2837
2838 entry.size = token.GetNumber();
2839
2840 // Filename
2841 if (!pLine->GetToken(index++, token, true))
2842 return false;
2843
2844 entry.name = token.GetString();
2845
2846 return true;
2847}
2848
2849void CDirectoryListingParser::Reset()
2850{
2851 for (std::list<t_list>::iterator iter = m_DataList.begin(); iter != m_DataList.end(); ++iter)
2852 delete [] iter->p;
2853 m_DataList.clear();
2854
2855 delete m_prevLine;
2856 m_prevLine = 0;
2857
2858 m_entryList.clear();
2859 m_fileList.clear();
2860 m_currentOffset = 0;
2861 m_fileListOnly = true;
2862 m_maybeMultilineVms = false;
2863}
2864
2865bool CDirectoryListingParser::ParseAsZVM(CLine* pLine, CDirentry &entry)
2866{
2867 int index = 0;
2868 CToken token;
2869
2870 // Get name
2871 if (!pLine->GetToken(index, token))
2872 return false;
2873
2874 entry.name = token.GetString();
2875
2876 // Get filename extension
2877 if (!pLine->GetToken(++index, token))
2878 return false;
2879 entry.name += _T(".") + token.GetString();
2880
2881 // File format. Unused
2882 if (!pLine->GetToken(++index, token))
2883 return false;
2884 wxString format = token.GetString();
2885 if (format != _T("V") && format != _T("F"))
2886 return false;
2887
2888 // Record length
2889 if (!pLine->GetToken(++index, token))
2890 return false;
2891
2892 if (!token.IsNumeric())
2893 return false;
2894
2895 entry.size = token.GetNumber();
2896
2897 // Number of records
2898 if (!pLine->GetToken(++index, token))
2899 return false;
2900
2901 if (!token.IsNumeric())
2902 return false;
2903
2904 entry.size *= token.GetNumber();
2905
2906 // Unused (Block size?)
2907 if (!pLine->GetToken(++index, token))
2908 return false;
2909
2910 if (!token.IsNumeric())
2911 return false;
2912
2913 entry.flags = 0;
2914
2915 // Date
2916 if (!pLine->GetToken(++index, token))
2917 return false;
2918
2919 if (!ParseShortDate(token, entry, true))
2920 return false;
2921
2922 // Time
2923 if (!pLine->GetToken(++index, token))
2924 return false;
2925
2926 if (!ParseTime(token, entry))
2927 return false;
2928
2929 // Owner
2930 if (!pLine->GetToken(++index, token))
2931 return false;
2932
2933 entry.ownerGroup = token.GetString();
2934
2935 // No further token!
2936 if (pLine->GetToken(++index, token))
2937 return false;
2938
2939 entry.permissions = _T("");
2940 entry.target = _T("");
2941
2942 if (entry.has_time())
2943 entry.time.Add(m_timezoneOffset);
2944
2945 return true;
2946}
2947
2948bool CDirectoryListingParser::ParseAsHPNonstop(CLine *pLine, CDirentry &entry)
2949{
2950 int index = 0;
2951 CToken token;
2952
2953 // Get name
2954 if (!pLine->GetToken(index, token))
2955 return false;
2956
2957 entry.name = token.GetString();
2958
2959 // File code, numeric, unsuded
2960 if (!pLine->GetToken(++index, token))
2961 return false;
2962 if (!token.IsNumeric())
2963 return false;
2964
2965 // Size
2966 if (!pLine->GetToken(++index, token))
2967 return false;
2968 if (!token.IsNumeric())
2969 return false;
2970
2971 entry.size = token.GetNumber();
2972
2973 entry.flags = 0;
2974
2975 // Date
2976 if (!pLine->GetToken(++index, token))
2977 return false;
2978 if (!ParseShortDate(token, entry, false))
2979 return false;
2980
2981 // Time
2982 if (!pLine->GetToken(++index, token))
2983 return false;
2984 if (!ParseTime(token, entry))
2985 return false;
2986
2987 // Owner
2988 if (!pLine->GetToken(++index, token))
2989 return false;
2990 entry.ownerGroup = token.GetString();
2991
2992 if (token[token.GetLength() - 1] == ',')
2993 {
2994 // Owner, part 2
2995 if (!pLine->GetToken(++index, token))
2996 return false;
2997 entry.ownerGroup += _T(" ") + token.GetString();
2998 }
2999
3000 // Permissions
3001 if (!pLine->GetToken(++index, token))
3002 return false;
3003 entry.permissions = token.GetString();
3004
3005 // Nothing
3006 if (pLine->GetToken(++index, token))
3007 return false;
3008
3009 return true;
3010}
3011
3012bool CDirectoryListingParser::GetMonthFromName(const wxString& name, int &month)
3013{
3014 std::map<wxString, int>::iterator iter = m_MonthNamesMap.find(name.Lower());
3015 if (iter == m_MonthNamesMap.end())
3016 {
3017 wxString lower(name);
3018 MakeLowerAscii(lower);
3019 iter = m_MonthNamesMap.find(lower);
3020 if (iter == m_MonthNamesMap.end())
3021 return false;
3022 }
3023
3024 month = iter->second;
3025
3026 return true;
3027}