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

Last change on this file since 26 was 26, checked in by cheese, 8 years ago

#1 fix memory overlap

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
248cf::bin cf::file::read(const cf::int32_t len)
249 throw (cf::exception)
250{
251 cf::bin out;
252
253 out.resize(len > 0 ? len : size());
254 read(out);
255
256 return out;
257}
258
259cf::void_t cf::file::write(const cf::bin & in)
260 throw (cf::exception)
261{
262 cf::void_t * buf = reinterpret_cast<cf::void_t *>(in.buffer());
263 size_t len = static_cast<size_t>(in.size());
264 size_t writtenLen = 0;
265
266 if (!buf)
267 THROW_EXCEPTION("writing buffer is null");
268
269 writtenLen = ::write(mFD, buf, len);
270 if (writtenLen != len)
271 THROW_EXCEPTION("cannot write to file("
272 << errno << ":" << strerror(errno) << ")");
273}
274
275cf::void_t cf::file::remove()
276 throw (cf::exception)
277{
278 close();
279
280 if (::remove(mPath.c_str()) < 0)
281 THROW_EXCEPTION("cannot remove file {" << mPath << "} ("
282 << errno << ":" << strerror(errno) << ")");
283}
284
285cf::void_t cf::file::rename(const cf::char_t * path)
286 throw (cf::exception)
287{
288 if (!path)
289 THROW_EXCEPTION("new file path is null");
290
291 close();
292
293 if (::rename(mPath.c_str(), path) < 0)
294 THROW_EXCEPTION("cannot rename file from {" << mPath
295 << "} to {" << path << "} ("
296 << errno << ":" << strerror(errno) << ")");
297}
298
299const cf::char_t * cf::file::getPath() const
300{
301 return mPath.c_str();
302}
303
304cf::bool_t cf::file::isLocked() const
305{
306 return mIsLocked;
307}
308
309cf::void_t cf::file::makedir(const cf::char_t * path)
310 throw (cf::exception)
311{
312 if (!path)
313 THROW_EXCEPTION("directory path is null");
314
315 std::string fullpath = path;
316 std::string step;
317 std::string::iterator iter;
318
319 fullpath += getDelimiter();
320
321 for (iter = fullpath.begin() ; iter != fullpath.end() ; iter++)
322 {
323 step += *iter;
324
325 if (*iter != *getDelimiter())
326 continue;
327
328 if (file(step.c_str()).exists())
329 continue;
330
331#define DIRECTORY_MODE (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
332 cf::int32_t result = mkdir(step.c_str(), DIRECTORY_MODE);
333 if (result && errno != EEXIST)
334 THROW_EXCEPTION("cannot make directory {" << step
335 << "} (" << errno << ":"
336 << strerror(errno)
337 << ")");
338 }
339}
340
341cf::file::EntryList cf::file::getEntryList(const cf::char_t * path,
342 const cf::bool_t exceptDots)
343 throw (cf::exception)
344{
345 if (!path)
346 THROW_EXCEPTION("directory path is null");
347
348 if (!file(path).exists())
349 THROW_EXCEPTION("{" << path << "} does not exist");
350
351 DIR * dirp = NULL;
352 struct dirent * entp = NULL;
353 cf::int32_t error = 0;
354
355 file::EntryList entryList;
356
357 dirp = opendir(path);
358 if (!dirp)
359 THROW_EXCEPTION("cannot open directory {" << path
360 << "} (" << errno << ":"
361 << strerror(errno)
362 << ")");
363
364 error = errno;
365
366 /**
367 * readdir manapge
368 *--------------------------------------------------------------
369 * command : man readdir
370 *--------------------------------------------------------------
371 * On success, readdir() returns a pointer to a dirent structure.
372 * (This structure may be statically allocated; do not attempt to free(3) it.)
373 * If the end of the directory stream is reached, NULL is returned and errno is not changed.
374 * If an error occurs, NULL is returned and errno is set appropriately.
375 *--------------------------------------------------------------
376 *
377 * from. gnu-libc-manual
378 *--------------------------------------------------------------
379 * url : http://www.gnu.org/software/libc/manual/html_node/Reading_002fClosing-Directory.html
380 *--------------------------------------------------------------
381 * In POSIX.1-2008, readdir is not thread-safe.
382 * In the GNU C Library implementation,
383 * it is safe to call readdir concurrently on different dirstreams,
384 * but multiple threads accessing the same dirstream result in undefined behavior.
385 * readdir_r is a fully thread-safe alternative, but suffers from poor portability(see below).
386 * It is recommended that you use readdir, with external locking if multiple threads access the same dirstream.
387 *--------------------------------------------------------------
388 */
389 while ((entp = readdir(dirp)))
390 {
391 /* success */
392 ENTRY ent;
393 ent.mName = entp->d_name;
394 if (exceptDots && (ent.mName == "." || ent.mName == ".."))
395 continue;
396
397 /**
398 * FIXME : set entry type
399 * @see http://stackoverflow.com/questions/3828192/checking-if-a-directory-exists-in-unix-system-call
400 * @remarks
401 * do not use 'd_type' field in 'struct dirent'.
402 * 'd_type' field is not supported in unix operating system(s) such as SunOS/Solaris.
403 * to support linux and unix, S_ISDIR() macro should be used.
404 */
405#if 0
406 switch (entp->d_type)
407 {
408 case DT_DIR: ent.mType = DIRECTORY; break;
409 case DT_REG: ent.mType = REGULAR_FILE; break;
410 default: ent.mType = UNDEFINED; break;
411 }
412#endif
413
414 entryList.push_back(ent);
415 }
416
417 closedir(dirp);
418
419 if (error != errno)
420 THROW_EXCEPTION("cannot read directory {" << path
421 << "} (" << errno << ":" << strerror(errno) << ")");
422
423 entryList.sort(compareByName);
424
425 return entryList;
426}
Note: See TracBrowser for help on using the repository browser.