2
#include "directorylistingparser.h"
3
#include "ControlSocket.h"
9
std::map<wxString, int> CDirectoryListingParser::m_MonthNamesMap;
11
//#define LISTDEBUG_MVS
14
static char data[][150]={
15
"" // Has to be terminated with empty string
35
, m_leftNumeric(Unknown)
36
, m_rightNumeric(Unknown)
47
CToken(const wxChar* p, unsigned int len)
51
, m_leftNumeric(Unknown)
52
, m_rightNumeric(Unknown)
56
const wxChar* GetToken() const
61
unsigned int GetLength() const
66
wxString GetString(unsigned int type = 0)
74
if (!m_str[type].IsEmpty())
79
wxString str(m_pToken, m_len);
85
if (!IsRightNumeric() || IsNumeric())
89
while (m_pToken[pos] >= '0' && m_pToken[pos] <= '9')
92
wxString str(m_pToken, pos + 1);
98
if (!IsLeftNumeric() || IsNumeric())
102
while (m_pToken[pos] >= '0' && m_pToken[pos] <= '9')
105
wxString str(m_pToken + pos, m_len - pos);
113
bool IsNumeric(t_numberBase base = decimal)
119
if (m_numeric == Unknown)
122
for (unsigned int i = 0; i < m_len; ++i)
123
if (m_pToken[i] < '0' || m_pToken[i] > '9')
129
return m_numeric == Yes;
131
for (unsigned int i = 0; i < m_len; ++i)
133
const char c = m_pToken[i];
134
if ((c < '0' || c > '9') && (c < 'A' || c > 'F') && (c < 'a' || c > 'f'))
141
bool IsNumeric(unsigned int start, unsigned int len)
143
for (unsigned int i = start; i < wxMin(start + len, m_len); ++i)
144
if (m_pToken[i] < '0' || m_pToken[i] > '9')
151
if (m_leftNumeric == Unknown)
155
else if (m_pToken[0] < '0' || m_pToken[0] > '9')
160
return m_leftNumeric == Yes;
163
bool IsRightNumeric()
165
if (m_rightNumeric == Unknown)
169
else if (m_pToken[m_len - 1] < '0' || m_pToken[m_len - 1] > '9')
172
m_rightNumeric = Yes;
174
return m_rightNumeric == Yes;
177
int Find(const wxChar* chr, int start = 0) const
182
for (unsigned int i = start; i < m_len; ++i)
184
for (int c = 0; chr[c]; ++c)
186
if (m_pToken[i] == chr[c])
193
int Find(wxChar chr, int start = 0) const
198
for (unsigned int i = start; i < m_len; ++i)
199
if (m_pToken[i] == chr)
205
wxLongLong GetNumber(unsigned int start, int len)
212
if (start + static_cast<unsigned int>(len) > m_len)
215
if (m_pToken[start] < '0' || m_pToken[start] > '9')
218
wxLongLong number = 0;
219
for (unsigned int i = start; i < (start + len); ++i)
221
if (m_pToken[i] < '0' || m_pToken[i] > '9')
224
number += m_pToken[i] - '0';
229
wxLongLong GetNumber(t_numberBase base = decimal)
237
if (IsNumeric() || IsLeftNumeric())
240
for (unsigned int i = 0; i < m_len; ++i)
242
if (m_pToken[i] < '0' || m_pToken[i] > '9')
245
m_number += m_pToken[i] - '0';
248
else if (IsRightNumeric())
251
int start = m_len - 1;
252
while (m_pToken[start - 1] >= '0' && m_pToken[start - 1] <= '9')
254
for (unsigned int i = start; i < m_len; ++i)
257
m_number += m_pToken[i] - '0';
264
wxLongLong number = 0;
265
for (unsigned int i = 0; i < m_len; ++i)
267
const wxChar& c = m_pToken[i];
268
if (c >= '0' && c <= '9')
273
else if (c >= 'a' && c <= 'f')
276
number += c - '0' + 10;
278
else if (c >= 'A' && c <= 'F')
281
number += c - 'A' + 10;
291
wxChar operator[](unsigned int n) const
300
const wxChar* m_pToken;
303
TokenInformation m_numeric;
304
TokenInformation m_leftNumeric;
305
TokenInformation m_rightNumeric;
313
CLine(wxChar* p, int len = -1, int trailing_whitespace = 0)
323
m_Tokens.reserve(10);
324
m_LineEndTokens.reserve(10);
325
m_trailing_whitespace = trailing_whitespace;
332
std::vector<CToken *>::iterator iter;
333
for (iter = m_Tokens.begin(); iter != m_Tokens.end(); ++iter)
335
for (iter = m_LineEndTokens.begin(); iter != m_LineEndTokens.end(); ++iter)
339
bool GetToken(unsigned int n, CToken &token, bool toEnd = false, bool include_whitespace = false)
343
if (m_Tokens.size() > n)
345
token = *(m_Tokens[n]);
349
int start = m_parsePos;
350
while (m_parsePos < m_len)
352
if (m_pLine[m_parsePos] == ' ' || m_pLine[m_parsePos] == '\t')
354
CToken *pToken = new CToken(m_pLine + start, m_parsePos - start);
355
m_Tokens.push_back(pToken);
357
while ((m_pLine[m_parsePos] == ' ' || m_pLine[m_parsePos] == '\t') && m_parsePos < m_len)
360
if (m_Tokens.size() > n)
362
token = *(m_Tokens[n]);
370
if (m_parsePos != start)
372
CToken *pToken = new CToken(m_pLine + start, m_parsePos - start);
373
m_Tokens.push_back(pToken);
376
if (m_Tokens.size() > n)
378
token = *(m_Tokens[n]);
386
if (include_whitespace)
392
if (!GetToken(n, ref))
394
p = ref.GetToken() + ref.GetLength() + 1;
399
if (!GetToken(n - 1, ref))
401
p = ref.GetToken() + ref.GetLength() + 1;
404
token = CToken(p, m_len - (p - m_pLine));
408
if (m_LineEndTokens.size() > n)
410
token = *(m_LineEndTokens[n]);
414
if (m_Tokens.size() <= n)
415
if (!GetToken(n, token))
418
for (unsigned int i = static_cast<unsigned int>(m_LineEndTokens.size()); i <= n; ++i)
420
const CToken *refToken = m_Tokens[i];
421
const wxChar* p = refToken->GetToken();
422
CToken *pToken = new CToken(p, m_len - (p - m_pLine) - m_trailing_whitespace);
423
m_LineEndTokens.push_back(pToken);
425
token = *(m_LineEndTokens[n]);
430
CLine *Concat(const CLine *pLine) const
432
int newLen = m_len + pLine->m_len + 1;
433
wxChar* p = new wxChar[newLen];
434
memcpy(p, m_pLine, m_len * sizeof(wxChar));
436
memcpy(p + m_len + 1, pLine->m_pLine, pLine->m_len * sizeof(wxChar));
438
return new CLine(p, m_len + pLine->m_len + 1, pLine->m_trailing_whitespace);
442
std::vector<CToken *> m_Tokens;
443
std::vector<CToken *> m_LineEndTokens;
446
int m_trailing_whitespace;
450
CDirectoryListingParser::CDirectoryListingParser(CControlSocket* pControlSocket, const CServer& server)
451
: m_pControlSocket(pControlSocket), m_server(server)
455
m_fileListOnly = true;
456
m_maybeMultilineVms = false;
458
if (m_MonthNamesMap.empty())
460
//Fill the month names map
462
//English month names
463
m_MonthNamesMap[_T("jan")] = 1;
464
m_MonthNamesMap[_T("feb")] = 2;
465
m_MonthNamesMap[_T("mar")] = 3;
466
m_MonthNamesMap[_T("apr")] = 4;
467
m_MonthNamesMap[_T("may")] = 5;
468
m_MonthNamesMap[_T("jun")] = 6;
469
m_MonthNamesMap[_T("june")] = 6;
470
m_MonthNamesMap[_T("jul")] = 7;
471
m_MonthNamesMap[_T("july")] = 7;
472
m_MonthNamesMap[_T("aug")] = 8;
473
m_MonthNamesMap[_T("sep")] = 9;
474
m_MonthNamesMap[_T("sept")] = 9;
475
m_MonthNamesMap[_T("oct")] = 10;
476
m_MonthNamesMap[_T("nov")] = 11;
477
m_MonthNamesMap[_T("dec")] = 12;
479
//Numerical values for the month
480
m_MonthNamesMap[_T("1")] = 1;
481
m_MonthNamesMap[_T("01")] = 1;
482
m_MonthNamesMap[_T("2")] = 2;
483
m_MonthNamesMap[_T("02")] = 2;
484
m_MonthNamesMap[_T("3")] = 3;
485
m_MonthNamesMap[_T("03")] = 3;
486
m_MonthNamesMap[_T("4")] = 4;
487
m_MonthNamesMap[_T("04")] = 4;
488
m_MonthNamesMap[_T("5")] = 5;
489
m_MonthNamesMap[_T("05")] = 5;
490
m_MonthNamesMap[_T("6")] = 6;
491
m_MonthNamesMap[_T("06")] = 6;
492
m_MonthNamesMap[_T("7")] = 7;
493
m_MonthNamesMap[_T("07")] = 7;
494
m_MonthNamesMap[_T("8")] = 8;
495
m_MonthNamesMap[_T("08")] = 8;
496
m_MonthNamesMap[_T("9")] = 9;
497
m_MonthNamesMap[_T("09")] = 9;
498
m_MonthNamesMap[_T("10")] = 10;
499
m_MonthNamesMap[_T("11")] = 11;
500
m_MonthNamesMap[_T("12")] = 12;
503
m_MonthNamesMap[_T("mrz")] = 3;
504
m_MonthNamesMap[_T("m\xe4r")] = 3;
505
m_MonthNamesMap[_T("m\xe4rz")] = 3;
506
m_MonthNamesMap[_T("mai")] = 5;
507
m_MonthNamesMap[_T("juni")] = 6;
508
m_MonthNamesMap[_T("juli")] = 7;
509
m_MonthNamesMap[_T("okt")] = 10;
510
m_MonthNamesMap[_T("dez")] = 12;
512
//Austrian month names
513
m_MonthNamesMap[_T("j\xe4n")] = 1;
516
m_MonthNamesMap[_T("janv")] = 1;
517
m_MonthNamesMap[_T("f\xe9")_T("b")] = 1;
518
m_MonthNamesMap[_T("f\xe9v")] = 2;
519
m_MonthNamesMap[_T("fev")] = 2;
520
m_MonthNamesMap[_T("f\xe9vr")] = 2;
521
m_MonthNamesMap[_T("fevr")] = 2;
522
m_MonthNamesMap[_T("mars")] = 3;
523
m_MonthNamesMap[_T("mrs")] = 3;
524
m_MonthNamesMap[_T("avr")] = 4;
525
m_MonthNamesMap[_T("juin")] = 6;
526
m_MonthNamesMap[_T("juil")] = 7;
527
m_MonthNamesMap[_T("jui")] = 7;
528
m_MonthNamesMap[_T("ao\xfb")] = 8;
529
m_MonthNamesMap[_T("ao\xfbt")] = 8;
530
m_MonthNamesMap[_T("aout")] = 8;
531
m_MonthNamesMap[_T("d\xe9")_T("c")] = 12;
532
m_MonthNamesMap[_T("dec")] = 12;
534
//Italian month names
535
m_MonthNamesMap[_T("gen")] = 1;
536
m_MonthNamesMap[_T("mag")] = 5;
537
m_MonthNamesMap[_T("giu")] = 6;
538
m_MonthNamesMap[_T("lug")] = 7;
539
m_MonthNamesMap[_T("ago")] = 8;
540
m_MonthNamesMap[_T("set")] = 9;
541
m_MonthNamesMap[_T("ott")] = 10;
542
m_MonthNamesMap[_T("dic")] = 12;
544
//Spanish month names
545
m_MonthNamesMap[_T("ene")] = 1;
546
m_MonthNamesMap[_T("fbro")] = 2;
547
m_MonthNamesMap[_T("mzo")] = 3;
548
m_MonthNamesMap[_T("ab")] = 4;
549
m_MonthNamesMap[_T("abr")] = 4;
550
m_MonthNamesMap[_T("agto")] = 8;
551
m_MonthNamesMap[_T("sbre")] = 9;
552
m_MonthNamesMap[_T("obre")] = 9;
553
m_MonthNamesMap[_T("nbre")] = 9;
554
m_MonthNamesMap[_T("dbre")] = 9;
557
m_MonthNamesMap[_T("sty")] = 1;
558
m_MonthNamesMap[_T("lut")] = 2;
559
m_MonthNamesMap[_T("kwi")] = 4;
560
m_MonthNamesMap[_T("maj")] = 5;
561
m_MonthNamesMap[_T("cze")] = 6;
562
m_MonthNamesMap[_T("lip")] = 7;
563
m_MonthNamesMap[_T("sie")] = 8;
564
m_MonthNamesMap[_T("wrz")] = 9;
565
m_MonthNamesMap[_T("pa\x9f")] = 10;
566
m_MonthNamesMap[_T("lis")] = 11;
567
m_MonthNamesMap[_T("gru")] = 12;
569
//Russian month names
570
m_MonthNamesMap[_T("\xff\xed\xe2")] = 1;
571
m_MonthNamesMap[_T("\xf4\xe5\xe2")] = 2;
572
m_MonthNamesMap[_T("\xec\xe0\xf0")] = 3;
573
m_MonthNamesMap[_T("\xe0\xef\xf0")] = 4;
574
m_MonthNamesMap[_T("\xec\xe0\xe9")] = 5;
575
m_MonthNamesMap[_T("\xe8\xfe\xed")] = 6;
576
m_MonthNamesMap[_T("\xe8\xfe\xeb")] = 7;
577
m_MonthNamesMap[_T("\xe0\xe2\xe3")] = 8;
578
m_MonthNamesMap[_T("\xf1\xe5\xed")] = 9;
579
m_MonthNamesMap[_T("\xee\xea\xf2")] = 10;
580
m_MonthNamesMap[_T("\xed\xee\xff")] = 11;
581
m_MonthNamesMap[_T("\xe4\xe5\xea")] = 12;
584
m_MonthNamesMap[_T("mrt")] = 3;
585
m_MonthNamesMap[_T("mei")] = 5;
587
//Portuguese month names
588
m_MonthNamesMap[_T("out")] = 10;
590
//Finnish month names
591
m_MonthNamesMap[_T("tammi")] = 1;
592
m_MonthNamesMap[_T("helmi")] = 2;
593
m_MonthNamesMap[_T("maalis")] = 3;
594
m_MonthNamesMap[_T("huhti")] = 4;
595
m_MonthNamesMap[_T("touko")] = 5;
596
m_MonthNamesMap[_T("kes\xe4")] = 6;
597
m_MonthNamesMap[_T("hein\xe4")] = 7;
598
m_MonthNamesMap[_T("elo")] = 8;
599
m_MonthNamesMap[_T("syys")] = 9;
600
m_MonthNamesMap[_T("loka")] = 10;
601
m_MonthNamesMap[_T("marras")] = 11;
602
m_MonthNamesMap[_T("joulu")] = 12;
604
//Slovenian month names
605
m_MonthNamesMap[_T("avg")] = 8;
609
m_MonthNamesMap[_T("ma\x00ed")] = 5;
610
m_MonthNamesMap[_T("j\x00fan")] = 6;
611
m_MonthNamesMap[_T("j\x00fal")] = 7;
612
m_MonthNamesMap[_T("\x00e1g")] = 8;
613
m_MonthNamesMap[_T("n\x00f3v")] = 11;
615
m_MonthNamesMap[_T("des")] = 12;
618
m_MonthNamesMap[_T("sau")] = 1;
619
m_MonthNamesMap[_T("vas")] = 2;
620
m_MonthNamesMap[_T("kov")] = 3;
621
m_MonthNamesMap[_T("bal")] = 4;
622
m_MonthNamesMap[_T("geg")] = 5;
623
m_MonthNamesMap[_T("bir")] = 6;
624
m_MonthNamesMap[_T("lie")] = 7;
625
m_MonthNamesMap[_T("rgp")] = 8;
626
m_MonthNamesMap[_T("rgs")] = 9;
627
m_MonthNamesMap[_T("spa")] = 10;
628
m_MonthNamesMap[_T("lap")] = 11;
629
m_MonthNamesMap[_T("grd")] = 12;
631
//There are more languages and thus month
632
//names, but as long as nobody reports a
633
//problem, I won't add them, there are way
636
// Some servers send a combination of month name and number,
637
// Add corresponding numbers to the month names.
638
std::map<wxString, int> combo;
639
for (std::map<wxString, int>::iterator iter = m_MonthNamesMap.begin(); iter != m_MonthNamesMap.end(); ++iter)
641
// January could be 1 or 0, depends how the server counts
642
combo[wxString::Format(_T("%s%02d"), iter->first.c_str(), iter->second)] = iter->second;
643
combo[wxString::Format(_T("%s%02d"), iter->first.c_str(), iter->second - 1)] = iter->second;
644
if (iter->second < 10)
645
combo[wxString::Format(_T("%s%d"), iter->first.c_str(), iter->second)] = iter->second;
647
combo[wxString::Format(_T("%s%d"), iter->first.c_str(), iter->second % 10)] = iter->second;
648
if (iter->second <= 10)
649
combo[wxString::Format(_T("%s%d"), iter->first.c_str(), iter->second - 1)] = iter->second;
651
combo[wxString::Format(_T("%s%d"), iter->first.c_str(), (iter->second - 1) % 10)] = iter->second;
653
m_MonthNamesMap.insert(combo.begin(), combo.end());
655
m_MonthNamesMap[_T("1")] = 1;
656
m_MonthNamesMap[_T("2")] = 2;
657
m_MonthNamesMap[_T("3")] = 3;
658
m_MonthNamesMap[_T("4")] = 4;
659
m_MonthNamesMap[_T("5")] = 5;
660
m_MonthNamesMap[_T("6")] = 6;
661
m_MonthNamesMap[_T("7")] = 7;
662
m_MonthNamesMap[_T("8")] = 8;
663
m_MonthNamesMap[_T("9")] = 9;
664
m_MonthNamesMap[_T("10")] = 10;
665
m_MonthNamesMap[_T("11")] = 11;
666
m_MonthNamesMap[_T("12")] = 12;
670
for (unsigned int i = 0; data[i][0]; ++i)
672
unsigned int len = (unsigned int)strlen(data[i]);
673
char *pData = new char[len + 3];
674
strcpy(pData, data[i]);
675
strcat(pData, "\r\n");
676
AddData(pData, len + 2);
681
CDirectoryListingParser::~CDirectoryListingParser()
683
for (std::list<t_list>::iterator iter = m_DataList.begin(); iter != m_DataList.end(); ++iter)
689
bool CDirectoryListingParser::ParseData(bool partial)
692
CLine *pLine = GetLine(partial, error);
695
bool res = ParseLine(pLine, m_server.GetType(), false);
700
CLine* pConcatenatedLine = m_prevLine->Concat(pLine);
701
bool res = ParseLine(pConcatenatedLine, m_server.GetType(), true);
702
delete pConcatenatedLine;
722
pLine = GetLine(partial, error);
728
CDirectoryListing CDirectoryListingParser::Parse(const CServerPath &path)
730
CDirectoryListing listing;
732
listing.m_firstListTime = CTimeEx::Now();
734
if (!ParseData(false))
736
listing.m_failed = true;
740
if (!m_fileList.empty())
742
wxASSERT(m_entryList.empty());
744
listing.SetCount(m_fileList.size());
746
for (std::list<wxString>::const_iterator iter = m_fileList.begin(); iter != m_fileList.end(); ++iter, ++i)
757
listing.Assign(m_entryList);
763
bool CDirectoryListingParser::ParseLine(CLine *pLine, const enum ServerType serverType, bool concatenated)
769
if (serverType == ZVM)
771
res = ParseAsZVM(pLine, entry);
775
else if (serverType == HPNONSTOP)
777
res = ParseAsHPNonstop(pLine, entry);
782
ires = ParseAsMlsd(pLine, entry);
787
res = ParseAsUnix(pLine, entry, true); // Common 'ls -l'
790
res = ParseAsDos(pLine, entry);
793
res = ParseAsEplf(pLine, entry);
796
res = ParseAsVms(pLine, entry);
799
res = ParseOther(pLine, entry);
802
res = ParseAsIbm(pLine, entry);
805
res = ParseAsWfFtp(pLine, entry);
808
res = ParseAsIBM_MVS(pLine, entry);
811
res = ParseAsIBM_MVS_PDS(pLine, entry);
814
res = ParseAsOS9(pLine, entry);
817
#ifndef LISTDEBUG_MVS
818
if (serverType == MVS)
819
#endif //LISTDEBUG_MVS
821
res = ParseAsIBM_MVS_Migrated(pLine, entry);
824
res = ParseAsIBM_MVS_PDS2(pLine, entry);
827
res = ParseAsIBM_MVS_Tape(pLine, entry);
831
res = ParseAsUnix(pLine, entry, false); // 'ls -l' but without the date/time
835
// Some servers just send a list of filenames. If a line could not be parsed,
836
// check if it's a filename. If that's the case, store it for later, else clear
837
// list of stored files.
838
// If parsing finishes and no entries could be parsed and none of the lines
839
// contained a space, assume it's a raw filelisting.
844
if (!pLine->GetToken(0, token, true) || token.Find(' ') != -1)
846
m_maybeMultilineVms = false;
848
m_fileListOnly = false;
852
m_maybeMultilineVms = token.Find(';') != -1;
854
m_fileList.push_back(token.GetString());
858
m_maybeMultilineVms = false;
863
m_maybeMultilineVms = false;
865
m_fileListOnly = false;
868
if (entry.name == _T(".") || entry.name == _T(".."))
871
if (serverType == VMS && entry.is_dir())
873
// Trim version information from directories
874
int pos = entry.name.Find(';', true);
876
entry.name = entry.name.Left(pos);
880
int offset = m_server.GetTimezoneOffset();
881
if (offset && entry.has_time())
883
// Apply timezone offset
884
wxTimeSpan span(0, offset, 0, 0);
885
entry.time.Add(span);
889
m_entryList.push_back(entry);
892
m_maybeMultilineVms = false;
894
m_fileListOnly = false;
899
bool CDirectoryListingParser::ParseAsUnix(CLine *pLine, CDirentry &entry, bool expect_date)
903
if (!pLine->GetToken(index, token))
906
wxChar chr = token[0];
916
entry.permissions = token.GetString();
920
if (chr == 'd' || chr == 'l')
921
entry.flags |= CDirentry::flag_dir;
924
entry.flags |= CDirentry::flag_link;
926
// Check for netware servers, which split the permissions into two parts
927
bool netware = false;
928
if (token.GetLength() == 1)
930
if (!pLine->GetToken(++index, token))
932
entry.permissions += _T(" ") + token.GetString();
936
int numOwnerGroup = 3;
939
// Filter out link count, we don't need it
940
if (!pLine->GetToken(++index, token))
943
if (!token.IsNumeric())
947
// Repeat until numOwnerGroup is 0 since not all servers send every possible field
948
int startindex = index;
954
entry.ownerGroup.clear();
955
for (int i = 0; i < numOwnerGroup; ++i)
957
if (!pLine->GetToken(++index, token))
960
entry.ownerGroup += _T(" ");
961
entry.ownerGroup += token.GetString();
964
if (!pLine->GetToken(++index, token))
967
// Check for concatenated groupname and size fields
968
if (!ParseComplexFileSize(token, entry.size))
970
if (!token.IsRightNumeric())
972
entry.size = token.GetNumber();
975
// Append missing group to ownerGroup
976
if (!token.IsNumeric() && token.IsRightNumeric())
978
if (!entry.ownerGroup.IsEmpty())
979
entry.ownerGroup += _T(" ");
980
entry.ownerGroup += token.GetString(1);
985
entry.flags &= ~CDirentry::flag_timestamp_mask;
986
if (!ParseUnixDateTime(pLine, index, entry))
991
if (!pLine->GetToken(++index, token, 1))
994
entry.name = token.GetString();
996
// Filter out cpecial chars at the end of the filenames
997
chr = token[token.GetLength() - 1];
1001
entry.name.RemoveLast();
1003
if (entry.is_link())
1006
if ((pos = entry.name.Find(_T(" -> "))) != -1)
1008
entry.target = entry.name.Mid(pos + 4);
1009
entry.name = entry.name.Left(pos);
1013
if (entry.has_time())
1014
entry.time.Add(m_timezoneOffset);
1018
while (numOwnerGroup--);
1023
bool CDirectoryListingParser::ParseUnixDateTime(CLine *pLine, int &index, CDirentry &entry)
1025
bool mayHaveTime = true;
1026
bool bHasYearAndTime = false;
1027
bool hasTime = false;
1031
// Get the month date field
1033
if (!pLine->GetToken(++index, token))
1042
// Some servers use the following date formats:
1043
// 26-05 2002, 2002-10-14, 01-jun-99 or 2004.07.15
1044
// slashes instead of dashes are also possible
1045
int pos = token.Find(_T("-/."));
1048
int pos2 = token.Find(_T("-/."), pos + 1);
1051
if (token[pos] != '.')
1053
// something like 26-05 2002
1054
day = token.GetNumber(pos + 1, token.GetLength() - pos - 1).GetLo();
1055
if (day < 1 || day > 31)
1057
dateMonth = CToken(token.GetToken(), pos);
1062
else if (token[pos] != token[pos2])
1066
if (!ParseShortDate(token, entry))
1069
if (token[pos] == '.')
1072
year = entry.time.GetYear();
1073
month = entry.time.GetMonth() - wxDateTime::Jan + 1;
1074
day = entry.time.GetDay();
1077
else if (token.IsNumeric())
1079
if (token.GetNumber() > 1000 && token.GetNumber() < 10000)
1081
// Two possible variants:
1084
// assume first one.
1085
year = token.GetNumber().GetLo();
1086
if (!pLine->GetToken(++index, dateMonth))
1088
mayHaveTime = false;
1095
if (token.IsLeftNumeric() && (unsigned int)token[token.GetLength() - 1] > 127 &&
1096
token.GetNumber() > 1000)
1098
if (token.GetNumber() > 10000)
1101
// Asian date format: 2005xxx 5xx 20xxx with some non-ascii characters following
1102
year = token.GetNumber().GetLo();
1103
if (!pLine->GetToken(++index, dateMonth))
1105
mayHaveTime = false;
1114
if (!pLine->GetToken(++index, token))
1119
// Check for non-numeric day
1120
if (!token.IsNumeric() && !token.IsLeftNumeric())
1123
if (dateMonth.GetString().Right(1) == _T("."))
1125
if (!dateMonth.IsNumeric(0, dateMonth.GetLength() - offset))
1127
dateDay = dateMonth.GetNumber(0, dateMonth.GetLength() - offset).GetLo();
1132
dateDay = token.GetNumber().GetLo();
1133
if (token[token.GetLength() - 1] == ',')
1134
bHasYearAndTime = true;
1137
if (dateDay < 1 || dateDay > 31)
1144
wxString strMonth = dateMonth.GetString();
1145
if (dateMonth.IsLeftNumeric() && (unsigned int)strMonth[strMonth.Length() - 1] > 127)
1147
// Most likely an Asian server sending some unknown language specific
1148
// suffix at the end of the monthname. Filter it out.
1150
for (i = strMonth.Length() - 1; i > 0; --i)
1152
if (strMonth[i] >= '0' && strMonth[i] <= '9')
1155
strMonth = strMonth.Left(i + 1);
1158
while (strMonth.Right(1) == _T(",") || strMonth.Right(1) == _T("."))
1159
strMonth.RemoveLast();
1160
if (!GetMonthFromName(strMonth, month))
1164
// Get time/year field
1165
if (!pLine->GetToken(++index, token))
1168
pos = token.Find(_T(":.-"));
1169
if (pos != -1 && mayHaveTime)
1172
if (!pos || static_cast<size_t>(pos) == (token.GetLength() - 1))
1175
wxString str = token.GetString();
1176
if (!str.Left(pos).ToLong(&hour))
1178
if (!str.Mid(pos + 1).ToLong(&minute))
1181
if (hour < 0 || hour > 23)
1183
if (minute < 0 || minute > 59)
1188
// Some servers use times only for files newer than 6 months
1191
year = wxDateTime::Now().GetYear();
1192
int currentDayOfYear = wxDateTime::Now().GetDay() + 31 * (wxDateTime::Now().GetMonth() - wxDateTime::Jan);
1193
int fileDayOfYear = (month - 1) * 31 + day;
1195
// We have to compare with an offset of one. In the worst case,
1196
// the server's timezone might be up to 24 hours ahead of the
1198
// Problem: Servers which do send the time but not the year even
1199
// one day away from getting 1 year old. This is far more uncommon
1201
if ((currentDayOfYear + 1) < fileDayOfYear)
1208
if (!token.IsNumeric() && !token.IsLeftNumeric())
1211
year = token.GetNumber().GetLo();
1218
if (bHasYearAndTime)
1220
if (!pLine->GetToken(++index, token))
1223
if (token.Find(':') == 2 && token.GetLength() == 5 && token.IsLeftNumeric() && token.IsRightNumeric())
1225
int pos = token.Find(':');
1227
if (!pos || static_cast<size_t>(pos) == (token.GetLength() - 1))
1230
wxString str = token.GetString();
1232
if (!str.Left(pos).ToLong(&hour))
1234
if (!str.Mid(pos + 1).ToLong(&minute))
1237
if (hour < 0 || hour > 23)
1239
if (minute < 0 || minute > 59)
1251
entry.time = wxDateTime();
1252
if (!VerifySetDate(entry.time, year, (wxDateTime::Month)(wxDateTime::Jan + month - 1), day, hour, minute))
1255
entry.flags |= CDirentry::flag_timestamp_date;
1257
entry.flags |= CDirentry::flag_timestamp_time;
1262
bool CDirectoryListingParser::ParseShortDate(CToken &token, CDirentry &entry, bool saneFieldOrder /*=false*/)
1264
if (token.GetLength() < 1)
1267
bool gotYear = false;
1268
bool gotMonth = false;
1269
bool gotDay = false;
1270
bool gotMonthName = false;
1278
int pos = token.Find(_T("-./"));
1282
if (!token.IsNumeric(0, pos))
1284
// Seems to be monthname-dd-yy
1287
wxString dateMonth = token.GetString().Mid(0, pos);
1288
if (!GetMonthFromName(dateMonth, month))
1291
gotMonthName = true;
1295
// Seems to be yyyy-mm-dd
1296
year = token.GetNumber(0, pos).GetLo();
1297
if (year < 1900 || year > 3000)
1303
wxLongLong value = token.GetNumber(0, pos);
1304
if (token[pos] == '.')
1307
if (value < 1 || value > 31)
1309
day = value.GetLo();
1316
year = value.GetLo();
1325
// Detect mm-dd-yyyy or mm/dd/yyyy and
1326
// dd-mm-yyyy or dd/mm/yyyy
1334
day = value.GetLo();
1339
month = value.GetLo();
1348
int pos2 = token.Find(_T("-./"), pos + 1);
1349
if (pos2 == -1 || (pos2 - pos) == 1)
1351
if (static_cast<size_t>(pos2) == (token.GetLength() - 1))
1354
// If we already got the month and the second field is not numeric,
1355
// change old month into day and use new token as month
1356
if (!token.IsNumeric(pos + 1, pos2 - pos - 1) && gotMonth)
1369
if (gotYear || gotDay)
1371
// Month field in yyyy-mm-dd or dd-mm-yyyy
1373
wxString dateMonth = token.GetString().Mid(pos + 1, pos2 - pos - 1);
1374
if (!GetMonthFromName(dateMonth, month))
1380
wxLongLong value = token.GetNumber(pos + 1, pos2 - pos - 1);
1381
// Day field in mm-dd-yyyy
1382
if (value < 1 || value > 31)
1384
day = value.GetLo();
1388
value = token.GetNumber(pos2 + 1, token.GetLength() - pos2 - 1).GetLo();
1391
// Day field in yyy-mm-dd
1392
if (!value || value > 31)
1404
else if (value < 1000)
1411
if (!gotMonth || !gotDay || !gotYear)
1414
entry.time = wxDateTime();
1415
if (!VerifySetDate(entry.time, year, (wxDateTime::Month)(wxDateTime::Jan + month - 1), day))
1417
entry.flags |= CDirentry::flag_timestamp_date;
1422
bool CDirectoryListingParser::ParseAsDos(CLine *pLine, CDirentry &entry)
1427
// Get first token, has to be a valid date
1428
if (!pLine->GetToken(index, token))
1433
if (!ParseShortDate(token, entry))
1437
if (!pLine->GetToken(++index, token))
1440
if (!ParseTime(token, entry))
1443
// If next token is <DIR>, entry is a directory
1444
// else, it should be the filesize.
1445
if (!pLine->GetToken(++index, token))
1448
if (token.GetString() == _T("<DIR>"))
1450
entry.flags |= CDirentry::flag_dir;
1453
else if (token.IsNumeric() || token.IsLeftNumeric())
1455
// Convert size, filter out separators
1456
wxLongLong size = 0;
1457
int len = token.GetLength();
1458
for (int i = 0; i < len; ++i)
1460
char chr = token[i];
1461
if (chr == ',' || chr == '.')
1463
if (chr < '0' || chr > '9')
1475
if (!pLine->GetToken(++index, token, true))
1477
entry.name = token.GetString();
1479
entry.target = _T("");
1480
entry.ownerGroup = _T("");
1481
entry.permissions = _T("");
1483
if (entry.has_time())
1484
entry.time.Add(m_timezoneOffset);
1489
bool CDirectoryListingParser::ParseTime(CToken &token, CDirentry &entry)
1491
if (!entry.has_date())
1494
int pos = token.Find(':');
1495
if (pos < 1 || static_cast<unsigned int>(pos) >= (token.GetLength() - 1))
1498
wxLongLong hour = token.GetNumber(0, pos);
1499
if (hour < 0 || hour > 23)
1502
// See if we got seconds
1503
int pos2 = token.Find(':', pos + 1);
1508
len = pos2 - pos - 1;
1513
wxLongLong minute = token.GetNumber(pos + 1, len);
1514
if (minute < 0 || minute > 59)
1518
bool hasSeconds = false;
1524
seconds = token.GetNumber(pos2 + 1, -1);
1525
if (seconds < 0 || seconds > 59)
1530
// Convert to 24h format
1531
if (!token.IsRightNumeric())
1533
if (token[token.GetLength() - 2] == 'P')
1543
wxTimeSpan span(hour.GetLo(), minute.GetLo(), seconds.GetLo());
1544
entry.time.Add(span);
1546
entry.flags |= CDirentry::flag_timestamp_time;
1548
entry.flags |= CDirentry::flag_timestamp_seconds;
1553
bool CDirectoryListingParser::ParseAsEplf(CLine *pLine, CDirentry &entry)
1556
if (!pLine->GetToken(0, token, true))
1559
if (token[0] != '+')
1562
int pos = token.Find('\t');
1563
if (pos == -1 || static_cast<size_t>(pos) == (token.GetLength() - 1))
1566
entry.name = token.GetString().Mid(pos + 1);
1569
entry.ownerGroup = _T("");
1570
entry.permissions = _T("");
1576
int separator = token.Find(',', fact);
1578
if (separator == -1)
1581
len = separator - fact;
1589
char type = token[fact];
1592
entry.flags |= CDirentry::flag_dir;
1593
else if (type == 's')
1594
entry.size = token.GetNumber(fact + 1, len - 1);
1595
else if (type == 'm')
1597
wxLongLong number = token.GetNumber(fact + 1, len - 1);
1600
entry.time = wxDateTime((time_t)number.GetValue());
1602
entry.flags |= CDirentry::flag_timestamp_date | CDirentry::flag_timestamp_time | CDirentry::flag_timestamp_seconds;
1604
else if (type == 'u' && len > 2 && token[fact + 1] == 'p')
1605
entry.permissions = token.GetString().Mid(fact + 2, len - 2);
1613
wxString Unescape(const wxString& str, wxChar escape)
1616
for (unsigned int i = 0; i < str.Len(); ++i)
1631
bool CDirectoryListingParser::ParseAsVms(CLine *pLine, CDirentry &entry)
1636
if (!pLine->GetToken(index, token))
1639
int pos = token.Find(';');
1645
if (pos > 4 && token.GetString().Mid(pos - 4, 4) == _T(".DIR"))
1647
entry.flags |= CDirentry::flag_dir;
1648
if (token.GetString().Mid(pos) == _T(";1"))
1649
entry.name = token.GetString().Left(pos - 4);
1651
entry.name = token.GetString().Left(pos - 4) + token.GetString().Mid(pos);
1654
entry.name = token.GetString();
1656
// Some VMS servers escape special characters like additional dots with ^
1657
entry.name = Unescape(entry.name, '^');
1659
if (!pLine->GetToken(++index, token))
1662
entry.ownerGroup = _T("");
1664
// This field can either be the filesize, a username (at least that's what I think) enclosed in [] or a date.
1665
if (!token.IsNumeric() && !token.IsLeftNumeric())
1669
const int len = token.GetLength();
1670
if (len < 3 || token[0] != '[' || token[len - 1] != ']')
1672
entry.ownerGroup = token.GetString().Mid(1, len - 2);
1674
if (!pLine->GetToken(++index, token))
1676
if (!token.IsNumeric() && !token.IsLeftNumeric())
1680
// Current token is either size or date
1681
bool gotSize = false;
1682
pos = token.Find('/');
1687
if (token.IsNumeric() || (pos != -1 && token.Find('/', pos + 1) == -1))
1695
sizeToken = CToken(token.GetToken(), pos);
1696
if (!ParseComplexFileSize(sizeToken, entry.size, 512))
1700
if (!pLine->GetToken(++index, token))
1703
else if (pos == -1 && token.IsLeftNumeric())
1710
sizeToken = CToken(token.GetToken(), pos);
1711
if (ParseComplexFileSize(sizeToken, entry.size, 512))
1715
if (!pLine->GetToken(++index, token))
1721
if (!ParseShortDate(token, entry))
1725
if (!pLine->GetToken(++index, token))
1728
if (!ParseTime(token, entry))
1730
int len = token.GetLength();
1731
if (token[0] == '[' && token[len - 1] != ']')
1733
if (token[0] == '(' && token[len - 1] != ')')
1735
if (token[0] != '[' && token[len - 1] == ']')
1737
if (token[0] != '(' && token[len - 1] == ')')
1745
if (!pLine->GetToken(++index, token))
1748
if (!token.IsNumeric() && !token.IsLeftNumeric())
1751
int pos = token.Find('/');
1759
sizeToken = CToken(token.GetToken(), pos);
1760
if (!ParseComplexFileSize(sizeToken, entry.size, 512))
1764
// Owner / group and permissions
1765
entry.permissions = _T("");
1766
while (pLine->GetToken(++index, token))
1768
const int len = token.GetLength();
1769
if (len > 2 && token[0] == '(' && token[len - 1] == ')')
1771
if (!entry.permissions.IsEmpty())
1772
entry.permissions += _T(" ");
1773
entry.permissions += token.GetString().Mid(1, len - 2);
1775
else if (len > 2 && token[0] == '[' && token[len - 1] == ']')
1777
if (!entry.ownerGroup.IsEmpty())
1778
entry.ownerGroup += _T(" ");
1779
entry.ownerGroup += token.GetString().Mid(1, len - 2);
1783
if (!entry.permissions.IsEmpty())
1784
entry.permissions += _T(" ");
1785
entry.ownerGroup += token.GetString();
1789
if (entry.has_time())
1790
entry.time.Add(m_timezoneOffset);
1795
bool CDirectoryListingParser::ParseAsIbm(CLine *pLine, CDirentry &entry)
1801
if (!pLine->GetToken(index, token))
1804
entry.ownerGroup = token.GetString();
1807
if (!pLine->GetToken(++index, token))
1810
if (!token.IsNumeric())
1813
entry.size = token.GetNumber();
1816
if (!pLine->GetToken(++index, token))
1821
if (!ParseShortDate(token, entry))
1825
if (!pLine->GetToken(++index, token))
1828
if (!ParseTime(token, entry))
1832
if (!pLine->GetToken(index + 2, token, 1))
1835
entry.name = token.GetString();
1836
if (token[token.GetLength() - 1] == '/')
1838
entry.name.RemoveLast();
1839
entry.flags |= CDirentry::flag_dir;
1842
if (entry.has_time())
1843
entry.time.Add(m_timezoneOffset);
1848
bool CDirectoryListingParser::ParseOther(CLine *pLine, CDirentry &entry)
1853
if (!pLine->GetToken(index, firstToken))
1856
if (!firstToken.IsNumeric())
1859
// Possible formats: Numerical unix, VShell or OS/2
1862
if (!pLine->GetToken(++index, token))
1867
// If token is a number, than it's the numerical Unix style format,
1868
// else it's the VShell, OS/2 or nortel.VxWorks format
1869
if (token.IsNumeric())
1871
entry.permissions = firstToken.GetString();
1872
if (firstToken.GetLength() >= 2 && firstToken[1] == '4')
1873
entry.flags |= CDirentry::flag_dir;
1875
entry.ownerGroup += token.GetString();
1877
if (!pLine->GetToken(++index, token))
1880
entry.ownerGroup += _T(" ") + token.GetString();
1883
if (!pLine->GetToken(++index, token))
1886
if (!token.IsNumeric())
1889
entry.size = token.GetNumber();
1892
if (!pLine->GetToken(++index, token))
1895
wxLongLong number = token.GetNumber();
1898
entry.time = wxDateTime((time_t)number.GetValue());
1900
entry.flags |= CDirentry::flag_timestamp_date | CDirentry::flag_timestamp_time | CDirentry::flag_timestamp_seconds;
1903
if (!pLine->GetToken(++index, token, true))
1906
entry.name = token.GetString();
1908
entry.target = _T("");
1912
// Possible conflict with multiline VMS listings
1913
if (m_maybeMultilineVms)
1916
// VShell, OS/2 or nortel.VxWorks style format
1917
entry.size = firstToken.GetNumber();
1920
wxString dateMonth = token.GetString();
1922
if (!GetMonthFromName(dateMonth, month))
1924
// OS/2 or nortel.VxWorks
1925
int skippedCount = 0;
1928
if (token.GetString() == _T("DIR"))
1929
entry.flags |= CDirentry::flag_dir;
1930
else if (token.Find(_T("-/.")) != -1)
1935
if (!pLine->GetToken(++index, token))
1939
if (!ParseShortDate(token, entry))
1943
if (!pLine->GetToken(++index, token))
1946
if (!ParseTime(token, entry))
1950
if (!pLine->GetToken(++index, token, true))
1953
entry.name = token.GetString();
1954
wxString type = entry.name.Right(5);
1955
MakeLowerAscii(type);
1956
if (!skippedCount && type == _T("<dir>"))
1958
entry.flags |= CDirentry::flag_dir;
1959
entry.name = entry.name.Left(entry.name.Length() - 5);
1960
while (entry.name.Last() == ' ')
1961
entry.name.RemoveLast();
1967
if (!pLine->GetToken(++index, token))
1970
if (!token.IsNumeric() && !token.IsLeftNumeric())
1973
wxLongLong day = token.GetNumber();
1974
if (day < 0 || day > 31)
1978
if (!pLine->GetToken(++index, token))
1981
if (!token.IsNumeric())
1984
wxLongLong year = token.GetNumber();
1987
else if (year < 1000)
1990
entry.time = wxDateTime();
1991
if (!VerifySetDate(entry.time, year.GetLo(), (wxDateTime::Month)(month - 1), day.GetLo()))
1994
entry.flags |= CDirentry::flag_timestamp_date;
1997
if (!pLine->GetToken(++index, token))
2000
if (!ParseTime(token, entry))
2004
if (!pLine->GetToken(++index, token, 1))
2007
entry.name = token.GetString();
2008
char chr = token[token.GetLength() - 1];
2009
if (chr == '/' || chr == '\\')
2011
entry.flags |= CDirentry::flag_dir;
2012
entry.name.RemoveLast();
2015
entry.target = _T("");
2016
entry.ownerGroup = _T("");
2017
entry.permissions = _T("");
2019
if (entry.has_time())
2020
entry.time.Add(m_timezoneOffset);
2026
bool CDirectoryListingParser::AddData(char *pData, int len)
2032
m_DataList.push_back(item);
2034
return ParseData(true);
2037
bool CDirectoryListingParser::AddLine(const wxChar* pLine)
2039
if (m_pControlSocket)
2040
m_pControlSocket->LogMessageRaw(RawList, pLine);
2042
while (*pLine == ' ' || *pLine == '\t')
2048
const int len = wxStrlen(pLine);
2050
wxChar* p = new wxChar[len + 1];
2056
ParseLine(&line, m_server.GetType(), false);
2061
CLine *CDirectoryListingParser::GetLine(bool breakAtEnd /*=false*/, bool &error)
2063
while (!m_DataList.empty())
2065
// Trim empty lines and spaces
2066
std::list<t_list>::iterator iter = m_DataList.begin();
2067
int len = iter->len;
2068
while (iter->p[m_currentOffset]=='\r' || iter->p[m_currentOffset]=='\n' || iter->p[m_currentOffset]==' ' || iter->p[m_currentOffset]=='\t')
2071
if (m_currentOffset >= len)
2075
m_currentOffset = 0;
2076
if (iter == m_DataList.end())
2084
m_DataList.erase(m_DataList.begin(), iter);
2085
iter = m_DataList.begin();
2087
// Remember start offset and find next linebreak
2088
int startpos = m_currentOffset;
2093
int currentOffset = m_currentOffset;
2094
while ((iter->p[currentOffset] != '\n') && (iter->p[currentOffset] != '\r'))
2096
if (iter->p[currentOffset] == ' ' || iter->p[currentOffset] == '\t')
2103
if (currentOffset >= len)
2106
if (iter == m_DataList.end())
2110
m_pControlSocket->LogMessage(::Error, _("Received a line exceeding 10000 characters, aborting."));
2125
m_pControlSocket->LogMessage(::Error, _("Received a line exceeding 10000 characters, aborting."));
2129
m_currentOffset = currentOffset;
2131
// Reslen is now the length of the line, including any terminating whitespace
2132
char *res = new char[reslen + 1];
2138
std::list<t_list>::iterator i = m_DataList.begin();
2139
while (i != iter && reslen)
2141
int copylen = i->len - startpos;
2142
if (copylen > reslen)
2144
memcpy(&res[respos], &i->p[startpos], copylen);
2146
respos += i->len - startpos;
2154
if (iter != m_DataList.end() && reslen)
2156
int copylen = m_currentOffset-startpos;
2157
if (copylen > reslen)
2159
memcpy(&res[respos], &iter->p[startpos], copylen);
2160
if (reslen >= iter->len)
2163
m_DataList.erase(m_DataList.begin(), ++iter);
2166
m_DataList.erase(m_DataList.begin(), iter);
2169
m_DataList.erase(m_DataList.begin(), iter);
2172
if (m_pControlSocket)
2174
buffer = m_pControlSocket->ConvToLocalBuffer(res);
2175
m_pControlSocket->LogMessageRaw(RawList, buffer);
2179
wxString str(res, wxConvUTF8);
2182
str = wxString(res, wxConvLocal);
2184
str = wxString(res, wxConvISO8859_1);
2186
buffer = new wxChar[str.Len() + 1];
2187
wxStrcpy(buffer, str.c_str());
2193
// Line contained no usable data, start over
2197
return new CLine(buffer, -1, emptylen);
2203
bool CDirectoryListingParser::ParseAsWfFtp(CLine *pLine, CDirentry &entry)
2209
if (!pLine->GetToken(index++, token))
2212
entry.name = token.GetString();
2215
if (!pLine->GetToken(index++, token))
2218
if (!token.IsNumeric())
2221
entry.size = token.GetNumber();
2226
if (!pLine->GetToken(index++, token))
2229
if (!ParseShortDate(token, entry))
2233
if (!pLine->GetToken(index++, token))
2236
if (token.GetString().Right(1) != _T("."))
2240
if (!pLine->GetToken(index++, token, true))
2243
if (!ParseTime(token, entry))
2246
entry.ownerGroup = _T("");
2247
entry.permissions = _T("");
2249
if (entry.has_time())
2250
entry.time.Add(m_timezoneOffset);
2255
bool CDirectoryListingParser::ParseAsIBM_MVS(CLine *pLine, CDirentry &entry)
2261
if (!pLine->GetToken(index++, token))
2265
if (!pLine->GetToken(index++, token))
2269
if (!pLine->GetToken(index++, token))
2273
if (token.GetString() != _T("**NONE**") && !ParseShortDate(token, entry))
2275
// Perhaps of the following type:
2276
// TSO004 3390 VSAM FOO.BAR
2277
if (token.GetString() != _T("VSAM"))
2280
if (!pLine->GetToken(index++, token))
2283
entry.name = token.GetString();
2284
if (entry.name.Find(' ') != -1)
2288
entry.ownerGroup = _T("");
2289
entry.permissions = _T("");
2295
if (!pLine->GetToken(index++, token))
2297
if (!token.IsNumeric())
2300
int prevLen = token.GetLength();
2303
if (!pLine->GetToken(index++, token))
2305
if (token.IsNumeric() || token.GetString() == _T("????") || token.GetString() == _T("++++") )
2308
if (!pLine->GetToken(index++, token))
2310
if (token.IsNumeric())
2320
if (!pLine->GetToken(index++, token))
2322
if (!token.IsNumeric())
2326
if (!pLine->GetToken(index++, token))
2328
if (!token.IsNumeric())
2332
if (!pLine->GetToken(index++, token))
2335
if (token.GetString() == _T("PO") || token.GetString() == _T("PO-E"))
2337
entry.flags |= CDirentry::flag_dir;
2343
// name of dataset or sequential file
2344
if (!pLine->GetToken(index++, token, true))
2347
entry.name = token.GetString();
2349
entry.ownerGroup = _T("");
2350
entry.permissions = _T("");
2355
bool CDirectoryListingParser::ParseAsIBM_MVS_PDS(CLine *pLine, CDirentry &entry)
2361
if (!pLine->GetToken(index++, token))
2363
entry.name = token.GetString();
2366
if (!pLine->GetToken(index++, token))
2372
if (!pLine->GetToken(index++, token))
2374
if (!ParseShortDate(token, entry))
2377
// modification date
2378
if (!pLine->GetToken(index++, token))
2380
if (!ParseShortDate(token, entry))
2383
// modification time
2384
if (!pLine->GetToken(index++, token))
2386
if (!ParseTime(token, entry))
2390
if (!pLine->GetToken(index++, token))
2392
if (!token.IsNumeric())
2394
entry.size = token.GetNumber();
2397
if (!pLine->GetToken(index++, token))
2399
if (!token.IsNumeric())
2403
if (!pLine->GetToken(index++, token))
2405
if (!token.IsNumeric())
2409
if (!pLine->GetToken(index++, token, true))
2412
entry.ownerGroup = _T("");
2413
entry.permissions = _T("");
2415
if (entry.has_time())
2416
entry.time.Add(m_timezoneOffset);
2421
bool CDirectoryListingParser::ParseAsIBM_MVS_Migrated(CLine *pLine, CDirentry &entry)
2423
// Migrated MVS file
2424
// "Migrated SOME.NAME"
2428
if (!pLine->GetToken(index, token))
2431
if (token.GetString().CmpNoCase(_T("Migrated")))
2434
if (!pLine->GetToken(++index, token))
2437
entry.name = token.GetString();
2440
entry.ownerGroup = _T("");
2441
entry.permissions = _T("");
2444
if (pLine->GetToken(++index, token))
2450
bool CDirectoryListingParser::ParseAsIBM_MVS_PDS2(CLine *pLine, CDirentry &entry)
2454
if (!pLine->GetToken(index, token))
2457
entry.name = token.GetString();
2460
entry.ownerGroup = _T("");
2461
entry.permissions = _T("");
2464
if (!pLine->GetToken(++index, token))
2467
entry.size = token.GetNumber(CToken::hex);
2468
if (entry.size == -1)
2471
// Unused hexadecimal token
2472
if (!pLine->GetToken(++index, token))
2474
if (!token.IsNumeric(CToken::hex))
2477
// Unused numeric token
2478
if (!pLine->GetToken(++index, token))
2480
if (!token.IsNumeric())
2483
int start = ++index;
2484
while (pLine->GetToken(index, token))
2488
if ((index - start < 2))
2492
pLine->GetToken(index, token);
2493
if (!token.IsNumeric() && (token.GetString() != _T("ANY")))
2496
pLine->GetToken(index - 1, token);
2497
if (!token.IsNumeric() && (token.GetString() != _T("ANY")))
2500
for (int i = start; i < index - 1; ++i)
2502
pLine->GetToken(i, token);
2503
int len = token.GetLength();
2504
for (int j = 0; j < len; ++j)
2505
if (token[j] < 'A' || token[j] > 'Z')
2512
bool CDirectoryListingParser::ParseAsIBM_MVS_Tape(CLine *pLine, CDirentry &entry)
2518
if (!pLine->GetToken(index++, token))
2522
if (!pLine->GetToken(index++, token))
2525
if (token.GetString().CmpNoCase(_T("Tape")))
2529
if (!pLine->GetToken(index++, token))
2532
entry.name = token.GetString();
2534
entry.ownerGroup = _T("");
2535
entry.permissions = _T("");
2538
if (pLine->GetToken(index++, token))
2544
bool CDirectoryListingParser::ParseComplexFileSize(CToken& token, wxLongLong& size, int blocksize /*=-1*/)
2546
if (token.IsNumeric())
2548
size = token.GetNumber();
2549
if (blocksize != -1)
2555
int len = token.GetLength();
2557
char last = token[len - 1];
2558
if (last == 'B' || last == 'b')
2563
char c = token[--len - 1];
2564
if (c < '0' || c > '9')
2572
else if (last >= '0' && last <= '9')
2583
for (int i = 0; i < len; ++i)
2586
if (c >= '0' && c <= '9')
2608
size *= 1024 * 1024;
2612
size *= 1024 * 1024 * 1024;
2616
size *= 1024 * 1024;
2617
size *= 1024 * 1024;
2623
if (blocksize != -1)
2635
int CDirectoryListingParser::ParseAsMlsd(CLine *pLine, CDirentry &entry)
2637
// MLSD format as described here: http://www.ietf.org/internet-drafts/draft-ietf-ftpext-mlst-16.txt
2639
// Parsing is done strict, abort on slightest error.
2643
if (!pLine->GetToken(0, token))
2646
wxString facts = token.GetString();
2647
if (facts.IsEmpty())
2652
entry.ownerGroup = _T("");
2653
entry.permissions = _T("");
2655
wxString owner, group, uid, gid;
2657
while (!facts.IsEmpty())
2659
int delim = facts.Find(';');
2665
delim = facts.Len();
2668
int pos = facts.Find('=');
2669
if (pos < 1 || pos > delim)
2672
wxString factname = facts.Left(pos);
2673
MakeLowerAscii(factname);
2674
wxString value = facts.Mid(pos + 1, delim - pos - 1);
2675
if (factname == _T("type"))
2677
if (!value.CmpNoCase(_T("dir")))
2678
entry.flags |= CDirentry::flag_dir;
2679
else if (!value.Left(13).CmpNoCase(_T("OS.unix=slink")))
2681
entry.flags |= CDirentry::flag_dir | CDirentry::flag_link;
2682
if (value[13] == ':' && value[14] != 0)
2683
entry.target = value.Mid(14);
2685
else if (!value.CmpNoCase(_T("cdir")) ||
2686
!value.CmpNoCase(_T("pdir")))
2689
else if (factname == _T("size"))
2693
for (unsigned int i = 0; i < value.Len(); ++i)
2695
if (value[i] < '0' || value[i] > '9')
2698
entry.size += value[i] - '0';
2701
else if (factname == _T("modify") ||
2702
(!entry.has_date() && factname == _T("create")))
2704
wxDateTime dateTime;
2705
const wxChar* time = dateTime.ParseFormat(value, _T("%Y%m%d"));
2712
if (!dateTime.ParseFormat(time, _T("%H%M%S"), dateTime))
2714
entry.flags |= CDirentry::flag_timestamp_date | CDirentry::flag_timestamp_time | CDirentry::flag_timestamp_seconds;
2717
entry.flags |= CDirentry::flag_timestamp_date;
2719
entry.time = dateTime.FromTimezone(wxDateTime::GMT0);
2721
else if (factname == _T("perm"))
2725
if (!entry.permissions.empty())
2726
entry.permissions = value + _T(" (") + entry.permissions + _T(")");
2728
entry.permissions = value;
2731
else if (factname == _T("unix.mode"))
2733
if (!entry.permissions.empty())
2734
entry.permissions = entry.permissions + _T(" (") + value + _T(")");
2736
entry.permissions = value;
2738
else if (factname == _T("unix.owner") || factname == _T("unix.user"))
2740
else if (factname == _T("unix.group"))
2742
else if (factname == _T("unix.uid"))
2744
else if (factname == _T("unix.gid"))
2747
facts = facts.Mid(delim + 1);
2750
// The order of the facts is undefined, so assemble ownerGroup in correct
2753
entry.ownerGroup += owner;
2754
else if (!uid.empty())
2755
entry.ownerGroup += uid;
2757
entry.ownerGroup += _T(" ") + group;
2758
else if (!gid.empty())
2759
entry.ownerGroup += _T(" ") + gid;
2761
if (!pLine->GetToken(1, token, true, true))
2764
entry.name = token.GetString();
2769
bool CDirectoryListingParser::ParseAsOS9(CLine *pLine, CDirentry &entry)
2775
if (!pLine->GetToken(index++, token))
2778
// Make sure it's number.number
2779
int pos = token.Find('.');
2780
if (pos == -1 || !pos || pos == ((int)token.GetLength() - 1))
2783
if (!token.IsNumeric(0, pos))
2786
if (!token.IsNumeric(pos + 1, token.GetLength() - pos - 1))
2789
entry.ownerGroup = token.GetString();
2794
if (!pLine->GetToken(index++, token))
2797
if (!ParseShortDate(token, entry, true))
2801
if (!pLine->GetToken(index++, token))
2805
if (!pLine->GetToken(index++, token))
2808
entry.permissions = token.GetString();
2810
if (token[0] == 'd')
2811
entry.flags |= CDirentry::flag_dir;
2814
if (!pLine->GetToken(index++, token))
2818
if (!pLine->GetToken(index++, token))
2821
if (!token.IsNumeric())
2824
entry.size = token.GetNumber();
2827
if (!pLine->GetToken(index++, token, true))
2830
entry.name = token.GetString();
2835
void CDirectoryListingParser::Reset()
2837
for (std::list<t_list>::iterator iter = m_DataList.begin(); iter != m_DataList.end(); ++iter)
2844
m_entryList.clear();
2846
m_currentOffset = 0;
2847
m_fileListOnly = true;
2848
m_maybeMultilineVms = false;
2851
bool CDirectoryListingParser::ParseAsZVM(CLine* pLine, CDirentry &entry)
2857
if (!pLine->GetToken(index, token))
2860
entry.name = token.GetString();
2862
// Get filename extension
2863
if (!pLine->GetToken(++index, token))
2865
entry.name += _T(".") + token.GetString();
2867
// File format. Unused
2868
if (!pLine->GetToken(++index, token))
2870
wxString format = token.GetString();
2871
if (format != _T("V") && format != _T("F"))
2875
if (!pLine->GetToken(++index, token))
2878
if (!token.IsNumeric())
2881
entry.size = token.GetNumber();
2883
// Number of records
2884
if (!pLine->GetToken(++index, token))
2887
if (!token.IsNumeric())
2890
entry.size *= token.GetNumber();
2892
// Unused (Block size?)
2893
if (!pLine->GetToken(++index, token))
2896
if (!token.IsNumeric())
2902
if (!pLine->GetToken(++index, token))
2905
if (!ParseShortDate(token, entry, true))
2909
if (!pLine->GetToken(++index, token))
2912
if (!ParseTime(token, entry))
2916
if (!pLine->GetToken(++index, token))
2919
entry.ownerGroup = token.GetString();
2921
// No further token!
2922
if (pLine->GetToken(++index, token))
2925
entry.permissions = _T("");
2926
entry.target = _T("");
2928
if (entry.has_time())
2929
entry.time.Add(m_timezoneOffset);
2934
bool CDirectoryListingParser::ParseAsHPNonstop(CLine *pLine, CDirentry &entry)
2940
if (!pLine->GetToken(index, token))
2943
entry.name = token.GetString();
2945
// File code, numeric, unsuded
2946
if (!pLine->GetToken(++index, token))
2948
if (!token.IsNumeric())
2952
if (!pLine->GetToken(++index, token))
2954
if (!token.IsNumeric())
2957
entry.size = token.GetNumber();
2962
if (!pLine->GetToken(++index, token))
2964
if (!ParseShortDate(token, entry, false))
2968
if (!pLine->GetToken(++index, token))
2970
if (!ParseTime(token, entry))
2974
if (!pLine->GetToken(++index, token))
2976
entry.ownerGroup = token.GetString();
2978
if (token[token.GetLength() - 1] == ',')
2981
if (!pLine->GetToken(++index, token))
2983
entry.ownerGroup += _T(" ") + token.GetString();
2987
if (!pLine->GetToken(++index, token))
2989
entry.permissions = token.GetString();
2992
if (pLine->GetToken(++index, token))
2998
bool CDirectoryListingParser::GetMonthFromName(const wxString& name, int &month)
3000
std::map<wxString, int>::iterator iter = m_MonthNamesMap.find(name.Lower());
3001
if (iter == m_MonthNamesMap.end())
3003
wxString lower(name);
3004
MakeLowerAscii(lower);
3005
iter = m_MonthNamesMap.find(lower);
3006
if (iter == m_MonthNamesMap.end())
3010
month = iter->second;