1 /*
2  * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 #include <memory>
27 #include <algorithm>
28 
29 #include "FileUtils.h"
30 
31 namespace FileUtils {
32 
33 #ifdef _WIN32
34 const tstring::value_type pathSeparator = _T(';');
35 #else
36 const tstring::value_type pathSeparator = _T(':');
37 #endif
38 
39 namespace {
40 #ifdef _WIN32
41     const tstring::value_type dirSeparator = _T('\\');
42     const tstring::value_type alianDirSeparator = _T('/');
43 #else
44     const tstring::value_type dirSeparator = _T('/');
45     const tstring::value_type alianDirSeparator = _T('\\');
46 #endif
47 } // namespace
48 
49 
isDirSeparator(const tstring::value_type c)50 bool isDirSeparator(const tstring::value_type c) {
51     return (c == dirSeparator || c == alianDirSeparator);
52 }
53 
54 
dirname(const tstring & path)55 tstring dirname(const tstring &path) {
56     tstring::size_type pos;
57     if (tstrings::endsWith(path, _T("/.")) || tstrings::endsWith(path, _T("\\."))) {
58         // this method is really getparent dirname - if the path ends with "/.",
59         // we need to ignore that when looking for the last "/" to find parent
60         pos = (path.substr(0, path.length() - 2)).find_last_of(_T("\\/"));
61     } else {
62         pos = path.find_last_of(_T("\\/"));
63     }
64 
65     if (pos != tstring::npos) {
66         pos = path.find_last_not_of(_T("\\/"), pos); // skip trailing slashes
67     }
68     return pos == tstring::npos ? tstring() : path.substr(0, pos + 1);
69 }
70 
71 
basename(const tstring & path)72 tstring basename(const tstring &path) {
73     const tstring::size_type pos = path.find_last_of(_T("\\/"));
74     if (pos == tstring::npos) {
75         return path;
76     }
77     return path.substr(pos + 1);
78 }
79 
80 
suffix(const tstring & path)81 tstring suffix(const tstring &path) {
82     const tstring::size_type pos = path.rfind(_T('.'));
83     if (pos == tstring::npos) {
84         return tstring();
85     }
86     const tstring::size_type dirSepPos = path.find_first_of(_T("\\/"),
87                                                             pos + 1);
88     if (dirSepPos != tstring::npos) {
89         return tstring();
90     }
91     // test for '/..' and '..' cases
92     if (pos != 0 && path[pos - 1] == _T('.')
93                             && (pos == 1 || isDirSeparator(path[pos - 2]))) {
94         return tstring();
95     }
96     return path.substr(pos);
97 }
98 
99 
combinePath(const tstring & parent,const tstring & child)100 tstring combinePath(const tstring& parent, const tstring& child) {
101     if (parent.empty()) {
102         return child;
103     }
104     if (child.empty()) {
105         return parent;
106     }
107 
108     tstring parentWOSlash = removeTrailingSlash(parent);
109     // also handle the case when child contains starting slash
110     bool childHasSlash = isDirSeparator(*child.begin());
111     tstring childWOSlash = childHasSlash ? child.substr(1) : child;
112 
113     return parentWOSlash.append(1, dirSeparator).append(childWOSlash);
114 }
115 
116 
removeTrailingSlash(const tstring & path)117 tstring removeTrailingSlash(const tstring& path) {
118     if (path.empty()) {
119         return path;
120     }
121     tstring::const_reverse_iterator it = path.rbegin();
122     tstring::const_reverse_iterator end = path.rend();
123 
124     while (it != end && isDirSeparator(*it)) {
125         ++it;
126     }
127     return path.substr(0, end - it);
128 }
129 
130 
normalizePath(tstring v)131 tstring normalizePath(tstring v) {
132     std::replace(v.begin(), v.end(), alianDirSeparator, dirSeparator);
133 #ifdef _WIN32
134     return tstrings::toLower(v);
135 #else
136     return v;
137 #endif
138 }
139 
140 
replaceSuffix(const tstring & path,const tstring & newSuffix)141 tstring replaceSuffix(const tstring& path, const tstring& newSuffix) {
142     const tstring oldSuffix = suffix(path);
143     if (oldSuffix.empty()) {
144         return tstring().append(path).append(newSuffix);
145     }
146 
147     return path.substr(0, path.size() - oldSuffix.size()).append(newSuffix);
148 }
149 
150 } //  namespace FileUtils
151