source: libcf++/trunk/src/file.cpp@ 18

Last change on this file since 18 was 18, checked in by cheese, 9 years ago

#1 unify descriptor for initializing

File size: 10.0 KB
Line 
1/**
2 * @file file.cpp
3 * @author myusgun@gmail.com
4 * @brief file
5 */
6#include "cf/file.h"
7
8#include <stdio.h>
9#include <string.h>
10#include <fcntl.h>
11#include <sys/stat.h>
12#include <errno.h>
13
14/* for file system */
15#ifdef _ON_WINDOWS
16# include <io.h>
17# include <direct.h>
18# include "dirent.win32.hpp"
19
20# define DELIMITER "\\"
21# define mkdir(a,b) _mkdir(a)
22# define access(a,b) _access(a,b)
23# define F_OK 0
24# define W_OK 2
25# define R_OK 4
26# define S_IWUSR _S_IWRITE
27# define S_IRUSR _S_IREAD
28# define S_IXUSR _S_IEXEC
29# define S_IRGRP 0x00000000
30# define S_IWGRP 0x00000000
31# define S_IXGRP 0x00000000
32# define S_IROTH 0x00000000
33# define S_IWOTH 0x00000000
34# define S_IXOTH 0x00000000
35# define S_IRWXU 0x00000000
36#else
37# include <unistd.h>
38# include <dirent.h>
39
40# define DELIMITER "/"
41# define O_BINARY 0x00000000
42#endif
43
44/*--------------------------------------------------------------*/
45
46static inline cf::int32_t getFlag(const cf::int32_t flag)
47{
48 cf::int32_t result = 0;
49
50 if (flag & cf::file::READ) result |= O_RDONLY;
51 else if (flag & cf::file::WRITE) result |= O_WRONLY;
52
53 if (flag & cf::file::RW) result |= O_RDWR;
54 if (flag & cf::file::CREATE) result |= O_CREAT;
55 if (flag & cf::file::TRUNC) result |= O_TRUNC;
56 if (flag & cf::file::APPEND) result |= O_APPEND;
57 if (flag & cf::file::EXCL) result |= O_EXCL;
58
59 return result;
60}
61
62static inline cf::int32_t getWhence(const cf::int32_t whence)
63 throw (cf::exception)
64{
65 switch(whence)
66 {
67 case cf::file::BEGIN: return SEEK_SET;
68 case cf::file::CURRENT: return SEEK_CUR;
69 case cf::file::END: return SEEK_END;
70 default:
71 THROW_EXCEPTION("unknown whence");
72 }
73}
74
75static inline cf::void_t splitPath(const std::string & in,
76 std::string & path,
77 std::string & file)
78{
79 size_t found = in.find_last_of(cf::file::getDelimiter());
80
81 if (found == std::string::npos)
82 {
83 path = ".";
84 file = in;
85 }
86 else
87 {
88 path = in.substr(0, found);
89 file = in.substr(found + 1);
90 }
91}
92
93static inline std::string getLockfilePath(const std::string & path)
94{
95 std::string parent;
96 std::string file;
97 std::string lockPath;
98
99 splitPath(path, parent, file);
100 lockPath = parent + cf::file::getDelimiter() + "." + file + ".lock";
101
102 return lockPath;
103}
104
105static inline cf::bool_t compareByName(const cf::file::ENTRY & lhs,
106 const cf::file::ENTRY & rhs)
107{
108 std::size_t len = lhs.mName.length() < rhs.mName.length()
109 ? lhs.mName.length()
110 : rhs.mName.length();
111 cf::int32_t smaller = static_cast<cf::int32_t>(len);
112
113 if (strncmp(lhs.mName.c_str(), rhs.mName.c_str(), smaller) < 0)
114 return true;
115 else
116 return false;
117}
118/*--------------------------------------------------------------*/
119
120cf::file::file(const cf::char_t * path)
121 throw (cf::exception)
122 : mFD(cf::nodesc),
123 mIsLocked(false)
124{
125 if (!path)
126 THROW_EXCEPTION("file path is null");
127
128 mPath = path;
129}
130
131cf::file::~file()
132{
133 close();
134}
135
136const cf::char_t * cf::file::getDelimiter()
137{
138 return DELIMITER;
139}
140
141cf::void_t cf::file::open(const cf::int32_t flag)
142 throw (cf::exception)
143{
144#define FILE_MODE (S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH|S_IREAD|S_IWRITE)
145 if (flag & file::LOCK)
146 mIsLocked = true;
147
148 /* lock */
149 std::string lockfile = getLockfilePath(mPath);
150 cf::int32_t lockfd = ::open(lockfile.c_str(), O_CREAT | O_EXCL, FILE_MODE);
151 if (lockfd < 0)
152 {
153 if (errno == EEXIST)
154 THROW_EXCEPTION("{" << mPath << "} is already locked");
155 else
156 THROW_EXCEPTION("cannot create locking file {"
157 << lockfile << "} (" << errno << ":"
158 << strerror(errno) << ")");
159 }
160 ::close(lockfd);
161
162 if (!mIsLocked)
163 file(lockfile.c_str()).remove();
164
165 /* open */
166 cf::int32_t nativeFlag = getFlag(flag) | O_BINARY;
167 mFD = ::open(mPath.c_str(), nativeFlag, FILE_MODE);
168 if (mFD < 0)
169 THROW_EXCEPTION("cannot open file("
170 << errno << ":" << strerror(errno) << ")");
171}
172
173cf::void_t cf::file::close()
174 throw (cf::exception)
175{
176 if (isOpened())
177 {
178 ::close(mFD);
179 mFD = cf::nodesc;
180 }
181
182 if (mIsLocked)
183 {
184 file lockfile(getLockfilePath(mPath).c_str());
185 if (lockfile.exists())
186 lockfile.remove();
187
188 mIsLocked = false;
189 }
190}
191
192cf::bool_t cf::file::isOpened()
193{
194 return mFD > cf::nodesc ? true : false;
195}
196
197cf::int32_t cf::file::seek(const cf::int32_t offset,
198 const cf::int32_t whence)
199 throw (cf::exception)
200{
201 off_t delta = ::lseek(mFD,
202 static_cast<off_t>(offset),
203 getWhence(whence));
204 if (delta < 0)
205 THROW_EXCEPTION("cannot seek in file("
206 << errno << ":" << strerror(errno) << ")");
207
208 return static_cast<cf::int32_t>(delta);
209}
210
211cf::int32_t cf::file::tell()
212 throw (cf::exception)
213{
214 return seek(0, file::CURRENT);
215}
216
217cf::int32_t cf::file::size()
218 throw (cf::exception)
219{
220 struct stat statbuf;
221
222 if (stat(mPath.c_str(), &statbuf) < 0)
223 THROW_EXCEPTION("cannot get stat {" << mPath << "} ("
224 << errno << ":" << strerror(errno) << ")");
225
226 return static_cast<cf::int32_t>(statbuf.st_size);
227}
228
229cf::bool_t cf::file::exists() const
230{
231 return access(mPath.c_str(), F_OK) ? false : true;
232}
233
234cf::void_t cf::file::read(cf::bin & out)
235 throw (cf::exception)
236{
237 if (out.size() <= 0)
238 THROW_EXCEPTION("read-buffer size is {" << out.size() << "}");
239
240 cf::uint8_t * buf = out.buffer();
241 cf::int32_t bufSize = static_cast<cf::int32_t>(out.size());
242 cf::int32_t readLen = static_cast<cf::int32_t>(::read(mFD, buf, bufSize));
243 if (readLen < 0)
244 THROW_EXCEPTION("cannot read from file("
245 << errno << ":" << strerror(errno) << ")");
246
247 out.set(buf, readLen);
248}
249
250cf::bin cf::file::read(const cf::int32_t len)
251 throw (cf::exception)
252{
253 cf::bin out;
254
255 out.resize(len > 0 ? len : size());
256 read(out);
257
258 return out;
259}
260
261cf::void_t cf::file::write(const cf::bin & in)
262 throw (cf::exception)
263{
264 cf::void_t * buf = reinterpret_cast<cf::void_t *>(in.buffer());
265 size_t len = static_cast<size_t>(in.size());
266 size_t writtenLen = 0;
267
268 if (!buf)
269 THROW_EXCEPTION("writing buffer is null");
270
271 writtenLen = ::write(mFD, buf, len);
272 if (writtenLen != len)
273 THROW_EXCEPTION("cannot write to file("
274 << errno << ":" << strerror(errno) << ")");
275}
276
277cf::void_t cf::file::remove()
278 throw (cf::exception)
279{
280 close();
281
282 if (::remove(mPath.c_str()) < 0)
283 THROW_EXCEPTION("cannot remove file {" << mPath << "} ("
284 << errno << ":" << strerror(errno) << ")");
285}
286
287cf::void_t cf::file::rename(const cf::char_t * path)
288 throw (cf::exception)
289{
290 if (!path)
291 THROW_EXCEPTION("new file path is null");
292
293 close();
294
295 if (::rename(mPath.c_str(), path) < 0)
296 THROW_EXCEPTION("cannot rename file from {" << mPath
297 << "} to {" << path << "} ("
298 << errno << ":" << strerror(errno) << ")");
299}
300
301const cf::char_t * cf::file::getPath() const
302{
303 return mPath.c_str();
304}
305
306cf::bool_t cf::file::isLocked() const
307{
308 return mIsLocked;
309}
310
311cf::void_t cf::file::makedir(const cf::char_t * path)
312 throw (cf::exception)
313{
314 if (!path)
315 THROW_EXCEPTION("directory path is null");
316
317 std::string fullpath = path;
318 std::string step;
319 std::string::iterator iter;
320
321 fullpath += getDelimiter();
322
323 for (iter = fullpath.begin() ; iter != fullpath.end() ; iter++)
324 {
325 step += *iter;
326
327 if (*iter != *getDelimiter())
328 continue;
329
330 if (file(step.c_str()).exists())
331 continue;
332
333#define DIRECTORY_MODE (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
334 cf::int32_t result = mkdir(step.c_str(), DIRECTORY_MODE);
335 if (result && errno != EEXIST)
336 THROW_EXCEPTION("cannot make directory {" << step
337 << "} (" << errno << ":"
338 << strerror(errno)
339 << ")");
340 }
341}
342
343cf::file::EntryList cf::file::getEntryList(const cf::char_t * path,
344 const cf::bool_t exceptDots)
345 throw (cf::exception)
346{
347 if (!path)
348 THROW_EXCEPTION("directory path is null");
349
350 if (!file(path).exists())
351 THROW_EXCEPTION("{" << path << "} does not exist");
352
353 DIR * dirp = NULL;
354 struct dirent * entp = NULL;
355 cf::int32_t error = 0;
356
357 file::EntryList entryList;
358
359 dirp = opendir(path);
360 if (!dirp)
361 THROW_EXCEPTION("cannot open directory {" << path
362 << "} (" << errno << ":"
363 << strerror(errno)
364 << ")");
365
366 error = errno;
367
368 /**
369 * readdir manapge
370 *--------------------------------------------------------------
371 * command : man readdir
372 *--------------------------------------------------------------
373 * On success, readdir() returns a pointer to a dirent structure.
374 * (This structure may be statically allocated; do not attempt to free(3) it.)
375 * If the end of the directory stream is reached, NULL is returned and errno is not changed.
376 * If an error occurs, NULL is returned and errno is set appropriately.
377 *--------------------------------------------------------------
378 *
379 * from. gnu-libc-manual
380 *--------------------------------------------------------------
381 * url : http://www.gnu.org/software/libc/manual/html_node/Reading_002fClosing-Directory.html
382 *--------------------------------------------------------------
383 * In POSIX.1-2008, readdir is not thread-safe.
384 * In the GNU C Library implementation,
385 * it is safe to call readdir concurrently on different dirstreams,
386 * but multiple threads accessing the same dirstream result in undefined behavior.
387 * readdir_r is a fully thread-safe alternative, but suffers from poor portability(see below).
388 * It is recommended that you use readdir, with external locking if multiple threads access the same dirstream.
389 *--------------------------------------------------------------
390 */
391 while ((entp = readdir(dirp)))
392 {
393 /* success */
394 ENTRY ent;
395 ent.mName = entp->d_name;
396 if (exceptDots && (ent.mName == "." || ent.mName == ".."))
397 continue;
398
399 /**
400 * FIXME : set entry type
401 * @see http://stackoverflow.com/questions/3828192/checking-if-a-directory-exists-in-unix-system-call
402 * @remarks
403 * do not use 'd_type' field in 'struct dirent'.
404 * 'd_type' field is not supported in unix operating system(s) such as SunOS/Solaris.
405 * to support linux and unix, S_ISDIR() macro should be used.
406 */
407#if 0
408 switch (entp->d_type)
409 {
410 case DT_DIR: ent.mType = DIRECTORY; break;
411 case DT_REG: ent.mType = REGULAR_FILE; break;
412 default: ent.mType = UNDEFINED; break;
413 }
414#endif
415
416 entryList.push_back(ent);
417 }
418
419 closedir(dirp);
420
421 if (error != errno)
422 THROW_EXCEPTION("cannot read directory {" << path
423 << "} (" << errno << ":" << strerror(errno) << ")");
424
425 entryList.sort(compareByName);
426
427 return entryList;
428}
Note: See TracBrowser for help on using the repository browser.