~ubuntu-branches/ubuntu/saucy/dune-common/saucy-proposed

« back to all changes in this revision

Viewing changes to dune/common/path.cc

  • Committer: Package Import Robot
  • Author(s): Ansgar Burchardt
  • Date: 2012-03-17 17:15:13 UTC
  • Revision ID: package-import@ubuntu.com-20120317171513-l2eqm95mddmu2dj3
Tags: upstream-2.2~svn6573
Import upstream version 2.2~svn6573

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// -*- tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 
2
// vi: set ts=8 sw=2 et sts=2:
 
3
 
 
4
#if HAVE_CONFIG_H
 
5
#include "config.h"
 
6
#endif
 
7
 
 
8
#include <algorithm>
 
9
#include <iterator>
 
10
#include <string>
 
11
 
 
12
#include <dune/common/exceptions.hh>
 
13
#include <dune/common/misc.hh>
 
14
#include <dune/common/path.hh>
 
15
 
 
16
namespace Dune {
 
17
  /**
 
18
   * @addtogroup Path Filesystem Paths
 
19
   * @ingroup Common
 
20
   * @{
 
21
   */
 
22
 
 
23
  /**
 
24
   * @file
 
25
   * @author Jö Fahlke <jorrit@jorrit.de>
 
26
   * @brief Utilites for handling filesystem paths
 
27
   */
 
28
 
 
29
  //! concatenate two paths
 
30
  std::string concatPaths(const std::string& base, const std::string& p) {
 
31
    if(p == "")              return base;
 
32
    if(p[0] == '/')          return p;
 
33
    if(base == "")           return p;
 
34
    if(hasSuffix(base, "/")) return base+p;
 
35
    else                     return base+'/'+p;
 
36
  }
 
37
 
 
38
  //! sanitize a path for further processing
 
39
  std::string processPath(const std::string& p) {
 
40
    std::string result = p;
 
41
    std::string::size_type src, dst;
 
42
 
 
43
    // append a '/' to non-empty paths
 
44
    if(result != "") result += '/';
 
45
 
 
46
    // each path component now has a trailing '/'
 
47
 
 
48
    // collapse any occurance of multiple '/' to a single '/'
 
49
    dst = src = 0;
 
50
    while(src < result.size()) {
 
51
      result[dst] = result[src];
 
52
      ++src;
 
53
      if(result[dst] == '/')
 
54
        while(src < result.size() && result[src] == '/')
 
55
          ++src;
 
56
      ++dst;
 
57
    }
 
58
    result.resize(dst);
 
59
 
 
60
    // the path is now free of multiple '/' in a row
 
61
 
 
62
    // collapse any occurance of "/./" to "/"
 
63
    dst = src = 0;
 
64
    while(src < result.size()) {
 
65
      result[dst] = result[src];
 
66
      ++src;
 
67
      if(result[dst] == '/')
 
68
        while(src+1 < result.size() && result[src] == '.' &&
 
69
              result[src+1] == '/')
 
70
          src+=2;
 
71
      ++dst;
 
72
    }
 
73
    result.resize(dst);
 
74
 
 
75
    // there may be at most one leading "./".  If so, remove it
 
76
    if(hasPrefix(result, "./")) result.erase(0, 2);
 
77
 
 
78
    // the path is now free of "."-components
 
79
 
 
80
    // remove any "<component>/../" pairs
 
81
    src = 0;
 
82
    while(true) {
 
83
      src = result.find("/../", src);
 
84
      if(src == std::string::npos)
 
85
        break;
 
86
      for(dst = src; dst > 0 && result[dst-1] != '/'; --dst);
 
87
      if(result.substr(dst, src-dst) == "..") {
 
88
        // don't remove "../../"
 
89
        src += 3;
 
90
        continue;
 
91
      }
 
92
      if(dst == src)
 
93
        // special case: "<component>" is the empty component.  This means we
 
94
        // found a leading "/../" in an absolute path, remove "/.."
 
95
        result.erase(0, 3);
 
96
      else {
 
97
        // remove "<component>/../".
 
98
        result.erase(dst, src-dst+4);
 
99
        src = dst;
 
100
        // try to back up one character so we are at a '/' instead of at the
 
101
        // beginning of a component
 
102
        if(src > 0) --src;
 
103
      }
 
104
    }
 
105
 
 
106
    // absolute paths are now free of ".." components, and relative paths
 
107
    // contain only leading ".." components
 
108
 
 
109
    return result;
 
110
  }
 
111
 
 
112
  //! check whether the given path indicates that it is a directory
 
113
  bool pathIndicatesDirectory(const std::string& p) {
 
114
    if(p == "")             return true;
 
115
    if(p == ".")            return true;
 
116
    if(p == "..")           return true;
 
117
    if(hasSuffix(p, "/"))   return true;
 
118
    if(hasSuffix(p, "/."))  return true;
 
119
    if(hasSuffix(p, "/..")) return true;
 
120
    else                    return false;
 
121
  }
 
122
 
 
123
  //! pretty print path
 
124
  std::string prettyPath(const std::string& p, bool isDirectory) {
 
125
    std::string result = processPath(p);
 
126
    // current directory
 
127
    if(result == "")  return ".";
 
128
    // root directory
 
129
    if(result == "/") return result;
 
130
 
 
131
    // remove the trailing '/' for now
 
132
    result.resize(result.size()-1);
 
133
 
 
134
    // if the result ends in "..", we don't need to append '/' to make clear
 
135
    // it's a directory
 
136
    if(result == ".." || hasSuffix(result, "/.."))
 
137
      return result;
 
138
 
 
139
    // if it's a directory, tuck the '/' back on
 
140
    if(isDirectory) result += '/';
 
141
 
 
142
    return result;
 
143
  }
 
144
 
 
145
  //! pretty print path
 
146
  std::string prettyPath(const std::string& p) {
 
147
    return prettyPath(p, pathIndicatesDirectory(p));
 
148
  }
 
149
 
 
150
  //! compute a relative path between two paths
 
151
  std::string relativePath(const std::string& newbase, const std::string& p)
 
152
  {
 
153
    bool absbase = hasPrefix(newbase, "/");
 
154
    bool absp    = hasPrefix(p, "/");
 
155
    if(absbase != absp)
 
156
      DUNE_THROW(NotImplemented, "relativePath: paths must be either both "
 
157
                 "relative or both absolute: newbase=\"" << newbase << "\" "
 
158
                 "p=\"" << p << "\"");
 
159
 
 
160
    std::string mybase = processPath(newbase);
 
161
    std::string myp =    processPath(p);
 
162
 
 
163
    // remove as many matching leading components as possible
 
164
    // determine prefix length
 
165
    std::string::size_type preflen = 0;
 
166
    while(preflen < mybase.size() && preflen < myp.size() &&
 
167
          mybase[preflen] == myp[preflen])
 
168
      ++preflen;
 
169
    // backup to the beginning of the component
 
170
    while(preflen > 0 && myp[preflen-1] != '/')
 
171
      --preflen;
 
172
    mybase.erase(0, preflen);
 
173
    myp.erase(0,preflen);
 
174
 
 
175
    // if mybase contains leading ".." components, we're screwed
 
176
    if(hasPrefix(mybase, "../"))
 
177
      DUNE_THROW(NotImplemented, "relativePath: newbase has too many leading "
 
178
                 "\"..\" components: newbase=\"" << newbase << "\" "
 
179
                 "p=\"" << p << "\"");
 
180
 
 
181
    // count the number of components in mybase
 
182
    typedef std::iterator_traits<std::string::iterator>::difference_type
 
183
      count_t;
 
184
    count_t count = std::count(mybase.begin(), mybase.end(), '/');
 
185
 
 
186
    std::string result;
 
187
    // prefix with that many leading components
 
188
    for(count_t i = 0; i < count; ++i)
 
189
      result += "../";
 
190
    // append what is left of p
 
191
    result += myp;
 
192
 
 
193
    return result;
 
194
  }
 
195
 
 
196
  /** @} group Path */
 
197
}