xref: /4.0.0/platform/src/dirutils.cc (revision f46a036a)
1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 *     Copyright 2013 Couchbase, Inc
4 *
5 *   Licensed under the Apache License, Version 2.0 (the "License");
6 *   you may not use this file except in compliance with the License.
7 *   You may obtain a copy of the License at
8 *
9 *       http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *   Unless required by applicable law or agreed to in writing, software
12 *   distributed under the License is distributed on an "AS IS" BASIS,
13 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *   See the License for the specific language governing permissions and
15 *   limitations under the License.
16 */
17#include "config.h"
18#include <platform/dirutils.h>
19
20#ifdef _MSC_VER
21#include <direct.h>
22#define rmdir _rmdir
23#define mkdir(a, b) _mkdir(a)
24#else
25#include <dirent.h>
26#include <unistd.h>
27#endif
28
29#include <stdio.h>
30#include <string.h>
31#include <sys/stat.h>
32
33namespace CouchbaseDirectoryUtilities
34{
35    using namespace std;
36
37    static string split(const string &input, bool directory)
38    {
39        string::size_type path = input.find_last_of("\\/");
40        string file;
41        string dir;
42
43        if (path == string::npos) {
44            dir = ".";
45            file = input;
46        } else {
47            dir = input.substr(0, path);
48            if (dir.length() == 0) {
49                dir = input.substr(0, 1);
50            }
51
52            while (dir.length() > 1 && dir.find_last_of("\\/") == (dir.length() - 1)) {
53                if (dir.length() > 1) {
54                    dir.resize(dir.length() - 1);
55                }
56            }
57
58            file = input.substr(path + 1);
59        }
60
61        if (directory) {
62            return dir;
63        } else {
64            return file;
65        }
66    }
67
68    PLATFORM_PUBLIC_API
69    string dirname(const string &dir)
70    {
71        return split(dir, true);
72    }
73
74    PLATFORM_PUBLIC_API
75    string basename(const string &name)
76    {
77        return split(name, false);
78    }
79
80#ifdef _MSC_VER
81    PLATFORM_PUBLIC_API
82    vector<string> findFilesWithPrefix(const string &dir, const string &name)
83    {
84        vector<string> files;
85        std::string match = dir + "\\" + name + "*";
86        WIN32_FIND_DATA FindFileData;
87
88        HANDLE hFind = FindFirstFileEx(match.c_str(), FindExInfoStandard,
89                                       &FindFileData, FindExSearchNameMatch,
90                                       NULL, 0);
91
92        if (hFind != INVALID_HANDLE_VALUE) {
93            do {
94                string fnm(FindFileData.cFileName);
95                if (fnm != "." && fnm != "..") {
96                    string entry = dir;
97                    entry.append("\\");
98                    entry.append(FindFileData.cFileName);
99                    files.push_back(entry);
100                }
101            } while (FindNextFile(hFind, &FindFileData));
102
103            FindClose(hFind);
104        }
105        return files;
106    }
107#else
108    PLATFORM_PUBLIC_API
109    vector<string> findFilesWithPrefix(const string &dir, const string &name)
110    {
111        vector<string> files;
112        DIR *dp = opendir(dir.c_str());
113        if (dp != NULL) {
114            struct dirent *de;
115            while ((de = readdir(dp)) != NULL) {
116                string fnm(de->d_name);
117                if (fnm == "." || fnm == "..") {
118                    continue;
119                }
120                if (strncmp(de->d_name, name.c_str(), name.length()) == 0) {
121                    string entry = dir;
122                    entry.append("/");
123                    entry.append(de->d_name);
124                    files.push_back(entry);
125                }
126            }
127
128            closedir(dp);
129        }
130        return files;
131    }
132#endif
133
134    PLATFORM_PUBLIC_API
135    vector<string> findFilesWithPrefix(const string &name)
136    {
137        return findFilesWithPrefix(dirname(name), basename(name));
138    }
139
140#ifdef _MSC_VER
141    PLATFORM_PUBLIC_API
142    vector<string> findFilesContaining(const string &dir, const string &name)
143    {
144        vector<string> files;
145        std::string match = dir + "\\*" + name + "*";
146        WIN32_FIND_DATA FindFileData;
147
148        HANDLE hFind = FindFirstFileEx(match.c_str(), FindExInfoStandard,
149                                       &FindFileData, FindExSearchNameMatch,
150                                       NULL, 0);
151
152        if (hFind != INVALID_HANDLE_VALUE) {
153            do {
154                string fnm(FindFileData.cFileName);
155                if (fnm != "." && fnm != "..") {
156                    string entry = dir;
157                    entry.append("\\");
158                    entry.append(FindFileData.cFileName);
159                    files.push_back(entry);
160                }
161            } while (FindNextFile(hFind, &FindFileData));
162
163            FindClose(hFind);
164        }
165        return files;
166    }
167#else
168    PLATFORM_PUBLIC_API
169    vector<string> findFilesContaining(const string &dir, const string &name)
170    {
171        vector<string> files;
172        DIR *dp = opendir(dir.c_str());
173        if (dp != NULL) {
174            struct dirent *de;
175            while ((de = readdir(dp)) != NULL) {
176                if (name.empty() || strstr(de->d_name, name.c_str()) != NULL) {
177                    string fnm(de->d_name);
178                    if (fnm != "." && fnm != "..") {
179                        string entry = dir;
180                        entry.append("/");
181                        entry.append(de->d_name);
182                        files.push_back(entry);
183                    }
184                }
185            }
186
187            closedir(dp);
188        }
189
190        return files;
191    }
192#endif
193
194    PLATFORM_PUBLIC_API
195    bool rmrf(const std::string &path) {
196        struct stat st;
197        if (stat(path.c_str(), &st) == -1) {
198            return false;
199        }
200
201        if ((st.st_mode & S_IFDIR) != S_IFDIR) {
202            return remove(path.c_str()) == 0;
203        }
204
205        if (rmdir(path.c_str()) == 0) {
206            return true;
207        }
208
209        // Ok, this is a directory. Go ahead and delete it recurively
210        std::vector<std::string> directories;
211        directories.push_back(path);
212        do {
213            std::vector<std::string> vec = findFilesContaining(directories.back(), "");
214            directories.pop_back();
215            std::vector<std::string>::iterator ii;
216
217            for (ii = vec.begin(); ii != vec.end(); ++ii) {
218                if (stat(ii->c_str(), &st) == -1) {
219                    return false;
220                }
221
222                if ((st.st_mode & S_IFDIR) == S_IFDIR) {
223                    if (rmdir(ii->c_str()) != 0) {
224                        directories.push_back(*ii);
225                    }
226                } else if (remove(ii->c_str()) != 0) {
227                    return false;
228                }
229            }
230        } while (!directories.empty());
231
232        return rmdir(path.c_str()) == 0;
233    }
234
235    PLATFORM_PUBLIC_API
236    bool isDirectory(const std::string &directory) {
237#ifdef WIN32
238        DWORD dwAttrib = GetFileAttributes(directory.c_str());
239        if (dwAttrib == INVALID_FILE_ATTRIBUTES) {
240            return false;
241        }
242        return (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY;
243#else
244        struct stat st;
245        if (stat(directory.c_str(), &st) == -1) {
246            return false;
247        }
248        return (S_ISDIR(st.st_mode));
249#endif
250    }
251
252    PLATFORM_PUBLIC_API
253    bool mkdirp(const std::string &directory) {
254        struct stat st;
255        if (stat(directory.c_str(), &st) == 0) {
256            if ((st.st_mode & S_IFDIR) == S_IFDIR) {
257                // It exists and is a directory!
258                return true;
259            }
260
261            // It exists but is a file!
262            return false;
263        }
264
265        std::string parent = dirname(directory);
266
267        if (!mkdirp(parent)) {
268            // failed to create parent directory
269            return false;
270        }
271
272        if (mkdir(directory.c_str(), S_IREAD | S_IWRITE | S_IEXEC) != 0) {
273            return false;
274        }
275
276        return true;
277    }
278}
279