xapian-core  1.4.27
fileutils.cc
Go to the documentation of this file.
1 
4 /* Copyright (C) 2008 Lemur Consulting Ltd
5  * Copyright (C) 2008,2009,2010,2012 Olly Betts
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
22 #include <config.h>
23 
24 #include "fileutils.h"
25 
26 #include "xapian/error.h"
27 #include "safedirent.h"
28 #include "safeunistd.h"
29 
30 #include <cerrno>
31 #include <cstring>
32 #include <string>
33 #include <sys/types.h>
34 
35 using namespace std;
36 
37 class dircloser {
38  DIR * dir;
39  public:
40  dircloser(DIR * dir_) : dir(dir_) {}
42  if (dir != NULL) {
43  closedir(dir);
44  dir = NULL;
45  }
46  }
47 };
48 
49 void
50 removedir(const string &dirname)
51 {
52  DIR * dir;
53 
54  dir = opendir(dirname.c_str());
55  if (dir == NULL) {
56  if (errno == ENOENT) return;
57  throw Xapian::DatabaseError("Cannot open directory '" + dirname + "'", errno);
58  }
59 
60  {
61  dircloser dc(dir);
62  while (true) {
63  errno = 0;
64  struct dirent * entry = readdir(dir);
65  if (entry == NULL) {
66  if (errno == 0)
67  break;
68  throw Xapian::DatabaseError("Cannot read entry from directory at '" + dirname + "'", errno);
69  }
70  string name(entry->d_name);
71  if (name == "." || name == "..")
72  continue;
73  if (unlink((dirname + "/" + name).c_str())) {
74  throw Xapian::DatabaseError("Cannot remove file '" + string(entry->d_name) + "'", errno);
75  }
76  }
77  }
78  if (rmdir(dirname.c_str())) {
79  throw Xapian::DatabaseError("Cannot remove directory '" + dirname + "'", errno);
80  }
81 }
82 
83 #ifdef __WIN32__
84 static bool
86 has_drive(const string &path)
87 {
88  return (path.size() >= 2 && path[1] == ':');
89 }
90 
92 static bool
93 uncw_path(const string & path)
94 {
95  return (path.size() >= 4 && memcmp(path.data(), "\\\\?\\", 4) == 0);
96 }
97 
98 static inline bool slash(char ch)
99 {
100  return ch == '/' || ch == '\\';
101 }
102 #endif
103 
104 void
105 resolve_relative_path(string & path, const string & base)
106 {
107 #ifndef __WIN32__
108  if (path.empty() || path[0] != '/') {
109  // path is relative.
110  string::size_type last_slash = base.rfind('/');
111  if (last_slash != string::npos)
112  path.insert(0, base, 0, last_slash + 1);
113  }
114 #else
115  // Microsoft Windows paths may begin with a drive letter but still be
116  // relative within that drive.
117  bool drive = has_drive(path);
118  string::size_type p = (drive ? 2 : 0);
119  bool absolute = (p != path.size() && slash(path[p]));
120 
121  if (absolute) {
122  // If path is absolute and has a drive specifier, just return it.
123  if (drive)
124  return;
125 
126  // If base has a drive specifier prepend that to path.
127  if (has_drive(base)) {
128  path.insert(0, base, 0, 2);
129  return;
130  }
131 
132  // If base has a UNC (\\SERVER\\VOLUME) or \\?\ prefix, prepend that
133  // to path.
134  if (uncw_path(base)) {
135  string::size_type sl = 0;
136  if (base.size() >= 7 && memcmp(base.data() + 5, ":\\", 2) == 0) {
137  // "\\?\X:\"
138  sl = 6;
139  } else if (base.size() >= 8 &&
140  memcmp(base.data() + 4, "UNC\\", 4) == 0) {
141  // "\\?\UNC\server\volume\"
142  sl = base.find('\\', 8);
143  if (sl != string::npos)
144  sl = base.find('\\', sl + 1);
145  }
146  if (sl) {
147  // With the \\?\ prefix, '/' isn't recognised so change it
148  // to '\' in path.
149  string::iterator i;
150  for (i = path.begin(); i != path.end(); ++i) {
151  if (*i == '/')
152  *i = '\\';
153  }
154  path.insert(0, base, 0, sl);
155  }
156  } else if (base.size() >= 5 && slash(base[0]) && slash(base[1])) {
157  // Handle UNC base.
158  string::size_type sl = base.find_first_of("/\\", 2);
159  if (sl != string::npos) {
160  sl = base.find_first_of("/\\", sl + 1);
161  path.insert(0, base, 0, sl);
162  }
163  }
164  return;
165  }
166 
167  // path is relative, so if it has no drive specifier or the same drive
168  // specifier as base, then we want to qualify it using base.
169  bool base_drive = has_drive(base);
170  if (!drive || (base_drive && (path[0] | 32) == (base[0] | 32))) {
171  string::size_type last_slash = base.find_last_of("/\\");
172  if (last_slash == string::npos && !drive && base_drive)
173  last_slash = 1;
174  if (last_slash != string::npos) {
175  string::size_type b = (drive && base_drive ? 2 : 0);
176  if (uncw_path(base)) {
177  // With the \\?\ prefix, '/' isn't recognised so change it
178  // to '\' in path.
179  string::iterator i;
180  for (i = path.begin(); i != path.end(); ++i) {
181  if (*i == '/')
182  *i = '\\';
183  }
184  }
185  path.insert(b, base, b, last_slash + 1 - b);
186  }
187  }
188 #endif
189 }
int closedir(DIR *)
struct dirent * readdir(DIR *)
STL namespace.
include <dirent.h>, with alternative implementation for windows.
Hierarchy of classes which Xapian can throw as exceptions.
DIR * opendir(const char *)
char * d_name
Definition: msvc_dirent.h:36
DIR * dir
Definition: fileutils.cc:38
struct DIR DIR
Definition: msvc_dirent.h:32
~dircloser()
Definition: fileutils.cc:41
dircloser(DIR *dir_)
Definition: fileutils.cc:40
void resolve_relative_path(string &path, const string &base)
Resolve path relative to base.
Definition: fileutils.cc:105
char name[9]
Definition: dbcheck.cc:55
<unistd.h>, but with compat.
void removedir(const string &dirname)
Remove a directory, and its contents.
Definition: fileutils.cc:50
DatabaseError indicates some sort of database related error.
Definition: error.h:367
File and path manipulation routines.