gpath.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2013 Graeme Walker <graeme_walker@users.sourceforge.net>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gpath.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gpath.h"
23 #include "gfs.h"
24 #include "gstr.h"
25 #include "gdebug.h"
26 #include "glog.h"
27 #include "gassert.h"
28 #include <cstring>
29 
31  m_dot(std::string::npos)
32 {
33 }
34 
36 {
37 }
38 
39 G::Path::Path( const std::string & path )
40 {
41  set( path ) ;
42 }
43 
44 G::Path::Path( const char * path )
45 {
46  G_ASSERT( path != NULL ) ;
47  set( std::string(path) ) ;
48 }
49 
50 G::Path::Path( const Path & path , const std::string & tail )
51 {
52  set( path.str() ) ;
53  pathAppend( tail ) ;
54 }
55 
56 G::Path::Path( const Path & path , const std::string & tail_1 , const std::string & tail_2 )
57 {
58  set( path.str() ) ;
59  pathAppend( tail_1 ) ;
60  pathAppend( tail_2 ) ;
61 }
62 
63 G::Path::Path( const Path & other )
64 {
65  set( other.str() ) ;
66 }
67 
68 void G::Path::set( const std::string & path )
69 {
70  clear() ;
71  m_str = path ;
72  normalise() ;
73 }
74 
75 void G::Path::clear()
76 {
77  m_extension.erase() ;
78  m_str.erase() ;
79  m_dot = std::string::npos ;
80 }
81 
82 void G::Path::normalise()
83 {
84  char ns[2] ; char s[2] ; char ss[3] ;
85  s[0] = ss[0] = ss[1] = FileSystem::slash() ;
86  ns[0] = FileSystem::nonSlash() ;
87  ns[1] = s[1] = ss[2] = '\0' ;
88 
89  // normalise spaces
91  Str::replaceAll( m_str , " " , "" ) ;
92 
93  // normalise alternate (non) slash characters
94  Str::replaceAll( m_str , ns , s ) ;
95 
96  // save leading double-slashes
97  bool has_leading_double_slash = FileSystem::leadingDoubleSlash() && m_str.find( ss ) == 0U ;
98 
99  // normalise double slashes
100  Str::replaceAll( m_str , ss , s ) ;
101 
102  // normalise funny characters
103  Str::removeAll( m_str , '\t' ) ;
104  Str::removeAll( m_str , '\n' ) ;
105  Str::removeAll( m_str , '\r' ) ;
106 
107  // remove trailing slashes where appropriate
108  while( (
109  m_str.size() > 1U &&
110  m_str.at(m_str.size()-1) == FileSystem::slash() &&
112  m_str.size() == 3U &&
113  m_str.at(1) == ':' ) ) )
114  {
115  m_str.resize( m_str.size()-1U ) ;
116  }
117 
118  // restore leading double slash
119  if( has_leading_double_slash )
120  m_str = s + m_str ;
121 
122  // prepare a pointer to the extension
123  std::string::size_type slash = m_str.rfind( FileSystem::slash() ) ;
124  m_dot = m_str.rfind( '.' ) ;
125  if( m_dot != std::string::npos && slash != std::string::npos && m_dot < slash ) // ie. if "foo.bar/bletch"
126  m_dot = std::string::npos ;
127 
128  // make a copy of the extension
129  if( m_dot != std::string::npos )
130  {
131  m_extension = (m_dot+1U) == m_str.length() ? std::string() : m_str.substr(m_dot+1U) ;
132  }
133 }
134 
135 std::string G::Path::str() const
136 {
137  return m_str ;
138 }
139 
140 bool G::Path::simple() const
141 {
142  return dirnameImp().empty() ;
143 }
144 
146 {
147  return !isAbsolute() ;
148 }
149 
151 {
152  if( hasNetworkDrive() )
153  return true ;
154 
155  std::string str(m_str) ;
156  if( hasDriveLetter() )
157  str.erase( 0U , driveString().length() ) ;
158 
159  return str.length() > 0U && str.at(0U) == FileSystem::slash() ;
160 }
161 
162 std::string G::Path::basename() const
163 {
164  // for consistency compare m_str with dirname() and return the
165  // difference, excluding any leading slash
166 
167  std::string head( dirnameImp() ) ;
168  if( head.length() == 0 )
169  {
170  return m_str ;
171  }
172  else
173  {
174  std::string result( m_str ) ;
175  result.erase( 0U , head.length() ) ;
176  if( result.at(0) == FileSystem::slash() )
177  result.erase( 0U , 1U ) ;
178  return result ;
179  }
180 }
181 
183 {
184  return Path( dirnameImp() ) ;
185 }
186 
187 std::string G::Path::dirnameImp() const
188 {
189  std::string result ;
190  if( FileSystem::usesDriveLetters() && m_str.size() >= 2 && m_str.at(1) == ':' )
191  {
192  if( hasNoSlash() )
193  {
194  result = m_str.size() > 2 ? driveString() : std::string() ;
195  }
196  else
197  {
198  if( m_str.rfind(FileSystem::slash()) == 2U )
199  {
200  if( m_str.size() == 3U )
201  {
202  result = "" ;
203  }
204  else
205  {
206  result = withoutTail() ;
207  if( result.length() == 2 )
208  result.append( slashString() ) ;
209  }
210  }
211  else
212  {
213  result = withoutTail() ;
214  }
215  }
216  }
217  else if( FileSystem::leadingDoubleSlash() && m_str.size() >= 2U && m_str.substr(0U,2U) == doubleSlashString() )
218  {
219  size_t slash_count = 0U ;
220  for( std::string::const_iterator p = m_str.begin() ; p != m_str.end() ; ++p )
221  if( *p == FileSystem::slash() )
222  slash_count++ ;
223 
224  result = slash_count > 3U ? withoutTail() : std::string() ;
225  }
226  else if( hasNoSlash() || m_str.size() == 1U )
227  {
228  ;
229  }
230  else
231  {
232  result = withoutTail() ;
233  if( result.length() == 0U )
234  result = slashString() ;
235  }
236  return result ;
237 }
238 
239 std::string G::Path::withoutTail() const
240 {
241  G_ASSERT( !hasNoSlash() ) ;
242  return m_str.substr( 0U , m_str.rfind(FileSystem::slash()) ) ;
243 }
244 
245 bool G::Path::hasNoSlash() const
246 {
247  return m_str.find(FileSystem::slash()) == std::string::npos ;
248 }
249 
250 std::string::size_type G::Path::slashAt() const
251 {
252  std::string::size_type position = m_str.find( FileSystem::slash() ) ;
253  G_ASSERT( position != std::string::npos ) ;
254  return position ;
255 }
256 
257 std::string G::Path::slashString()
258 {
259  return std::string( 1U , FileSystem::slash() ) ;
260 }
261 
262 std::string G::Path::doubleSlashString()
263 {
264  return std::string ( 2U , FileSystem::slash() ) ;
265 }
266 
268 {
269  return
271  m_str.size() >= 2U &&
272  m_str.at(1U) == ':' ;
273 }
274 
275 bool G::Path::hasNetworkDrive() const
276 {
277  return
279  m_str.size() > 2U&&
280  m_str.at(0U) == FileSystem::slash() &&
281  m_str.at(1U) == FileSystem::slash() ;
282 }
283 
284 std::string G::Path::driveString() const
285 {
286  G_ASSERT( m_str.at(1U) == ':' ) ;
288  return std::string( 1U , m_str.at(0U) ) + std::string(":") ;
289 }
290 
292 {
293  if( m_dot != std::string::npos )
294  {
295  m_str.resize( m_dot ) ;
296  m_dot = std::string::npos ;
297  normalise() ; // in case of dir/foo.bar.bletch
298  }
299 }
300 
301 void G::Path::pathAppend( const std::string & tail )
302 {
303  if( !tail.empty() )
304  {
305  // if empty or root or just a drive letter...
306  if( m_str.size() == 0U || m_str == slashString() ||
307  ( hasDriveLetter() && m_str == driveString() ) )
308  {
309  ; // no-op
310  }
311  else
312  {
313  m_str.append( slashString() ) ;
314  }
315  m_str.append( tail ) ;
316  normalise() ;
317  }
318 }
319 
320 std::string G::Path::extension() const
321 {
322  return m_extension ;
323 }
324 
325 G::Path G::Path::join( const G::Path & p1 , const G::Path & p2 )
326 {
327  if( p1 == Path() )
328  {
329  return p2 ;
330  }
331  else if( p2 == Path() )
332  {
333  return p1 ;
334  }
335  else
336  {
337  G::Path result( p1 ) ;
338  Strings list = p2.split() ;
339  for( Strings::iterator p = list.begin() ; p != list.end() ; ++p )
340  result.pathAppend( *p ) ;
341  return result ;
342  }
343 }
344 
345 G::Strings G::Path::split( bool no_dot ) const
346 {
347  Path path( *this ) ;
348  Strings list ;
349  for( unsigned int part = 0U ;; part++ )
350  {
351  std::string front = path.dirnameImp() ;
352  std::string back = path.basename() ;
353 
354  // if a dot in the middle or end of the path...
355  if( back == std::string(".") && no_dot && front.length() != 0U )
356  ; // ...ignore it
357  else
358  list.push_front( back ) ;
359 
360  if( front.length() == 0U )
361  break ;
362 
363  path = Path(front) ;
364  }
365  return list ;
366 }
367 
368 bool G::Path::operator==( const Path & other ) const
369 {
370  return
371  ( m_str.empty() && other.m_str.empty() ) ||
372  ( m_str == other.m_str ) ;
373 }
374 
375 bool G::Path::operator!=( const Path & other ) const
376 {
377  return m_str != other.m_str ;
378 }
379 
380 G::Path & G::Path::operator=( const Path & other )
381 {
382  if( &other != this )
383  set( other.str() ) ;
384  return *this ;
385 }
386 
std::string str() const
Returns the path string.
Definition: gpath.cpp:135
Path & operator=(const Path &other)
Assignment operator.
Definition: gpath.cpp:380
Path()
Default constructor.
Definition: gpath.cpp:30
std::string basename() const
Returns the path, excluding drive/directory parts.
Definition: gpath.cpp:162
Strings split(bool no_dot=true) const
Spits the path into a list of component parts.
Definition: gpath.cpp:345
std::list< std::string > Strings
A std::list of std::strings.
Definition: gstrings.h:39
bool operator!=(const Path &path) const
Comparison operator.
Definition: gpath.cpp:375
std::string::size_type size_type
A std::size_t type.
Definition: md5.h:43
bool hasDriveLetter() const
Returns true if the path has a leading drive letter (and the operating system uses drive letters)...
Definition: gpath.cpp:267
std::string extension() const
Returns the path's filename extension.
Definition: gpath.cpp:320
static bool usesDriveLetters()
Definition: gfs_unix.cpp:49
bool simple() const
Returns true if the path is just a file/directory name without any separators.
Definition: gpath.cpp:140
static char slash()
Definition: gfs_unix.cpp:29
bool isRelative() const
Returns true if the path is a relative path.
Definition: gpath.cpp:145
#define G_ASSERT(test)
Definition: gassert.h:30
static unsigned int replaceAll(std::string &s, const std::string &from, const std::string &to)
Does a global replace on string 's', replacing all occurences of sub-string 'from' with 'to'...
Definition: gstr.cpp:78
static bool allowsSpaces()
Definition: gfs_unix.cpp:39
bool isAbsolute() const
Returns !isRelative().
Definition: gpath.cpp:150
bool operator==(const Path &path) const
Comparison operator.
Definition: gpath.cpp:368
Path dirname() const
Returns the drive/directory parts of the path.
Definition: gpath.cpp:182
void pathAppend(const std::string &tail)
Appends a filename to the path.
Definition: gpath.cpp:301
static void removeAll(std::string &, char)
Removes all occurrences of the character from the string.
Definition: gstr.cpp:105
static bool leadingDoubleSlash()
Definition: gfs_unix.cpp:54
A Path object represents a file system path.
Definition: gpath.h:44
void removeExtension()
Modifies the path by removing any extension.
Definition: gpath.cpp:291
static G::Path join(const G::Path &p1, const G::Path &p2)
Joins two paths together.
Definition: gpath.cpp:325
~Path()
Destructor.
Definition: gpath.cpp:35
static char nonSlash()
Definition: gfs_unix.cpp:34