constinfo for unittest too
[xapian.git] / xapian-applications / omega / diritor.h
blob1dd4eac6998d1201a237c57708db5e6b31cfca1c
1 /* diritor.h: Iterator through entries in a directory.
3 * Copyright (C) 2007,2008,2010,2011,2012,2013,2014,2015 Olly Betts
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 #ifndef OMEGA_INCLUDED_DIRITOR_H
21 #define OMEGA_INCLUDED_DIRITOR_H
23 #include <string>
25 #include "safedirent.h"
26 #include "safeerrno.h"
27 #include "safefcntl.h"
28 #include "safesysstat.h"
29 #include "safeunistd.h"
31 #include <sys/types.h>
33 #ifndef __WIN32__
34 #include <grp.h> // For getgrgid().
35 #include <pwd.h> // For getpwuid().
36 #endif
38 #include <magic.h>
39 #include <zlib.h>
41 #include "loadfile.h"
42 #include "runfilter.h" // For class ReadError.
44 struct FileNotFound { };
46 // Exception to signify changes should be committed, but indexing aborted.
47 class CommitAndExit {
48 std::string msg;
50 public:
51 CommitAndExit(const char * msg_, const std::string & path, int errno_);
52 CommitAndExit(const char * msg_, int errno_);
53 CommitAndExit(const char * msg_, const char * error);
55 const std::string & what() const { return msg; }
58 class DirectoryIterator {
59 #if defined O_NOATIME && O_NOATIME != 0
60 static uid_t euid;
61 #endif
63 static magic_t magic_cookie;
65 std::string path;
66 std::string::size_type path_len;
68 DIR * dir;
69 struct dirent *entry;
70 struct stat statbuf;
71 bool statbuf_valid;
72 bool follow_symlinks;
73 int fd;
75 void call_stat();
77 void ensure_statbuf_valid() {
78 if (!statbuf_valid) {
79 call_stat();
80 statbuf_valid = true;
84 void build_path();
86 public:
88 explicit DirectoryIterator(bool follow_symlinks_)
89 : dir(NULL), follow_symlinks(follow_symlinks_), fd(-1) { }
91 ~DirectoryIterator() {
92 if (dir) closedir(dir);
93 if (fd >= 0) close(fd);
96 /// Start iterating through entries in @a path.
98 // Throws a std::string exception upon failure.
99 void start(const std::string & path);
101 /// Read the next directory entry which doesn't start with ".".
103 // We do this to skip ".", "..", and Unix hidden files.
105 // @return false if there are no more entries.
106 bool next() {
107 if (fd >= 0) {
108 close(fd);
109 fd = -1;
111 path.resize(path_len);
112 errno = 0;
113 do {
114 entry = readdir(dir);
115 } while (entry && entry->d_name[0] == '.');
116 statbuf_valid = false;
117 if (entry == NULL && errno != 0) next_failed();
118 return (entry != NULL);
121 [[noreturn]]
122 void next_failed() const;
124 const char * leafname() const { return entry->d_name; }
126 const std::string & pathname() const { return path; }
128 typedef enum { REGULAR_FILE, DIRECTORY, OTHER } type;
130 type get_type() {
131 #ifdef DT_UNKNOWN
132 /* Possible values:
133 * DT_UNKNOWN DT_FIFO DT_CHR DT_DIR DT_BLK DT_REG DT_LNK DT_SOCK DT_WHT
135 switch (entry->d_type) {
136 case DT_UNKNOWN:
137 // The current filing system doesn't support d_type.
138 break;
139 case DT_REG:
140 return REGULAR_FILE;
141 case DT_DIR:
142 return DIRECTORY;
143 #ifdef HAVE_LSTAT
144 case DT_LNK:
145 if (follow_symlinks) break;
146 return OTHER;
147 #endif
148 default:
149 return OTHER;
151 #endif
153 ensure_statbuf_valid();
155 if (S_ISREG(statbuf.st_mode)) return REGULAR_FILE;
156 if (S_ISDIR(statbuf.st_mode)) return DIRECTORY;
157 return OTHER;
160 off_t get_size() {
161 ensure_statbuf_valid();
162 return statbuf.st_size;
165 time_t get_mtime() {
166 ensure_statbuf_valid();
167 return statbuf.st_mtime;
170 time_t get_ctime() {
171 ensure_statbuf_valid();
172 return statbuf.st_ctime;
175 const char * get_owner() {
176 #ifndef __WIN32__
177 ensure_statbuf_valid();
178 struct passwd * pwentry = getpwuid(statbuf.st_uid);
179 return pwentry ? pwentry->pw_name : NULL;
180 #else
181 return NULL;
182 #endif
185 const char * get_group() {
186 #ifndef __WIN32__
187 ensure_statbuf_valid();
188 struct group * grentry = getgrgid(statbuf.st_gid);
189 return grentry ? grentry->gr_name : NULL;
190 #else
191 return NULL;
192 #endif
195 bool is_owner_readable() {
196 ensure_statbuf_valid();
197 #ifndef __WIN32__
198 return (statbuf.st_mode & S_IRUSR);
199 #else
200 return (statbuf.st_mode & S_IREAD);
201 #endif
204 bool is_group_readable() {
205 ensure_statbuf_valid();
206 #ifndef __WIN32__
207 return (statbuf.st_mode & S_IRGRP);
208 #else
209 return false;
210 #endif
213 bool is_other_readable() {
214 ensure_statbuf_valid();
215 #ifndef __WIN32__
216 return (statbuf.st_mode & S_IROTH);
217 #else
218 return false;
219 #endif
222 bool try_noatime() {
223 #if defined O_NOATIME && O_NOATIME != 0
224 if (euid == 0) return true;
225 ensure_statbuf_valid();
226 return statbuf.st_uid == euid;
227 #else
228 return false;
229 #endif
232 std::string get_magic_mimetype();
234 std::string file_to_string() {
235 build_path();
236 std::string out;
237 int flags = NOCACHE;
238 if (try_noatime()) flags |= NOATIME;
239 fd = load_file_fd(path, out, flags);
240 if (fd < 0) {
241 if (errno == ENOENT || errno == ENOTDIR) throw FileNotFound();
242 throw ReadError("load_file failed");
244 return out;
247 std::string gzfile_to_string() {
248 build_path();
249 std::string out;
250 gzFile zfh = gzopen(path.c_str(), "rb");
251 if (zfh == NULL) {
252 if (errno == ENOENT || errno == ENOTDIR) {
253 throw FileNotFound();
255 throw ReadError("gzopen() failed");
257 char buf[8192];
258 while (true) {
259 int r = gzread(zfh, buf, sizeof(buf));
260 if (r < 0) {
261 gzclose(zfh);
262 throw ReadError("gzread() failed");
264 out.append(buf, r);
265 if (unsigned(r) < sizeof(buf)) break;
267 gzclose(zfh);
268 return out;
272 #endif // OMEGA_INCLUDED_DIRITOR_H