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

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

First release to xenial

File size: 10.3 KB
Line 
1#include <filezilla.h>
2#include "local_path.h"
3#ifndef __WXMSW__
4#include <errno.h>
5#endif
6
7#include <deque>
8
9#ifdef __WXMSW__
10const wxChar CLocalPath::path_separator = '\\';
11#else
12const wxChar CLocalPath::path_separator = '/';
13#endif
14
15CLocalPath::CLocalPath(const wxString& path, wxString* file /*=0*/)
16{
17        SetPath(path, file);
18}
19
20bool CLocalPath::SetPath(const wxString& path, wxString* file /*=0*/)
21{
22        // This function ensures that the path is in canonical form on success.
23
24        if (path.empty()) {
25                m_path.clear();
26                return false;
27        }
28
29        std::deque<wxChar*> segments; // List to store the beginnings of segments
30
31        const wxChar* in = path.c_str();
32
33        {
34                wxStringBuffer start(m_path.Get(), path.Len() + 2);
35                wxChar* out = start;
36
37#ifdef __WXMSW__
38                if (path == _T("\\"))
39                {
40                        *out++ = '\\';
41                        *out++ = 0;
42                        if (file)
43                                file->clear();
44                        return true;
45                }
46
47                if (*in == '\\') {
48                        // possibly UNC
49
50                        in++;
51                        if (*in++ != '\\') {
52                                *start = 0;
53                                return false;
54                        }
55
56                        if (*in == '?') {
57                                // Could be \\?\c:\foo\bar
58                                // or \\?\UNC\server\sharee
59                                // or something else we do not support.
60                                if (*(++in) != '\\') {
61                                        return false;
62                                }
63                                in++;
64                                if (((*in >= 'a' && *in <= 'z') || (*in >= 'A' || *in <= 'Z')) && *(in+1) == ':') {
65                                        // It's \\?\c:\foo\bar
66                                        goto parse_regular;
67                                }
68                                if (wxStrlen(in) < 5 || wxStrnicmp(in, _T("UNC\\"), 4)) {
69                                        return false;
70                                }
71                                in += 4;
72                        }
73                        *out++ = '\\';
74                        *out++ = '\\';
75
76                        // UNC path
77                        while (*in)
78                        {
79                                if (*in == '/' || *in == '\\')
80                                        break;
81                                *out++ = *in++;
82                        }
83                        *out++ = path_separator;
84
85                        if (out - start <= 3) {
86                                // not a valid UNC path
87                                *start = 0;
88                                return false;
89                        }
90
91                        segments.push_back(out);
92                }
93                else if ((*in >= 'a' && *in <= 'z') || (*in >= 'A' || *in <= 'Z'))
94                {
95parse_regular:
96                        // Regular path
97                        *out++ = *in++;
98
99                        if (*in++ != ':') {
100                                *start = 0;
101                                return false;
102                        }
103                        *out++ = ':';
104                        if (*in != '/' && *in != '\\' && *in) {
105                                *start = 0;
106                                return false;
107                        }
108                        *out++ = path_separator;
109                        segments.push_back(out);
110                }
111                else {
112                        *start = 0;
113                        return false;
114                }
115#else
116                if (*in++ != '/')
117                {
118                        // SetPath only accepts absolute paths
119                        *start = 0;
120                        return false;
121                }
122
123                *out++ = '/';
124                segments.push_back(out);
125#endif
126
127                enum _last
128                {
129                        separator,
130                        dot,
131                        dotdot,
132                        segment
133                };
134                enum _last last = separator;
135
136                while (*in)
137                {
138                        if (*in == '/'
139        #ifdef __WXMSW__
140                                || *in == '\\'
141        #endif
142                                )
143                        {
144                                in++;
145                                if (last == separator)
146                                {
147                                        // /foo//bar is equal to /foo/bar
148                                        continue;
149                                }
150                                else if (last == dot)
151                                {
152                                        // /foo/./bar is equal to /foo/bar
153                                        last = separator;
154                                        out = segments.back();
155                                        continue;
156                                }
157                                else if (last == dotdot)
158                                {
159                                        last = separator;
160
161                                        // Go two segments back if possible
162                                        if (segments.size() > 1)
163                                                segments.pop_back();
164                                        wxASSERT(!segments.empty());
165                                        out = segments.back();
166                                        continue;
167                                }
168
169                                // Ordinary segment just ended.
170                                *out++ = path_separator;
171                                segments.push_back(out);
172                                last = separator;
173                                continue;
174                        }
175                        else if (*in == '.')
176                        {
177                                if (last == separator)
178                                        last = dot;
179                                else if (last == dot)
180                                        last = dotdot;
181                                else if (last == dotdot)
182                                        last = segment;
183                        }
184                        else
185                                last = segment;
186
187                        *out++ = *in++;
188                }
189                if (last == dot)
190                        out = segments.back();
191                else if (last == dotdot)
192                {
193                        if (segments.size() > 1)
194                                segments.pop_back();
195                        out = segments.back();
196                }
197                else if (last == segment)
198                {
199                        if (file)
200                        {
201                                *out = 0;
202                                out = segments.back();
203                                *file = out;
204                        }
205                        else
206                                *out++ = path_separator;
207                }
208
209                *out = 0;
210        }
211
212        return true;
213}
214
215bool CLocalPath::empty() const
216{
217        return m_path->empty();
218}
219
220void CLocalPath::clear()
221{
222        m_path.clear();
223}
224
225bool CLocalPath::IsWriteable() const
226{
227        if (m_path->empty())
228                return false;
229
230#ifdef __WXMSW__
231        if (m_path == _T("\\"))
232                // List of drives not writeable
233                return false;
234
235        if (m_path->Left(2) == _T("\\\\")) {
236                int pos = m_path->Mid(2).Find('\\');
237                if (pos == -1 || pos + 3 == (int)m_path->Len())
238                        // List of shares on a computer not writeable
239                        return false;
240        }
241#endif
242
243        return true;
244}
245
246bool CLocalPath::HasParent() const
247{
248#ifdef __WXMSW__
249        // C:\f\ has parent
250        // C:\ does not
251        // \\x\y\ shortest UNC
252        //   ^ min
253        const int min = 2;
254#else
255        const int min = 0;
256#endif
257        for (int i = (int)m_path->Len() - 2; i >= min; --i) {
258                if ((*m_path)[i] == path_separator)
259                        return true;
260        }
261
262        return false;
263}
264
265bool CLocalPath::HasLogicalParent() const
266{
267#ifdef __WXMSW__
268        if (m_path->Len() == 3 && (*m_path)[0] != '\\') // Drive root
269                return true;
270#endif
271        return HasParent();
272}
273
274CLocalPath CLocalPath::GetParent(wxString* last_segment /*=0*/) const
275{
276        CLocalPath parent;
277
278#ifdef __WXMSW__
279        if (m_path->Len() == 3 && (*m_path)[0] != '\\') // Drive root
280        {
281                if (last_segment)
282                        last_segment->clear();
283                return CLocalPath(_T("\\"));
284        }
285
286        // C:\f\ has parent
287        // C:\ does not
288        // \\x\y\ shortest UNC
289        //   ^ min
290        const int min = 2;
291#else
292        const int min = 0;
293#endif
294        for (int i = (int)m_path->Len() - 2; i >= min; --i) {
295                if ((*m_path)[i] == path_separator) {
296                        if (last_segment) {
297                                *last_segment = m_path->Mid(i + 1);
298                                last_segment->RemoveLast();
299                        }
300                        return CLocalPath(m_path->Left(i + 1));
301                }
302        }
303
304        return CLocalPath();
305}
306
307bool CLocalPath::MakeParent(wxString* last_segment /*=0*/)
308{
309        wxString& path = m_path.Get();
310
311#ifdef __WXMSW__
312        if (path.Len() == 3 && path[0] != '\\') // Drive root
313        {
314                path = _T("\\");
315                return true;
316        }
317
318        // C:\f\ has parent
319        // C:\ does not
320        // \\x\y\ shortest UNC
321        //   ^ min
322        const int min = 2;
323#else
324        const int min = 0;
325#endif
326        for (int i = (int)path.Len() - 2; i >= min; --i) {
327                if (path[i] == path_separator) {
328                        if (last_segment) {
329                                *last_segment = path.Mid(i + 1);
330                                last_segment->RemoveLast();
331                        }
332                        path = path.Left(i + 1);
333                        return true;
334                }
335        }
336
337        return false;
338}
339
340void CLocalPath::AddSegment(const wxString& segment)
341{
342        wxString& path = m_path.Get();
343
344        wxASSERT(!path.empty());
345        wxASSERT(segment.Find(_T("/")) == -1);
346#ifdef __WXMSW__
347        wxASSERT(segment.Find(_T("\\")) == -1);
348#endif
349
350        if (!segment.empty()) {
351                path += segment;
352                path += path_separator;
353        }
354}
355
356bool CLocalPath::ChangePath(const wxString& new_path)
357{
358        if (new_path.empty())
359                return false;
360
361        wxString& path = m_path.Get();
362
363#ifdef __WXMSW__
364        if (new_path == _T("\\") || new_path == _T("/")) {
365                path = _T("\\");
366                return true;
367        }
368
369        if (new_path.Len() >= 2 && new_path[0] == '\\' && new_path[1] == '\\') {
370                // Absolute UNC
371                return SetPath(new_path);
372        }
373        if (new_path.Len() >= 2 && new_path[0] && new_path[1] == ':') {
374                // Absolute new_path
375                return SetPath(new_path);
376        }
377
378        // Relative new_path
379        if (path.empty())
380                return false;
381
382        if (new_path.Len() >= 2 && (new_path[0] == '\\' || new_path[0] == '/') && path[1] == ':') {
383                // Relative to drive root
384                return SetPath(path.Left(2) + new_path);
385        }
386        else {
387                // Relative to current directory
388                return SetPath(path + new_path);
389        }
390#else
391        if (!new_path.empty() && new_path[0] == path_separator) {
392                // Absolute new_path
393                return SetPath(new_path);
394        }
395        else
396        {
397                // Relative new_path
398
399                if (path.empty())
400                        return false;
401
402                return SetPath(path + new_path);
403        }
404#endif
405}
406
407bool CLocalPath::Exists(wxString *error /*=0*/) const
408{
409        wxASSERT(!m_path->empty());
410        if (m_path->empty())
411                return false;
412
413#ifdef __WXMSW__
414        if (m_path == _T("\\"))
415        {
416                // List of drives always exists
417                return true;
418        }
419
420        if ((*m_path)[0] == '\\') {
421                // \\server\share\ UNC path
422
423                size_t pos;
424
425                // Search for backslash separating server from share
426                for (pos = 3; pos < m_path->Len(); pos++)
427                        if ((*m_path)[pos] == '\\')
428                                break;
429                pos++;
430                if (pos >= m_path->Len()) {
431                        // Partial UNC path
432                        return true;
433                }
434        }
435
436        wxString path = *m_path;
437        if (path.Len() > 3)
438                path.RemoveLast();
439        DWORD ret = ::GetFileAttributes(path);
440        if (ret == INVALID_FILE_ATTRIBUTES) {
441                if (!error)
442                        return false;
443
444                error->Printf(_("'%s' does not exist or cannot be accessed."), path);
445
446                if ((*m_path)[0] == '\\')
447                        return false;
448
449                // Check for removable drive, display a more specific error message in that case
450                if (::GetLastError() != ERROR_NOT_READY)
451                        return false;
452                int type = GetDriveType(m_path->Left(3));
453                if (type == DRIVE_REMOVABLE || type == DRIVE_CDROM)
454                        error->Printf(_("Cannot access '%s', no media inserted or drive not ready."), path);
455                return false;
456        }
457        else if (!(ret & FILE_ATTRIBUTE_DIRECTORY)) {
458                if (error)
459                        error->Printf(_("'%s' is not a directory."), path);
460                return false;
461        }
462
463        return true;
464#else
465        wxString path = *m_path;
466        if (path.Len() > 1)
467                path.RemoveLast();
468
469        const wxCharBuffer s = path.fn_str();
470
471        struct stat buf;
472        int result = stat(s, &buf);
473
474        if (!result) {
475                if (S_ISDIR(buf.st_mode))
476                        return true;
477
478                if (error)
479                        error->Printf(_("'%s' is not a directory."), path);
480
481                return false;
482        }
483        else if (result == ENOTDIR) {
484                if (error)
485                        error->Printf(_("'%s' is not a directory."), path);
486                return false;
487        }
488        else {
489                if (error)
490                        error->Printf(_("'%s' does not exist or cannot be accessed."), path);
491                return false;
492        }
493#endif
494}
495
496bool CLocalPath::operator==(const CLocalPath& op) const
497{
498#ifdef __WXMSW__
499        return m_path->CmpNoCase(*op.m_path) == 0;
500#else
501        return m_path == op.m_path;
502#endif
503}
504
505bool CLocalPath::operator!=(const CLocalPath& op) const
506{
507#ifdef __WXMSW__
508        return m_path->CmpNoCase(*op.m_path) != 0;
509#else
510        return m_path != op.m_path;
511#endif
512}
513
514bool CLocalPath::IsParentOf(const CLocalPath &path) const
515{
516        if (empty() || path.empty())
517                return false;
518
519        if (path.m_path->Len() < m_path->Len())
520                return false;
521
522#ifdef __WXMSW__
523        if (m_path->CmpNoCase(path.m_path->Left(m_path->Len())))
524                return false;
525#else
526        if (*m_path != path.m_path->Left(m_path->Len()))
527                return false;
528#endif
529
530        return true;
531}
532
533bool CLocalPath::IsSubdirOf(const CLocalPath &path) const
534{
535        if (empty() || path.empty())
536                return false;
537
538        if (path.m_path->Len() > m_path->Len())
539                return false;
540
541#ifdef __WXMSW__
542        if (path.m_path->CmpNoCase(m_path->Left(path.m_path->Len())))
543                return false;
544#else
545        if (*path.m_path != m_path->Left(path.m_path->Len()))
546                return false;
547#endif
548
549        return true;
550}
551
552wxString CLocalPath::GetLastSegment() const
553{
554        wxASSERT(HasParent());
555
556#ifdef __WXMSW__
557        // C:\f\ has parent
558        // C:\ does not
559        // \\x\y\ shortest UNC
560        //   ^ min
561        const int min = 2;
562#else
563        const int min = 0;
564#endif
565        for (int i = (int)m_path->Len() - 2; i >= min; i--) {
566                if ((*m_path)[i] == path_separator) {
567                        wxString last = m_path->Mid(i + 1);
568                        last.RemoveLast();
569                        return last;
570                }
571        }
572
573        return wxString();
574}
Note: See TracBrowser for help on using the repository browser.