source: filezilla/trunk/fuentes/src/engine/serverpath.cpp @ 130

Last change on this file since 130 was 130, checked in by jrpelegrina, 3 years ago

First release to xenial

File size: 21.3 KB
Line 
1#include <filezilla.h>
2#include "serverpath.h"
3
4#define FTP_MVS_DOUBLE_QUOTE (wxChar)0xDC
5
6struct CServerTypeTraits
7{
8        const wxChar* separators;
9        bool has_root; // Root = simply separator nothing else
10        wxChar left_enclosure; // Example: VMS paths: [FOO.BAR]
11        wxChar right_enclosure;
12        bool filename_inside_enclosure; // MVS
13        int prefixmode; //0 = normal prefix, 1 = suffix
14        wxChar separatorEscape;
15        bool has_dots; // Special meaning for .. (parent) and . (self)
16        bool separator_after_prefix;
17};
18
19static const CServerTypeTraits traits[SERVERTYPE_MAX] = {
20        { _T("/"),   true,     0,    0,    false, 0, 0,   true,  false }, // Failsafe
21        { _T("/"),   true,     0,    0,    false, 0, 0,   true,  false },
22        { _T("."),   false,  '[',  ']',    false, 0, '^', false, false },
23        { _T("\\/"), false,    0,    0,    false, 0, 0,   true,  false },
24        { _T("."),   false, '\'', '\'',     true, 1, 0,   false, false },
25        { _T("/"),   true,     0,    0,    false, 0, 0,   true,  false },
26        { _T("/"),   true,     0,    0,    false, 0, 0,   true,  false }, // Same as Unix
27        { _T("."),   false,    0,    0,    false, 0, 0,   false, false },
28        { _T("\\"),  true,     0,    0,    false, 0, 0,   true,  false },
29        { _T("/"),   true,     0,    0,    false, 0, 0,   true,  true  }  // Cygwin is like Unix but has optional prefix of form "//server"
30};
31
32bool CServerPathData::operator==(const CServerPathData& cmp) const
33{
34        if (m_prefix != cmp.m_prefix)
35                return false;
36
37        if (m_segments != cmp.m_segments)
38                return false;
39
40        return true;
41}
42
43CServerPath::CServerPath()
44        : m_type(DEFAULT)
45{
46}
47
48CServerPath::CServerPath(CServerPath const& path, wxString subdir)
49        : m_type(path.m_type)
50        , m_data(path.m_data)
51{
52        if (subdir.empty())
53                return;
54
55        if (!ChangePath(subdir))
56                clear();
57}
58
59CServerPath::CServerPath(wxString const& path, ServerType type /*=DEFAULT*/)
60        : m_type(type)
61{
62        SetPath(path);
63}
64
65void CServerPath::clear()
66{
67        m_data.clear();
68}
69
70bool CServerPath::SetPath(wxString newPath)
71{
72        return SetPath(newPath, false);
73}
74
75bool CServerPath::SetPath(wxString &newPath, bool isFile)
76{
77        wxString path = newPath;
78        wxString file;
79
80        if (path.empty())
81                return false;
82
83        if (m_type == DEFAULT) {
84                int pos1 = path.Find(_T(":["));
85                if (pos1 != -1) {
86                        int pos2 = path.Find(']', true);
87                        if (pos2 != -1 && static_cast<size_t>(pos2) == (path.Length() - 1) && !isFile)
88                                m_type = VMS;
89                        else if (isFile && pos2 > pos1)
90                                m_type = VMS;
91                }
92                else if (path.Length() >= 3 &&
93                        ((path.c_str()[0] >= 'A' && path.c_str()[0] <= 'Z') || (path.c_str()[0] >= 'a' && path.c_str()[0] <= 'z')) &&
94                        path.c_str()[1] == ':' && (path.c_str()[2] == '\\' || path.c_str()[2] == '/'))
95                                m_type = DOS;
96                else if (path.c_str()[0] == FTP_MVS_DOUBLE_QUOTE && path.Last() == FTP_MVS_DOUBLE_QUOTE)
97                        m_type = MVS;
98                else if (path[0] == ':' && (pos1 = path.Mid(1).Find(':')) > 0)
99                {
100                        int slash = path.Find('/');
101                        if (slash == -1 || slash > pos1)
102                                m_type = VXWORKS;
103                }
104                else if (path[0] == '\\')
105                        m_type = DOS_VIRTUAL;
106
107                if (m_type == DEFAULT)
108                        m_type = UNIX;
109        }
110
111        m_data.clear();
112
113        if (!ChangePath(path, isFile))
114                return false;
115
116        if (isFile)
117                newPath = path;
118        return true;
119}
120
121wxString CServerPath::GetPath() const
122{
123        if (empty())
124                return wxString();
125
126        wxString path;
127
128        if (!traits[m_type].prefixmode && m_data->m_prefix)
129                path = *m_data->m_prefix;
130
131        if (traits[m_type].left_enclosure != 0)
132                path += traits[m_type].left_enclosure;
133        if (m_data->m_segments.empty() && (!traits[m_type].has_root || !m_data->m_prefix || traits[m_type].separator_after_prefix))
134                path += traits[m_type].separators[0];
135
136        for (tConstSegmentIter iter = m_data->m_segments.begin(); iter != m_data->m_segments.end(); ++iter) {
137                const wxString& segment = *iter;
138                if (iter != m_data->m_segments.begin())
139                        path += traits[m_type].separators[0];
140                else if (traits[m_type].has_root) {
141                        if (!m_data->m_prefix || traits[m_type].separator_after_prefix)
142                                path += traits[m_type].separators[0];
143                }
144
145                if (traits[m_type].separatorEscape) {
146                        wxString tmp = segment;
147                        EscapeSeparators(m_type, tmp);
148                        path += tmp;
149                }
150                else
151                        path += segment;
152        }
153
154        if (traits[m_type].prefixmode && m_data->m_prefix)
155                path += *m_data->m_prefix;
156
157        if (traits[m_type].right_enclosure != 0)
158                path += traits[m_type].right_enclosure;
159
160        // DOS is strange.
161        // C: is current working dir on drive C, C:\ the drive root.
162        if (m_type == DOS && m_data->m_segments.size() == 1)
163                path += traits[m_type].separators[0];
164
165        return path;
166}
167
168bool CServerPath::HasParent() const
169{
170        if (empty())
171                return false;
172
173        if (!traits[m_type].has_root)
174                return m_data->m_segments.size() > 1;
175
176        return !m_data->m_segments.empty();
177}
178
179CServerPath CServerPath::GetParent() const
180{
181        if (empty() || !HasParent())
182                return CServerPath();
183
184        CServerPath parent(*this);
185        CServerPathData& parent_data = parent.m_data.Get();
186
187        parent_data.m_segments.pop_back();
188
189        if (m_type == MVS)
190                parent_data.m_prefix = CSparseOptional<wxString>(_T("."));
191
192        return parent;
193}
194
195wxString CServerPath::GetLastSegment() const
196{
197        if (empty() || !HasParent())
198                return wxString();
199
200        if (!m_data->m_segments.empty())
201                return m_data->m_segments.back();
202        else
203                return wxString();
204}
205
206// libc sprintf can be so slow at times...
207wxChar* fast_sprint_number(wxChar* s, size_t n)
208{
209        wxChar tmp[20]; // Long enough for 2^64-1
210
211        wxChar* c = tmp;
212        do
213        {
214                *(c++) = n % 10 + '0';
215                n /= 10;
216        } while( n > 0 );
217
218        do
219        {
220                *(s++) = *(--c);
221        } while (c != tmp);
222
223        return s;
224}
225
226#define tstrcpy wcscpy
227
228wxString CServerPath::GetSafePath() const
229{
230        if (empty())
231                return wxString();
232
233        #define INTLENGTH 20 // 2^64 - 1
234
235        int len = 5 // Type and 2x' ' and terminating 0
236                + INTLENGTH; // Max length of prefix
237
238        len += m_data->m_prefix ? m_data->m_prefix->size() : 0;
239        for( auto const& segment : m_data->m_segments ) {
240                len += segment.size() + 2 + INTLENGTH;
241        }
242
243        wxString safepath;
244        {
245                wxStringBuffer start(safepath, len);
246                wxChar* t = start;
247
248                t = fast_sprint_number(t, m_type);
249                *(t++) = ' ';
250                t = fast_sprint_number(t, m_data->m_prefix ? m_data->m_prefix->size() : 0);
251
252                if (m_data->m_prefix) {
253                        *(t++) = ' ';
254                        tstrcpy(t, m_data->m_prefix->c_str());
255                        t += m_data->m_prefix->size();
256                }
257
258                for( auto const& segment : m_data->m_segments ) {
259                        *(t++) = ' ';
260                        t = fast_sprint_number(t, segment.size());
261                        *(t++) = ' ';
262                        tstrcpy(t, segment.c_str());
263                        t += segment.size();
264                }
265                *t = 0;
266        }
267        safepath.Shrink();
268
269        return safepath;
270}
271
272bool CServerPath::SetSafePath(const wxString& path)
273{
274        bool const ret = DoSetSafePath(path);
275        if (!ret) {
276                clear();
277        }
278
279        return ret;
280}
281
282bool CServerPath::DoSetSafePath(const wxString& path)
283{
284        CServerPathData& data = m_data.Get();
285        data.m_prefix.clear();
286        data.m_segments.clear();
287
288        // Optimized for speed, avoid expensive wxString functions
289        // Before the optimization this function was responsible for
290        // most CPU cycles used during loading of transfer queues
291        // from file
292        const int len = (int)path.Len();
293        wxCharTypeBuffer<wxChar> buf(len + 1);
294        wxChar* begin = buf.data();
295
296        memcpy(begin, (const wxChar*)path.c_str(), (len + 1) * sizeof(wxChar));
297        wxChar* p = begin;
298
299        int type = 0;
300        do
301        {
302                if (*p < '0' || *p > '9')
303                        return false;
304                type *= 10;
305                type += *p - '0';
306
307                if (type >= SERVERTYPE_MAX)
308                        return false;
309                ++p;
310        } while (*p != ' ');
311
312        m_type = (ServerType)type;
313        ++p;
314
315        int prefix_len = 0;
316        do
317        {
318                if (*p < '0' || *p > '9')
319                        return false;
320                prefix_len *= 10;
321                prefix_len += *p - '0';
322
323                if (prefix_len > 32767) // Should be sane enough
324                        return false;
325                ++p;
326        }
327        while (*p && *p != ' ');
328
329        if (!*p) {
330                if (prefix_len != 0)
331                        return false;
332                else {
333                        // Is root directory, like / on unix like systems.
334                        return true;
335                }
336        }
337
338        ++p;
339
340        if (len - (p - begin) < prefix_len)
341                return false;
342        if (prefix_len)
343        {
344                *(p + prefix_len) = 0;
345                if( *p ) {
346                        data.m_prefix = CSparseOptional<wxString>(p);
347                }
348
349                p += prefix_len + 1;
350        }
351
352        while (len > (p - begin))
353        {
354                int segment_len = 0;
355                do
356                {
357                        if (*p < '0' || *p > '9')
358                                return false;
359                        segment_len *= 10;
360                        segment_len += *p - '0';
361
362                        if (segment_len > 32767) // Should be sane enough
363                                return false;
364                        ++p;
365                }
366                while (*p != ' ');
367
368                if (!segment_len)
369                        return false;
370                ++p;
371
372                if (len - (p - begin) < segment_len)
373                        return false;
374                *(p + segment_len) = 0;
375                wxString s(p);
376                data.m_segments.push_back(s);
377
378                p += segment_len + 1;
379        }
380
381        return true;
382}
383
384bool CServerPath::SetType(ServerType type)
385{
386        if (!empty() && m_type != DEFAULT)
387                return false;
388
389        m_type = type;
390
391        return true;
392}
393
394ServerType CServerPath::GetType() const
395{
396        return m_type;
397}
398
399bool CServerPath::IsSubdirOf(const CServerPath &path, bool cmpNoCase) const
400{
401        if (empty() || path.empty())
402                return false;
403
404        if (m_type != path.m_type)
405                return false;
406
407        if (!HasParent())
408                return false;
409
410        if (traits[m_type].prefixmode != 1) {
411                if (cmpNoCase ) {
412                        if( m_data->m_prefix && !path.m_data->m_prefix ) {
413                                return false;
414                        }
415                        else if( !m_data->m_prefix && path.m_data->m_prefix ) {
416                                return false;
417                        }
418                        else if( m_data->m_prefix && path.m_data->m_prefix && m_data->m_prefix->CmpNoCase(*path.m_data->m_prefix) ) {
419                                return false;
420                        }
421                }
422                if (!cmpNoCase && m_data->m_prefix != path.m_data->m_prefix)
423                        return false;
424        }
425
426        // On MVS, dirs like 'FOO.BAR' without trailing dot cannot have
427        // subdirectories
428        if (traits[m_type].prefixmode == 1 && !path.m_data->m_prefix)
429                return false;
430
431        tConstSegmentIter iter1 = m_data->m_segments.begin();
432        tConstSegmentIter iter2 = path.m_data->m_segments.begin();
433        while (iter1 != m_data->m_segments.end()) {
434                if (iter2 == path.m_data->m_segments.end())
435                        return true;
436                if (cmpNoCase) {
437                        if (iter1->CmpNoCase(*iter2))
438                                return false;
439                }
440                else if (*iter1 != *iter2)
441                        return false;
442
443                ++iter1;
444                ++iter2;
445        }
446
447        return false;
448}
449
450bool CServerPath::IsParentOf(const CServerPath &path, bool cmpNoCase) const
451{
452        return path.IsSubdirOf(*this, cmpNoCase);
453}
454
455bool CServerPath::ChangePath(wxString subdir)
456{
457        wxString subdir2 = subdir;
458        return ChangePath(subdir2, false);
459}
460
461bool CServerPath::ChangePath(wxString &subdir, bool isFile)
462{
463        bool ret = DoChangePath(subdir, isFile);
464        if (!ret) {
465                clear();
466        }
467
468        return ret;
469}
470
471bool CServerPath::DoChangePath(wxString &subdir, bool isFile)
472{
473        wxString dir = subdir;
474        wxString file;
475
476        if (dir.empty()) {
477                if (empty() || isFile)
478                        return false;
479                else
480                        return true;
481        }
482
483        bool const was_empty = empty();
484        CServerPathData& data = m_data.Get();
485
486        switch (m_type)
487        {
488        case VMS:
489                {
490                        int pos1 = dir.Find(traits[m_type].left_enclosure);
491                        if (pos1 == -1)
492                        {
493                                int pos2 = dir.Find(traits[m_type].right_enclosure, true);
494                                if (pos2 != -1)
495                                        return false;
496
497                                if (isFile)
498                                {
499                                        if (was_empty)
500                                                return false;
501
502                                        file = dir;
503                                        break;
504                                }
505                        }
506                        else
507                        {
508                                int pos2 = dir.Find(traits[m_type].right_enclosure, true);
509                                if (pos2 == -1 || pos2 <= pos1 + 1)
510                                        return false;
511
512                                bool enclosure_is_last = static_cast<size_t>(pos2) == (dir.Length() - 1);
513                                if (isFile == enclosure_is_last)
514                                        return false;
515
516                                if (isFile)
517                                        file = dir.Mid(pos2 + 1);
518                                dir = dir.Left(pos2);
519
520                                if (pos1)
521                                        data.m_prefix = CSparseOptional<wxString>(dir.Left(pos1));
522                                dir = dir.Mid(pos1 + 1);
523
524                                data.m_segments.clear();
525                        }
526
527                        if (!Segmentize(dir, data.m_segments))
528                                return false;
529                        if (data.m_segments.empty() && was_empty)
530                                return false;
531                }
532                break;
533        case DOS:
534                {
535                        bool is_relative = false;
536                        int sep = dir.find_first_of(traits[m_type].separators);
537                        if (sep == -1)
538                                sep = dir.Len();
539                        int colon = dir.Find(':');
540                        if (colon > 0 && colon == sep - 1)
541                                is_relative = true;
542
543                        if (is_relative)
544                                data.m_segments.clear();
545                        else if (wxString(traits[m_type].separators).Contains(dir[0]))
546                        {
547                                if (data.m_segments.empty())
548                                        return false;
549                                wxString first = data.m_segments.front();
550                                data.m_segments.clear();
551                                data.m_segments.push_back(first);
552                                dir = dir.Mid(1);
553                        }
554
555                        if (isFile && !ExtractFile(dir, file))
556                                return false;
557
558                        if (!Segmentize(dir, data.m_segments))
559                                return false;
560                        if (data.m_segments.empty() && was_empty)
561                                return false;
562                }
563                break;
564        case MVS:
565                {
566                        // Remove the double quoation some servers send in PWD reply
567                        int i = 0;
568                        wxChar c = dir.c_str()[i];
569                        while (c == FTP_MVS_DOUBLE_QUOTE)
570                                c = dir.c_str()[++i];
571                        dir.Remove(0, i);
572
573                        while (!dir.empty()) {
574                                c = dir.Last();
575                                if (c != FTP_MVS_DOUBLE_QUOTE)
576                                        break;
577                                else
578                                        dir.RemoveLast();
579                        }
580                }
581                if (dir.empty())
582                        return false;
583
584                if (dir.c_str()[0] == traits[m_type].left_enclosure) {
585                        if (dir.Last() != traits[m_type].right_enclosure)
586                                return false;
587
588                        dir.RemoveLast();
589                        dir = dir.Mid(1);
590
591                        data.m_segments.clear();
592                }
593                else if (dir.Last() == traits[m_type].right_enclosure)
594                        return false;
595                else if (was_empty)
596                        return false;
597
598                if (!dir.empty() && dir.Last() == ')') {
599                        // Partitioned dataset member
600                        if (!isFile)
601                                return false;
602
603                        int pos = dir.Find('(');
604                        if (pos == -1)
605                                return false;
606                        dir.RemoveLast();
607                        file = dir.Mid(pos + 1);
608                        dir = dir.Left(pos);
609
610                        if (!was_empty && !data.m_prefix && !dir.empty())
611                                return false;
612
613                        data.m_prefix.clear();
614                }
615                else {
616                        if (!was_empty && !data.m_prefix)
617                        {
618                                if (dir.Find('.') != -1 || !isFile)
619                                        return false;
620                        }
621
622                        if (isFile) {
623                                if (!ExtractFile(dir, file))
624                                        return false;
625                                data.m_prefix = CSparseOptional<wxString>(_T("."));
626                        }
627                        else if (!dir.empty() && dir.Last() == '.')
628                                data.m_prefix = CSparseOptional<wxString>(_T("."));
629                        else
630                                data.m_prefix.clear();
631                }
632
633                if (!Segmentize(dir, data.m_segments))
634                        return false;
635                break;
636        case HPNONSTOP:
637                if (dir[0] == '\\')
638                        data.m_segments.clear();
639
640                if (isFile && !ExtractFile(dir, file))
641                        return false;
642
643                if (!Segmentize(dir, data.m_segments))
644                        return false;
645                if (data.m_segments.empty() && was_empty)
646                        return false;
647
648                break;
649        case VXWORKS:
650                {
651                        if (dir[0] != ':')
652                        {
653                                if (was_empty)
654                                        return false;
655                        }
656                        else
657                        {
658                                int colon2;
659                                if ((colon2 = dir.Mid(1).Find(':')) < 1)
660                                        return false;
661                                data.m_prefix = CSparseOptional<wxString>(dir.Left(colon2 + 2));
662                                dir = dir.Mid(colon2 + 2);
663
664                                data.m_segments.clear();
665                        }
666
667                        if (isFile && !ExtractFile(dir, file))
668                                return false;
669
670                        if (!Segmentize(dir, data.m_segments))
671                                return false;
672                }
673                break;
674        case CYGWIN:
675                {
676                        if (wxString(traits[m_type].separators).Contains(dir[0]))
677                        {
678                                data.m_segments.clear();
679                                data.m_prefix.clear();
680                        }
681                        else if (was_empty)
682                                return false;
683                        if (dir.Left(2) == _T("//"))
684                        {
685                                data.m_prefix = CSparseOptional<wxString>(traits[m_type].separators[0]);
686                                dir = dir.Mid(1);
687                        }
688
689                        if (isFile && !ExtractFile(dir, file))
690                                return false;
691
692                        if (!Segmentize(dir, data.m_segments))
693                                return false;
694                }
695                break;
696        default:
697                {
698                        if (wxString(traits[m_type].separators).Contains(dir[0]))
699                                data.m_segments.clear();
700                        else if (was_empty)
701                                return false;
702
703                        if (isFile && !ExtractFile(dir, file))
704                                return false;
705
706                        if (!Segmentize(dir, data.m_segments))
707                                return false;
708                }
709                break;
710        }
711
712        if (!traits[m_type].has_root && data.m_segments.empty())
713                return false;
714
715        if (isFile)
716        {
717                if (traits[m_type].has_dots)
718                {
719                        if (file == _T("..") || file == _T("."))
720                                return false;
721                }
722                subdir = file;
723        }
724
725        return true;
726}
727
728bool CServerPath::operator==(const CServerPath &op) const
729{
730        if (empty() != op.empty())
731                return false;
732        else if (m_type != op.m_type)
733                return false;
734        else if (m_data != op.m_data)
735                return false;
736
737        return true;
738}
739
740bool CServerPath::operator!=(const CServerPath &op) const
741{
742        return !(*this == op);
743}
744
745bool CServerPath::operator<(const CServerPath &op) const
746{
747        if (empty()) {
748                if (!op.empty())
749                        return false;
750        }
751        else if (op.empty())
752                return true;
753
754        if( m_data->m_prefix || op.m_data->m_prefix ) {
755                if( m_data->m_prefix < op.m_data->m_prefix ) {
756                        return true;
757                }
758                else if( op.m_data->m_prefix < m_data->m_prefix ) {
759                        return false;
760                }
761        }
762
763        if (m_type > op.m_type)
764                return false;
765        else if (m_type < op.m_type)
766                return true;
767
768        tConstSegmentIter iter1, iter2;
769        for (iter1 = m_data->m_segments.begin(), iter2 = op.m_data->m_segments.begin(); iter1 != m_data->m_segments.end(); ++iter1, ++iter2) {
770                if (iter2 == op.m_data->m_segments.end())
771                        return false;
772
773                const int cmp = iter1->Cmp(*iter2);
774                if (cmp < 0)
775                        return true;
776                if (cmp > 0)
777                        return false;
778        }
779
780        return iter2 != op.m_data->m_segments.end();
781}
782
783wxString CServerPath::FormatFilename(const wxString &filename, bool omitPath /*=false*/) const
784{
785        if (empty())
786                return filename;
787
788        if (filename.empty())
789                return wxString();
790
791        if (omitPath && (!traits[m_type].prefixmode || (m_data->m_prefix && *m_data->m_prefix == _T("."))))
792                return filename;
793
794        wxString result = GetPath();
795        if (traits[m_type].left_enclosure && traits[m_type].filename_inside_enclosure)
796                result.RemoveLast();
797
798        switch (m_type)
799        {
800                case VXWORKS:
801                        if (!result.empty() && !wxString(traits[m_type].separators).Contains(result.Last()) && !m_data->m_segments.empty())
802                                result += traits[m_type].separators[0];
803                        break;
804                case VMS:
805                case MVS:
806                        break;
807                default:
808                        if (!result.empty() && !wxString(traits[m_type].separators).Contains(result.Last()))
809                                result += traits[m_type].separators[0];
810                        break;
811        }
812
813        if (traits[m_type].prefixmode == 1 && !m_data->m_prefix)
814                result += _T("(") + filename + _T(")");
815        else
816                result += filename;
817
818        if (traits[m_type].left_enclosure && traits[m_type].filename_inside_enclosure)
819                result += traits[m_type].right_enclosure;
820
821        return result;
822}
823
824int CServerPath::CmpNoCase(const CServerPath &op) const
825{
826        if (empty() != op.empty())
827                return 1;
828        else if (m_data->m_prefix != op.m_data->m_prefix)
829                return 1;
830        else if (m_type != op.m_type)
831                return 1;
832
833        if (m_data->m_segments.size() > op.m_data->m_segments.size())
834                return 1;
835        else if (m_data->m_segments.size() < op.m_data->m_segments.size())
836                return -1;
837
838        tConstSegmentIter iter = m_data->m_segments.begin();
839        tConstSegmentIter iter2 = op.m_data->m_segments.begin();
840        while (iter != m_data->m_segments.end()) {
841                int res = iter++->CmpNoCase(*iter2++);
842                if (res)
843                        return res;
844        }
845
846        return 0;
847}
848
849bool CServerPath::AddSegment(const wxString& segment)
850{
851        if (empty())
852                return false;
853
854        // TODO: Check for invalid characters
855        m_data.Get().m_segments.push_back(segment);
856
857        return true;
858}
859
860CServerPath CServerPath::GetCommonParent(const CServerPath& path) const
861{
862        if (*this == path)
863                return *this;
864
865        if (empty() || path.empty())
866                return CServerPath();
867
868        if (m_type != path.m_type ||
869                (!traits[m_type].prefixmode && m_data->m_prefix != path.m_data->m_prefix))
870        {
871                return CServerPath();
872        }
873
874        if (!HasParent())
875        {
876                if (path.IsSubdirOf(*this, false))
877                        return *this;
878                else
879                        return CServerPath();
880        }
881        else if (!path.HasParent())
882        {
883                if (IsSubdirOf(path, false))
884                        return path;
885                else
886                        return CServerPath();
887        }
888
889        CServerPath parent;
890        parent.m_type = m_type;
891
892        CServerPathData& parentData = parent.m_data.Get();
893
894        tConstSegmentIter last = m_data->m_segments.end();
895        tConstSegmentIter last2 = path.m_data->m_segments.end();
896        if (traits[m_type].prefixmode == 1) {
897                if (!m_data->m_prefix)
898                        --last;
899                if (!path.m_data->m_prefix)
900                        --last2;
901                parentData.m_prefix = GetParent().m_data->m_prefix;
902        }
903        else
904                parentData.m_prefix = m_data->m_prefix;
905
906        tConstSegmentIter iter = m_data->m_segments.begin();
907        tConstSegmentIter iter2 = path.m_data->m_segments.begin();
908        while (iter != last && iter2 != last2) {
909                if (*iter != *iter2) {
910                        if (!traits[m_type].has_root && parentData.m_segments.empty())
911                                return CServerPath();
912                        else
913                                return parent;
914                }
915
916                parentData.m_segments.push_back(*iter);
917
918                ++iter;
919                ++iter2;
920        }
921
922        return parent;
923}
924
925wxString CServerPath::FormatSubdir(const wxString &subdir) const
926{
927        if (!traits[m_type].separatorEscape)
928                return subdir;
929
930        wxString res = subdir;
931        EscapeSeparators(m_type, res);
932
933        return res;
934}
935
936bool CServerPath::SegmentizeAddSegment(wxString & segment, tSegmentList& segments, bool& append)
937{
938        if (traits[m_type].has_dots) {
939                if (segment == _T("."))
940                        return true;
941                else if (segment == _T("..")) {
942                        if (segments.empty())
943                                return false;
944                        else {
945                                segments.pop_back();
946                                return true;
947                        }
948                }
949        }
950
951        bool append_next = false;
952        if (!segment.empty() && traits[m_type].separatorEscape && segment.Last() == traits[m_type].separatorEscape) {
953                append_next = true;
954                segment[segment.size() - 1] = traits[m_type].separators[0];
955        }
956
957        if (append)
958                segments.back() += segment;
959        else
960                segments.push_back(std::move(segment));
961
962        append = append_next;
963
964        return true;
965}
966
967bool CServerPath::Segmentize(wxString const& str, tSegmentList& segments)
968{
969        bool append = false;
970        size_t start = 0;
971
972        size_t pos;
973        while (true) {
974                pos = str.find_first_of(traits[m_type].separators, start);
975                if (pos == std::string::npos) {
976                        break;
977                }
978                if (start == pos) {
979                        ++start;
980                        continue;
981                }
982
983                wxString segment = str.substr(start, pos - start);
984                start = pos + 1;
985
986                if (!SegmentizeAddSegment(segment, segments, append)) {
987                        return false;
988                }
989        }
990
991        if (start < str.size()) {
992                wxString segment = str.substr(start);
993                if (!SegmentizeAddSegment(segment, segments, append)) {
994                        return false;
995                }
996        }
997
998        if (append)
999                return false;
1000
1001        return true;
1002}
1003
1004bool CServerPath::ExtractFile(wxString& dir, wxString& file)
1005{
1006        int pos = dir.find_last_of(traits[m_type].separators);
1007        if (pos == (int)dir.Length() - 1)
1008                return false;
1009
1010        if (pos == -1) {
1011                file = dir;
1012                dir.clear();
1013                return true;
1014        }
1015
1016        file = dir.Mid(pos + 1);
1017        dir = dir.Left(pos + 1);
1018
1019        return true;
1020}
1021
1022void CServerPath::EscapeSeparators(ServerType type, wxString& subdir)
1023{
1024        if (traits[type].separatorEscape) {
1025                for (const wxChar* p = traits[type].separators; *p; ++p)
1026                        subdir.Replace((wxString)*p, (wxString)traits[type].separatorEscape + traits[type].separators[0]);
1027        }
1028}
1029
1030size_t CServerPath::SegmentCount() const
1031{
1032        return empty() ? 0 : m_data->m_segments.size();
1033}
Note: See TracBrowser for help on using the repository browser.