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

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

#1 commit prototype

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