1//
2// Path.cpp
3//
4// Library: Foundation
5// Package: Filesystem
6// Module: Path
7//
8// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/Path.h"
16#include "Poco/File.h"
17#include "Poco/Exception.h"
18#include "Poco/StringTokenizer.h"
19#include "Poco/UnicodeConverter.h"
20#include "Poco/Buffer.h"
21#include <algorithm>
22
23
24#if defined(POCO_OS_FAMILY_UNIX)
25#include "Path_UNIX.cpp"
26#elif defined(POCO_OS_FAMILY_WINDOWS)
27#if defined(_WIN32_WCE)
28#include "Path_WINCE.cpp"
29#else
30#include "Path_WIN32.cpp"
31#endif
32#endif
33
34
35namespace Poco {
36
37
38Path::Path(): _absolute(false)
39{
40}
41
42
43Path::Path(bool absolutePath): _absolute(absolutePath)
44{
45}
46
47
48Path::Path(const std::string& path)
49{
50 poco_assert(std::char_traits<char>::length(path.data()) == path.size());
51 assign(path);
52}
53
54
55Path::Path(const std::string& path, Style style)
56{
57 poco_assert(std::char_traits<char>::length(path.data()) == path.size());
58 assign(path, style);
59}
60
61
62Path::Path(const char* path)
63{
64 poco_check_ptr(path);
65 assign(path);
66}
67
68
69Path::Path(const char* path, Style style)
70{
71 poco_check_ptr(path);
72 assign(path, style);
73}
74
75
76Path::Path(const Path& path):
77 _node(path._node),
78 _device(path._device),
79 _name(path._name),
80 _version(path._version),
81 _dirs(path._dirs),
82 _absolute(path._absolute)
83{
84}
85
86
87Path::Path(const Path& rParent, const std::string& fileName):
88 _node(rParent._node),
89 _device(rParent._device),
90 _name(rParent._name),
91 _version(rParent._version),
92 _dirs(rParent._dirs),
93 _absolute(rParent._absolute)
94{
95 poco_assert(std::char_traits<char>::length(fileName.data()) == fileName.size());
96 makeDirectory();
97 _name = fileName;
98}
99
100
101Path::Path(const Path& rParent, const char* fileName):
102 _node(rParent._node),
103 _device(rParent._device),
104 _name(rParent._name),
105 _version(rParent._version),
106 _dirs(rParent._dirs),
107 _absolute(rParent._absolute)
108{
109 makeDirectory();
110 _name = fileName;
111}
112
113
114Path::Path(const Path& rParent, const Path& relative):
115 _node(rParent._node),
116 _device(rParent._device),
117 _name(rParent._name),
118 _version(rParent._version),
119 _dirs(rParent._dirs),
120 _absolute(rParent._absolute)
121{
122 resolve(relative);
123}
124
125
126Path::~Path()
127{
128}
129
130
131Path& Path::operator = (const Path& path)
132{
133 return assign(path);
134}
135
136
137Path& Path::operator = (const std::string& path)
138{
139 poco_assert(std::char_traits<char>::length(path.data()) == path.size());
140 return assign(path);
141}
142
143
144Path& Path::operator = (const char* path)
145{
146 poco_check_ptr(path);
147 return assign(path);
148}
149
150
151void Path::swap(Path& path)
152{
153 std::swap(_node, path._node);
154 std::swap(_device, path._device);
155 std::swap(_name, path._name);
156 std::swap(_version, path._version);
157 std::swap(_dirs, path._dirs);
158 std::swap(_absolute, path._absolute);
159}
160
161
162Path& Path::assign(const Path& path)
163{
164 if (&path != this)
165 {
166 _node = path._node;
167 _device = path._device;
168 _name = path._name;
169 _version = path._version;
170 _dirs = path._dirs;
171 _absolute = path._absolute;
172 }
173 return *this;
174}
175
176
177Path& Path::assign(const std::string& path)
178{
179#if defined(POCO_OS_FAMILY_WINDOWS)
180 parseWindows(path);
181#else
182 parseUnix(path);
183#endif
184 return *this;
185}
186
187
188Path& Path::assign(const std::string& path, Style style)
189{
190 switch (style)
191 {
192 case PATH_UNIX:
193 parseUnix(path);
194 break;
195 case PATH_WINDOWS:
196 parseWindows(path);
197 break;
198 case PATH_VMS:
199 parseVMS(path);
200 break;
201 case PATH_NATIVE:
202 assign(path);
203 break;
204 case PATH_GUESS:
205 parseGuess(path);
206 break;
207 default:
208 poco_bugcheck();
209 }
210 return *this;
211}
212
213
214Path& Path::assign(const char* path)
215{
216 return assign(std::string(path));
217}
218
219
220std::string Path::toString() const
221{
222#if defined(POCO_OS_FAMILY_WINDOWS)
223 return buildWindows();
224#else
225 return buildUnix();
226#endif
227}
228
229
230std::string Path::toString(Style style) const
231{
232 switch (style)
233 {
234 case PATH_UNIX:
235 return buildUnix();
236 case PATH_WINDOWS:
237 return buildWindows();
238 case PATH_VMS:
239 return buildVMS();
240 case PATH_NATIVE:
241 case PATH_GUESS:
242 return toString();
243 default:
244 poco_bugcheck();
245 }
246 return std::string();
247}
248
249
250bool Path::tryParse(const std::string& path)
251{
252 poco_assert(std::char_traits<char>::length(path.data()) == path.size());
253 try
254 {
255 Path p;
256 p.parse(path);
257 assign(p);
258 return true;
259 }
260 catch (...)
261 {
262 return false;
263 }
264}
265
266
267bool Path::tryParse(const std::string& path, Style style)
268{
269 poco_assert(std::char_traits<char>::length(path.data()) == path.size());
270 try
271 {
272 Path p;
273 p.parse(path, style);
274 assign(p);
275 return true;
276 }
277 catch (...)
278 {
279 return false;
280 }
281}
282
283
284Path& Path::parseDirectory(const std::string& path)
285{
286 poco_assert(std::char_traits<char>::length(path.data()) == path.size());
287 assign(path);
288 return makeDirectory();
289}
290
291
292Path& Path::parseDirectory(const std::string& path, Style style)
293{
294 poco_assert(std::char_traits<char>::length(path.data()) == path.size());
295 assign(path, style);
296 return makeDirectory();
297}
298
299
300Path& Path::makeDirectory()
301{
302 pushDirectory(_name);
303 _name.clear();
304 _version.clear();
305 return *this;
306}
307
308
309Path& Path::makeFile()
310{
311 if (!_dirs.empty() && _name.empty())
312 {
313 _name = _dirs.back();
314 _dirs.pop_back();
315 }
316 return *this;
317}
318
319
320Path& Path::makeAbsolute()
321{
322 return makeAbsolute(current());
323}
324
325
326Path& Path::makeAbsolute(const Path& base)
327{
328 if (!_absolute)
329 {
330 Path tmp = base;
331 tmp.makeDirectory();
332 for (StringVec::const_iterator it = _dirs.begin(); it != _dirs.end(); ++it)
333 {
334 tmp.pushDirectory(*it);
335 }
336 _node = tmp._node;
337 _device = tmp._device;
338 _dirs = tmp._dirs;
339 _absolute = base._absolute;
340 }
341 return *this;
342}
343
344
345Path Path::absolute() const
346{
347 Path result(*this);
348 if (!result._absolute)
349 {
350 result.makeAbsolute();
351 }
352 return result;
353}
354
355
356Path Path::absolute(const Path& base) const
357{
358 Path result(*this);
359 if (!result._absolute)
360 {
361 result.makeAbsolute(base);
362 }
363 return result;
364}
365
366
367Path Path::parent() const
368{
369 Path p(*this);
370 return p.makeParent();
371}
372
373
374Path& Path::makeParent()
375{
376 if (_name.empty())
377 {
378 if (_dirs.empty())
379 {
380 if (!_absolute)
381 _dirs.push_back("..");
382 }
383 else
384 {
385 if (_dirs.back() == "..")
386 _dirs.push_back("..");
387 else
388 _dirs.pop_back();
389 }
390 }
391 else
392 {
393 _name.clear();
394 _version.clear();
395 }
396 return *this;
397}
398
399
400Path& Path::append(const Path& path)
401{
402 makeDirectory();
403 _dirs.insert(_dirs.end(), path._dirs.begin(), path._dirs.end());
404 _name = path._name;
405 _version = path._version;
406 return *this;
407}
408
409
410Path& Path::resolve(const Path& path)
411{
412 if (path.isAbsolute())
413 {
414 assign(path);
415 }
416 else
417 {
418 for (int i = 0; i < path.depth(); ++i)
419 pushDirectory(path[i]);
420 _name = path._name;
421 }
422 return *this;
423}
424
425
426Path& Path::setNode(const std::string& node)
427{
428 poco_assert(std::char_traits<char>::length(node.data()) == node.size());
429 _node = node;
430 _absolute = _absolute || !node.empty();
431 return *this;
432}
433
434
435Path& Path::setDevice(const std::string& device)
436{
437 poco_assert(std::char_traits<char>::length(device.data()) == device.size());
438 _device = device;
439 _absolute = _absolute || !device.empty();
440 return *this;
441}
442
443
444const std::string& Path::directory(int n) const
445{
446 poco_assert (0 <= n && n <= _dirs.size());
447
448 if (n < _dirs.size())
449 return _dirs[n];
450 else
451 return _name;
452}
453
454
455const std::string& Path::operator [] (int n) const
456{
457 poco_assert (0 <= n && n <= _dirs.size());
458
459 if (n < _dirs.size())
460 return _dirs[n];
461 else
462 return _name;
463}
464
465
466Path& Path::pushDirectory(const std::string& dir)
467{
468 poco_assert(std::char_traits<char>::length(dir.data()) == dir.size());
469 if (!dir.empty() && dir != ".")
470 {
471 if (dir == "..")
472 {
473 if (!_dirs.empty() && _dirs.back() != "..")
474 _dirs.pop_back();
475 else if (!_absolute)
476 _dirs.push_back(dir);
477 }
478 else _dirs.push_back(dir);
479 }
480 return *this;
481}
482
483
484Path& Path::popDirectory()
485{
486 poco_assert (!_dirs.empty());
487
488 _dirs.pop_back();
489 return *this;
490}
491
492
493Path& Path::popFrontDirectory()
494{
495 poco_assert (!_dirs.empty());
496
497 StringVec::iterator it = _dirs.begin();
498 _dirs.erase(it);
499 return *this;
500}
501
502
503Path& Path::setFileName(const std::string& name)
504{
505 poco_assert(std::char_traits<char>::length(name.data()) == name.size());
506 _name = name;
507 return *this;
508}
509
510
511Path& Path::setBaseName(const std::string& name)
512{
513 poco_assert(std::char_traits<char>::length(name.data()) == name.size());
514 std::string ext = getExtension();
515 _name = name;
516 if (!ext.empty())
517 {
518 _name.append(".");
519 _name.append(ext);
520 }
521 return *this;
522}
523
524
525std::string Path::getBaseName() const
526{
527 std::string::size_type pos = _name.rfind('.');
528 if (pos != std::string::npos)
529 return _name.substr(0, pos);
530 else
531 return _name;
532}
533
534
535Path& Path::setExtension(const std::string& extension)
536{
537 poco_assert(std::char_traits<char>::length(extension.data()) == extension.size());
538 _name = getBaseName();
539 if (!extension.empty())
540 {
541 _name.append(".");
542 _name.append(extension);
543 }
544 return *this;
545}
546
547
548std::string Path::getExtension() const
549{
550 std::string::size_type pos = _name.rfind('.');
551 if (pos != std::string::npos)
552 return _name.substr(pos + 1);
553 else
554 return std::string();
555}
556
557
558Path& Path::clear()
559{
560 _node.clear();
561 _device.clear();
562 _name.clear();
563 _dirs.clear();
564 _version.clear();
565 _absolute = false;
566 return *this;
567}
568
569
570std::string Path::current()
571{
572 return PathImpl::currentImpl();
573}
574
575
576std::string Path::home()
577{
578 return PathImpl::homeImpl();
579}
580
581
582std::string Path::configHome()
583{
584#if defined(POCO_OS_FAMILY_UNIX) || defined(POCO_OS_FAMILY_WINDOWS)
585 return PathImpl::configHomeImpl();
586#else
587 return PathImpl::homeImpl();
588#endif
589}
590
591
592std::string Path::dataHome()
593{
594#if defined(POCO_OS_FAMILY_UNIX) || defined(POCO_OS_FAMILY_WINDOWS)
595 return PathImpl::dataHomeImpl();
596#else
597 return PathImpl::homeImpl();
598#endif
599}
600
601
602std::string Path::cacheHome()
603{
604#if defined(POCO_OS_FAMILY_UNIX) || defined(POCO_OS_FAMILY_WINDOWS)
605 return PathImpl::cacheHomeImpl();
606#else
607 return PathImpl::homeImpl();
608#endif
609}
610
611
612std::string Path::self()
613{
614 return PathImpl::selfImpl();
615}
616
617
618std::string Path::temp()
619{
620 return PathImpl::tempImpl();
621}
622
623
624std::string Path::config()
625{
626#if defined(POCO_OS_FAMILY_UNIX) || defined(POCO_OS_FAMILY_WINDOWS)
627 return PathImpl::configImpl();
628#else
629 return PathImpl::currentImpl();
630#endif
631}
632
633std::string Path::null()
634{
635 return PathImpl::nullImpl();
636}
637
638
639std::string Path::expand(const std::string& path)
640{
641 poco_assert(std::char_traits<char>::length(path.data()) == path.size());
642 return PathImpl::expandImpl(path);
643}
644
645
646void Path::listRoots(std::vector<std::string>& roots)
647{
648 PathImpl::listRootsImpl(roots);
649}
650
651
652bool Path::find(StringVec::const_iterator it, StringVec::const_iterator end, const std::string& name, Path& path)
653{
654 poco_assert(std::char_traits<char>::length(name.data()) == name.size());
655 while (it != end)
656 {
657#if defined(WIN32)
658 std::string cleanPath(*it);
659 if (cleanPath.size() > 1 && cleanPath[0] == '"' && cleanPath[cleanPath.size() - 1] == '"')
660 {
661 cleanPath = cleanPath.substr(1, cleanPath.size() - 2);
662 }
663 Path p(cleanPath);
664#else
665 Path p(*it);
666#endif
667 p.makeDirectory();
668 p.resolve(Path(name));
669 File f(p);
670 if (f.exists())
671 {
672 path = p;
673 return true;
674 }
675 ++it;
676 }
677 return false;
678}
679
680
681bool Path::find(const std::string& pathList, const std::string& name, Path& path)
682{
683 StringTokenizer st(pathList, std::string(1, pathSeparator()), StringTokenizer::TOK_IGNORE_EMPTY + StringTokenizer::TOK_TRIM);
684 return find(st.begin(), st.end(), name, path);
685}
686
687
688void Path::parseUnix(const std::string& path)
689{
690 clear();
691
692 std::string::const_iterator it = path.begin();
693 std::string::const_iterator end = path.end();
694
695 if (it != end)
696 {
697 if (*it == '/')
698 {
699 _absolute = true; ++it;
700 }
701 else if (*it == '~')
702 {
703 ++it;
704 if (it == end || *it == '/')
705 {
706 Path cwd(home());
707 _dirs = cwd._dirs;
708 _absolute = true;
709 }
710 else --it;
711 }
712
713 while (it != end)
714 {
715 std::string name;
716 while (it != end && *it != '/') name += *it++;
717 if (it != end)
718 {
719 if (_dirs.empty())
720 {
721 if (!name.empty() && *(name.rbegin()) == ':')
722 {
723 _absolute = true;
724 _device.assign(name, 0, name.length() - 1);
725 }
726 else
727 {
728 pushDirectory(name);
729 }
730 }
731 else pushDirectory(name);
732 }
733 else _name = name;
734 if (it != end) ++it;
735 }
736 }
737}
738
739
740void Path::parseWindows(const std::string& path)
741{
742 clear();
743
744 std::string::const_iterator it = path.begin();
745 std::string::const_iterator end = path.end();
746
747 if (it != end)
748 {
749 if (*it == '\\' || *it == '/') { _absolute = true; ++it; }
750 if (_absolute && it != end && (*it == '\\' || *it == '/')) // UNC
751 {
752 ++it;
753 while (it != end && *it != '\\' && *it != '/') _node += *it++;
754 if (it != end) ++it;
755 }
756 else if (it != end)
757 {
758 char d = *it++;
759 if (it != end && *it == ':') // drive letter
760 {
761 if (!((d >= 'a' && d <= 'z') || (d >= 'A' && d <= 'Z'))) throw PathSyntaxException(path);
762 _absolute = true;
763 _device += d;
764 ++it;
765 if (it == end || (*it != '\\' && *it != '/')) throw PathSyntaxException(path);
766 ++it;
767 }
768 else --it;
769 }
770 while (it != end)
771 {
772 std::string name;
773 while (it != end && *it != '\\' && *it != '/') name += *it++;
774 if (it != end)
775 pushDirectory(name);
776 else
777 _name = name;
778 if (it != end) ++it;
779 }
780 }
781 if (!_node.empty() && _dirs.empty() && !_name.empty())
782 makeDirectory();
783}
784
785
786void Path::parseVMS(const std::string& path)
787{
788 clear();
789
790 std::string::const_iterator it = path.begin();
791 std::string::const_iterator end = path.end();
792
793 if (it != end)
794 {
795 std::string name;
796 while (it != end && *it != ':' && *it != '[' && *it != ';') name += *it++;
797 if (it != end)
798 {
799 if (*it == ':')
800 {
801 ++it;
802 if (it != end && *it == ':')
803 {
804 _node = name;
805 ++it;
806 }
807 else _device = name;
808 _absolute = true;
809 name.clear();
810 }
811 if (it != end)
812 {
813 if (_device.empty() && *it != '[')
814 {
815 while (it != end && *it != ':' && *it != ';') name += *it++;
816 if (it != end)
817 {
818 if (*it == ':')
819 {
820 _device = name;
821 _absolute = true;
822 name.clear();
823 ++it;
824 }
825 }
826 }
827 }
828 if (name.empty())
829 {
830 if (it != end && *it == '[')
831 {
832 ++it;
833 if (it != end)
834 {
835 _absolute = true;
836 if (*it == '.')
837 { _absolute = false; ++it; }
838 else if (*it == ']' || *it == '-')
839 _absolute = false;
840 while (it != end && *it != ']')
841 {
842 name.clear();
843 if (*it == '-')
844 name = "-";
845 else
846 while (it != end && *it != '.' && *it != ']') name += *it++;
847 if (!name.empty())
848 {
849 if (name == "-")
850 {
851 if (_dirs.empty() || _dirs.back() == "..")
852 _dirs.push_back("..");
853 else
854 _dirs.pop_back();
855 }
856 else _dirs.push_back(name);
857 }
858 if (it != end && *it != ']') ++it;
859 }
860 if (it == end) throw PathSyntaxException(path);
861 ++it;
862 if (it != end && *it == '[')
863 {
864 if (!_absolute) throw PathSyntaxException(path);
865 ++it;
866 if (it != end && *it == '.') throw PathSyntaxException(path);
867 int d = int(_dirs.size());
868 while (it != end && *it != ']')
869 {
870 name.clear();
871 if (*it == '-')
872 name = "-";
873 else
874 while (it != end && *it != '.' && *it != ']') name += *it++;
875 if (!name.empty())
876 {
877 if (name == "-")
878 {
879 if (_dirs.size() > d)
880 _dirs.pop_back();
881 }
882 else _dirs.push_back(name);
883 }
884 if (it != end && *it != ']') ++it;
885 }
886 if (it == end) throw PathSyntaxException(path);
887 ++it;
888 }
889 }
890 _name.clear();
891 }
892 while (it != end && *it != ';') _name += *it++;
893 }
894 else _name = name;
895 if (it != end && *it == ';')
896 {
897 ++it;
898 while (it != end) _version += *it++;
899 }
900 }
901 else _name = name;
902 }
903}
904
905
906void Path::parseGuess(const std::string& path)
907{
908 bool hasBackslash = false;
909 bool hasSlash = false;
910 bool hasOpenBracket = false;
911 bool hasClosBracket = false;
912 bool isWindows = path.length() > 2 && path[1] == ':' && (path[2] == '/' || path[2] == '\\');
913 std::string::const_iterator end = path.end();
914 std::string::const_iterator semiIt = end;
915 if (!isWindows)
916 {
917 for (std::string::const_iterator it = path.begin(); it != end; ++it)
918 {
919 switch (*it)
920 {
921 case '\\': hasBackslash = true; break;
922 case '/': hasSlash = true; break;
923 case '[': hasOpenBracket = true;
924 case ']': hasClosBracket = hasOpenBracket;
925 case ';': semiIt = it; break;
926 }
927 }
928 }
929 if (hasBackslash || isWindows)
930 {
931 parseWindows(path);
932 }
933 else if (hasSlash)
934 {
935 parseUnix(path);
936 }
937 else
938 {
939 bool isVMS = hasClosBracket;
940 if (!isVMS && semiIt != end)
941 {
942 isVMS = true;
943 ++semiIt;
944 while (semiIt != end)
945 {
946 if (*semiIt < '0' || *semiIt > '9')
947 {
948 isVMS = false; break;
949 }
950 ++semiIt;
951 }
952 }
953 if (isVMS)
954 parseVMS(path);
955 else
956 parseUnix(path);
957 }
958}
959
960
961std::string Path::buildUnix() const
962{
963 std::string result;
964 if (!_device.empty())
965 {
966 result.append("/");
967 result.append(_device);
968 result.append(":/");
969 }
970 else if (_absolute)
971 {
972 result.append("/");
973 }
974 for (StringVec::const_iterator it = _dirs.begin(); it != _dirs.end(); ++it)
975 {
976 result.append(*it);
977 result.append("/");
978 }
979 result.append(_name);
980 return result;
981}
982
983
984std::string Path::buildWindows() const
985{
986 std::string result;
987 if (!_node.empty())
988 {
989 result.append("\\\\");
990 result.append(_node);
991 result.append("\\");
992 }
993 else if (!_device.empty())
994 {
995 result.append(_device);
996 result.append(":\\");
997 }
998 else if (_absolute)
999 {
1000 result.append("\\");
1001 }
1002 for (StringVec::const_iterator it = _dirs.begin(); it != _dirs.end(); ++it)
1003 {
1004 result.append(*it);
1005 result.append("\\");
1006 }
1007 result.append(_name);
1008 return result;
1009}
1010
1011
1012std::string Path::buildVMS() const
1013{
1014 std::string result;
1015 if (!_node.empty())
1016 {
1017 result.append(_node);
1018 result.append("::");
1019 }
1020 if (!_device.empty())
1021 {
1022 result.append(_device);
1023 result.append(":");
1024 }
1025 if (!_dirs.empty())
1026 {
1027 result.append("[");
1028 if (!_absolute && _dirs[0] != "..")
1029 result.append(".");
1030 for (StringVec::const_iterator it = _dirs.begin(); it != _dirs.end(); ++it)
1031 {
1032 if (it != _dirs.begin() && *it != "..")
1033 result.append(".");
1034 if (*it == "..")
1035 result.append("-");
1036 else
1037 result.append(*it);
1038 }
1039 result.append("]");
1040 }
1041 result.append(_name);
1042 if (!_version.empty())
1043 {
1044 result.append(";");
1045 result.append(_version);
1046 }
1047 return result;
1048}
1049
1050
1051std::string Path::transcode(const std::string& path)
1052{
1053#if defined(_WIN32)
1054 std::wstring uniPath;
1055 UnicodeConverter::toUTF16(path, uniPath);
1056 DWORD len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, uniPath.c_str(), static_cast<int>(uniPath.length()), NULL, 0, NULL, NULL);
1057 if (len > 0)
1058 {
1059 Buffer<char> buffer(len);
1060 DWORD rc = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, uniPath.c_str(), static_cast<int>(uniPath.length()), buffer.begin(), static_cast<int>(buffer.size()), NULL, NULL);
1061 if (rc)
1062 {
1063 return std::string(buffer.begin(), buffer.size());
1064 }
1065 }
1066#endif
1067 return path;
1068}
1069
1070
1071} // namespace Poco
1072