• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "filepath.h"
6 #include <string.h>
7 #include <sys/stat.h>
8 #include <sys/types.h>
9 #include <vector>
10 #include "utils.h"
11 
AppendToString(std::string * target,std::string & source)12 inline void AppendToString(std::string* target, std::string& source) {
13   target->append(source);
14 }
15 
DirName()16 FilePath FilePath::DirName() {
17   FilePath new_path(path_);
18 
19   std::string::size_type last_separator =
20       new_path.path_.find_last_of(kSeparators, std::string::npos);
21 
22   unsigned int letter = 0;
23   if (last_separator == std::string::npos) {
24     // path_ is in the current directory.
25     new_path.path_.resize(letter + 1);
26   } else if (last_separator == letter + 1) {
27     // path_ is in the root directory.
28     new_path.path_.resize(letter + 2);
29   } else if (last_separator == letter + 2 &&
30              IsSeparator(new_path.path_[letter + 1])) {
31     // path_ is in "//" (possibly with a drive letter); leave the double
32     // separator intact indicating alternate root.
33     new_path.path_.resize(letter + 3);
34   } else if (last_separator != 0) {
35     // path_ is somewhere else, trim the basename.
36     new_path.path_.resize(last_separator);
37   }
38   new_path.StripTrailingSeparatorsInternal();
39   if (!new_path.path_.length())
40     new_path.path_ = kCurrentDirectory;
41 
42   return new_path;
43 }
44 
value() const45 const std::string& FilePath::value() const {
46   return this->path_;
47 }
48 
IsSeparator(char character)49 bool FilePath::IsSeparator(char character) {
50   for (size_t i = 0; i < strlen(kSeparators) - 1; ++i) {
51     if (character == kSeparators[i]) {
52       return true;
53     }
54   }
55   return false;
56 }
57 
Append(const FilePath & path)58 FilePath FilePath::Append(const FilePath& path) {
59   // return FilePath(this->path_ + path.path_);
60   std::string component = path.value();
61   std::string appended = component;
62   std::string without_nuls;
63 
64   std::string::size_type nul_pos = component.find(kStringTerminator);
65   if (nul_pos != std::string::npos) {
66     // without_nuls = component.substr(0, nul_pos);
67     appended = std::string(without_nuls);
68   }
69 
70   // DCHECK(appended.length() <= 0 || appended[0] !=
71   // this->separator.c_str()[0]);
72   if (path_.compare(kCurrentDirectory) == 0 && !appended.empty()) {
73     // Append normally doesn't do any normalization, but as a special case,
74     // when appending to kCurrentDirectory, just return a new path for the
75     // component argument.  Appending component to kCurrentDirectory would
76     // serve no purpose other than needlessly lengthening the path, and
77     // it's likely in practice to wind up with FilePath objects containing
78     // only kCurrentDirectory when calling DirName on a single relative path
79     // component.
80     return FilePath(appended);
81   }
82   FilePath new_path(path_);
83   new_path.StripTrailingSeparatorsInternal();
84 
85   // Don't append a separator if the path is empty (indicating the current
86   // directory) or if the path component is empty (indicating nothing to
87   // append).
88   if (!appended.empty() && !new_path.path_.empty()) {
89     // Don't append a separator if the path still ends with a trailing
90     // separator after stripping (indicating the root directory).
91     char tmp = new_path.path_.back();
92     if (!IsSeparator(tmp)) {
93       // Don't append a separator if the path is just a drive letter.
94       if (FindDriveLetter(new_path.path_) + 1 != new_path.path_.length()) {
95         new_path.path_.append(1, kSeparators[0]);
96       }
97     }
98   }
99   AppendToString(&new_path.path_, appended);
100   return new_path;
101 }
102 
StripTrailingSeparatorsInternal()103 void FilePath::StripTrailingSeparatorsInternal() {
104   // If there is no drive letter, start will be 1, which will prevent
105   // stripping the leading separator if there is only one separator.  If there
106   // is a drive letter, start will be set appropriately to prevent stripping
107   // the first separator following the drive letter, if a separator
108   // immediately follows the drive letter.
109   std::string::size_type start = FindDriveLetter(path_) + 2;
110 
111   std::string::size_type last_stripped = std::string::npos;
112   for (std::string::size_type pos = path_.length();
113        pos > start && IsSeparator(path_[pos - 1]); --pos) {
114     // If the string only has two separators and they're at the beginning,
115     // don't strip them, unless the string began with more than two
116     // separators.
117     if (pos != start + 1 || last_stripped == start + 2 ||
118         !IsSeparator(path_[start - 1])) {
119       path_.resize(pos - 1);
120       last_stripped = pos;
121     }
122   }
123 }
124 
FindDriveLetter(std::string path)125 std::string::size_type FindDriveLetter(std::string path) {
126 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
127   // This is dependent on an ASCII-based character set, but that's a
128   // reasonable assumption.  iswalpha can be too inclusive here.
129   if (path.length() >= 2 && path[1] == L':' &&
130       ((path[0] >= L'A' && path[0] <= L'Z') ||
131        (path[0] >= L'a' && path[0] <= L'z'))) {
132     return 1;
133   }
134 #endif  // FILE_PATH_USES_DRIVE_LETTERS
135   return std::string::npos;
136 }
137 
CreateDirectory(FilePath & full_path)138 bool CreateDirectory(FilePath& full_path) {
139   std::vector<FilePath> subpaths;
140 
141   // Collect a list of all parent directories.
142   FilePath last_path = full_path;
143   subpaths.push_back(full_path);
144   for (FilePath path = full_path.DirName(); path.value() != last_path.value();
145        path = path.DirName()) {
146     subpaths.push_back(path);
147     last_path = path;
148   }
149 
150   // Iterate through the parents and create the missing ones.
151   for (auto i = subpaths.rbegin(); i != subpaths.rend(); ++i) {
152     if (check_dir_existence(i->value().c_str()))
153       continue;
154     if (mkdir(i->value().c_str(), 0700) == 0)
155       continue;
156     // Mkdir failed, but it might have failed with EEXIST, or some other error
157     // due to the the directory appearing out of thin air. This can occur if
158     // two processes are trying to create the same file system tree at the same
159     // time. Check to see if it exists and make sure it is a directory.
160     if (!check_dir_existence(i->value().c_str())) {
161       return false;
162     }
163   }
164   return true;
165 }
166