Line data Source code
1 : // Copyright (c) 2011-2022 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/snapdev
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 : #pragma once
20 :
21 : // self
22 : //
23 : #include "snapdev/reverse_cstring.h"
24 :
25 :
26 : // C++ lib
27 : //
28 : #include <string>
29 :
30 :
31 :
32 : namespace snapdev
33 : {
34 : namespace pathinfo
35 : {
36 :
37 :
38 : /** \brief Retrieve the basename of a path.
39 : *
40 : * This function retrieves the basename of a path. You can also remove the
41 : * suffix (often called a file extension) and a prefix.
42 : *
43 : * \code
44 : * // the following returns true
45 : * snapdev::pathinfo::basename(
46 : * "/usr/share/snapwebsites/in.basename.txt"
47 : * , ".txt"
48 : * , "in.") == "basename"
49 : * \endcode
50 : *
51 : * To remove the suffix, whatever it is, use the special pattern ".*".
52 : *
53 : * \tparam StringT The type of string to parse.
54 : * \param[in] path The path from which basename gets retrieved.
55 : * \param[in] suffix If the path ends with that suffix, remove it.
56 : * \param[in] prefix If the path starts with that prefix, remove it.
57 : *
58 : * \return The basename of \p path.
59 : */
60 : template<class StringT>
61 : StringT basename(StringT const & path
62 : , typename std::decay<StringT>::type const & suffix = ""
63 : , typename std::decay<StringT>::type const & prefix = "")
64 : {
65 : // ignore path if present
66 : //
67 : typename StringT::size_type pos(path.find_last_of('/'));
68 : if(pos == StringT::npos)
69 : {
70 : // if no '/' in string, the entire name is a basename
71 : //
72 : pos = 0;
73 : }
74 : else
75 : {
76 : ++pos; // skip the actual '/'
77 : }
78 :
79 : // ignore prefix if present
80 : //
81 : if(prefix.length() <= path.length() - pos
82 : && path.compare(pos, prefix.length(), prefix) == 0)
83 : {
84 : pos += prefix.length();
85 : }
86 :
87 : // if the path ends with suffix, then return the path without it
88 : //
89 : if(suffix.length() == 2
90 : && suffix[0] == '.'
91 : && suffix[1] == '*')
92 : {
93 : typename StringT::size_type end(path.rfind('.'));
94 : if(end != StringT::npos && end > pos)
95 : {
96 : // whatever the suffix is
97 : //
98 : return path.substr(pos, end - pos);
99 : }
100 : }
101 : else if(suffix.length() <= path.length() - pos
102 : && path.compare(path.length() - suffix.length(), suffix.length(), suffix) == 0)
103 : {
104 : // remove a specific suffix
105 : //
106 : return path.substr(pos, path.length() - pos - suffix.length());
107 : }
108 :
109 : // ignore possible suffix
110 : //
111 : return path.substr(pos);
112 : }
113 :
114 :
115 : /** \brief Replace the suffix with another.
116 : *
117 : * This function checks whether a file ends with a given suffix. If so then
118 : * the existing suffix gets removed. Then it happens the new suffix.
119 : *
120 : * The function is not checking whether a suffix starts with a period.
121 : * It can include any other character.
122 : *
123 : * \code
124 : * // the following expressions return true
125 : * snap::string_pathinfo_replace_suffix(
126 : * "/usr/share/snapwebsites/replace.cpp"
127 : * , ".cpp"
128 : * , ".h") == "/usr/share/snapwebsites/replace.h"
129 : *
130 : * snap::string_pathinfo_replace_suffix(
131 : * "/usr/share/snapwebsites/replace"
132 : * , ".cpp"
133 : * , ".h") == "/usr/share/snapwebsites/replace.h"
134 : * \endcode
135 : *
136 : * \note
137 : * By default, the \p new_suffix parameter is set to the empty string.
138 : * This means the function can be used to trim the string from
139 : * \p old_suffix.
140 : *
141 : * \todo
142 : * Add a function which supports an array of \p old_suffix.
143 : *
144 : * \tparam StringT The type of string to parse.
145 : * \param[in] path The path from which to replace a suffix.
146 : * \param[in] old_suffix If the path ends with that suffix, remove it.
147 : * \param[in] new_suffix Append this suffix.
148 : * \param[in] no_change_on_missing If old_suffix is missing, do not change
149 : * the \t path.
150 : *
151 : * \return \p path with its suffix replaced as defined above.
152 : */
153 : template<class StringT>
154 28 : StringT replace_suffix(
155 : StringT const & path
156 : , typename std::decay<StringT>::type const & old_suffix
157 : , typename std::decay<StringT>::type const & new_suffix = ""
158 : , bool no_change_on_missing = false)
159 : {
160 : // TODO: with C++20 we could use: path.ends_with(old_suffix)
161 : //
162 56 : if(path.length() >= old_suffix.length()
163 28 : && path.c_str() + path.length() - old_suffix.length() == old_suffix)
164 : {
165 12 : return path.substr(0, path.length() - old_suffix.length()) + new_suffix;
166 : }
167 :
168 16 : if(no_change_on_missing)
169 : {
170 4 : return path;
171 : }
172 :
173 12 : return path + new_suffix;
174 : }
175 :
176 :
177 : /** \brief Retrieve the directory name of a path.
178 : *
179 : * This function retrieves the directory name of a path. The returned path
180 : * is the empty string if the input does not include any '/'.
181 : *
182 : * \code
183 : * // the following returns true
184 : * snap::string_pathinfo_dirname(
185 : * "/usr/share/snapwebsites/in.filename.txt");
186 : * == "/usr/share/snapwebsites";
187 : * \endcode
188 : *
189 : * \param[in] path The path from which basename gets retrieved.
190 : *
191 : * \return The directory name of \p path.
192 : */
193 : template < class StringT >
194 2 : StringT dirname(StringT const & path)
195 : {
196 2 : typename StringT::size_type pos(path.find_last_of('/'));
197 2 : if(pos == StringT::npos)
198 : {
199 0 : return StringT();
200 : }
201 2 : else if(pos == 0)
202 : {
203 0 : if(path[0] == '/')
204 : {
205 0 : return StringT("/");
206 : }
207 0 : return StringT(".");
208 : }
209 : else
210 : {
211 2 : return path.substr(0, pos);
212 : }
213 : }
214 :
215 : } // namespace pathinfo
216 : } // namespace snapdev
217 : // vim: ts=4 sw=4 et
|