00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00038 #include "blocxx/BLOCXX_config.h"
00039 #include "blocxx/FileSystem.hpp"
00040 #include "blocxx/RandomNumber.hpp"
00041 #include "blocxx/Mutex.hpp"
00042 #include "blocxx/MutexLock.hpp"
00043 #include "blocxx/File.hpp"
00044 #include "blocxx/String.hpp"
00045 #include "blocxx/Array.hpp"
00046 #include "blocxx/Format.hpp"
00047 #include "blocxx/ExceptionIds.hpp"
00048 #include "blocxx/Assertion.hpp"
00049
00050 extern "C"
00051 {
00052 #ifdef BLOCXX_WIN32
00053
00054 #include <direct.h>
00055 #include <io.h>
00056 #include <share.h>
00057
00058 #define _ACCESS ::_access
00059 #define R_OK 4
00060 #define F_OK 0
00061 #define W_OK 2
00062 #define _CHDIR _chdir
00063 #define _MKDIR(a,b) _mkdir((a))
00064 #define _RMDIR _rmdir
00065 #define _UNLINK _unlink
00066
00067 #else
00068
00069 #ifdef BLOCXX_HAVE_UNISTD_H
00070 #include <unistd.h>
00071 #endif
00072 #ifdef BLOCXX_HAVE_DIRENT_H
00073 #include <dirent.h>
00074 #endif
00075
00076 #define _ACCESS ::access
00077 #define _CHDIR chdir
00078 #define _MKDIR(a,b) mkdir((a),(b))
00079 #define _RMDIR rmdir
00080 #define _UNLINK unlink
00081
00082 #ifdef BLOCXX_NETWARE
00083 #define MAXSYMLINKS 20
00084 #endif
00085
00086 #endif
00087
00088 #include <sys/stat.h>
00089 #include <sys/types.h>
00090 #include <fcntl.h>
00091 }
00092
00093 #include <cstdio>
00094 #include <fstream>
00095 #include <cerrno>
00096
00097 namespace BLOCXX_NAMESPACE
00098 {
00099
00100 BLOCXX_DEFINE_EXCEPTION_WITH_ID(FileSystem);
00101
00102 namespace FileSystem
00103 {
00104
00106
00107 int
00108 changeFileOwner(const String& filename,
00109 const UserId& userId)
00110 {
00111 #ifdef BLOCXX_WIN32
00112 return 0;
00113 #else
00114 return ::chown(filename.c_str(), userId, gid_t(-1));
00115 #endif
00116 }
00118
00119 File
00120 openFile(const String& path)
00121 {
00122 #ifdef BLOCXX_WIN32
00123 HANDLE fh = ::CreateFile(path.c_str(), GENERIC_READ | GENERIC_WRITE,
00124 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
00125 FILE_ATTRIBUTE_NORMAL, NULL);
00126
00127 return (fh != INVALID_HANDLE_VALUE) ? File(fh) : File();
00128 #else
00129 return File(::open(path.c_str(), O_RDWR));
00130 #endif
00131 }
00133
00134 File
00135 createFile(const String& path)
00136 {
00137 #ifdef BLOCXX_WIN32
00138 HANDLE fh = ::CreateFile(path.c_str(), GENERIC_READ | GENERIC_WRITE,
00139 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_NEW,
00140 FILE_ATTRIBUTE_NORMAL, NULL);
00141 return (fh != INVALID_HANDLE_VALUE) ? File(fh) : File();
00142 #else
00143 int fd = ::open(path.c_str(), O_CREAT | O_EXCL | O_TRUNC | O_RDWR, 0660);
00144 if (fd != -1)
00145 {
00146 return File(fd);
00147 }
00148 return File();
00149 #endif
00150
00151 }
00153
00154 File
00155 openOrCreateFile(const String& path)
00156 {
00157 #ifdef BLOCXX_WIN32
00158 HANDLE fh = ::CreateFile(path.c_str(), GENERIC_READ | GENERIC_WRITE,
00159 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS,
00160 FILE_ATTRIBUTE_NORMAL, NULL);
00161 return (fh != INVALID_HANDLE_VALUE) ? File(fh) : File();
00162 #else
00163 return File(::open(path.c_str(), O_RDWR | O_CREAT, 0660));
00164 #endif
00165 }
00166
00168 bool
00169 exists(const String& path)
00170 {
00171 return _ACCESS(path.c_str(), F_OK) == 0;
00172 }
00173
00174 bool
00175 isExecutable(const String& path)
00176 {
00177 return _ACCESS(path.c_str(), X_OK) == 0;
00178 }
00179
00181 bool
00182 canRead(const String& path)
00183 {
00184 return _ACCESS(path.c_str(), R_OK) == 0;
00185 }
00187 bool
00188 canWrite(const String& path)
00189 {
00190 return _ACCESS(path.c_str(), W_OK) == 0;
00191 }
00193 bool
00194 isLink(const String& path)
00195 {
00196 #ifdef BLOCXX_WIN32
00197 return false;
00198
00199
00200
00201
00202
00203
00204
00205
00206 #else
00207 struct stat st;
00208 if ( ::lstat(path.c_str(), &st) != 0)
00209 {
00210 return false;
00211 }
00212 return S_ISLNK(st.st_mode);
00213 #endif
00214 }
00216 bool
00217 isDirectory(const String& path)
00218 {
00219 #ifdef BLOCXX_WIN32
00220 struct _stat st;
00221 if (_stat(path.c_str(), &st) != 0)
00222 {
00223 return false;
00224 }
00225 return ((st.st_mode & _S_IFDIR) != 0);
00226 #else
00227 struct stat st;
00228 if ( ::stat(path.c_str(), &st) != 0)
00229 {
00230 return false;
00231 }
00232 return S_ISDIR(st.st_mode);
00233 #endif
00234 }
00236 bool
00237 changeDirectory(const String& path)
00238 {
00239 return _CHDIR(path.c_str()) == 0;
00240 }
00242 bool
00243 makeDirectory(const String& path, int mode)
00244 {
00245 return _MKDIR(path.c_str(), mode) == 0;
00246 }
00248 bool
00249 getFileSize(const String& path, off_t& size)
00250 {
00251 #ifdef BLOCXX_WIN32
00252 struct _stat st;
00253 if (_stat(path.c_str(), &st) != 0)
00254 {
00255 return false;
00256 }
00257 #else
00258 struct stat st;
00259 if (::stat(path.c_str(), &st) != 0)
00260 {
00261 return false;
00262 }
00263 #endif
00264 size = st.st_size;
00265 return true;
00266 }
00268 bool
00269 removeDirectory(const String& path)
00270 {
00271 return _RMDIR(path.c_str()) == 0;
00272 }
00274 bool
00275 removeFile(const String& path)
00276 {
00277 return _UNLINK(path.c_str()) == 0;
00278 }
00280 bool
00281 getDirectoryContents(const String& path,
00282 StringArray& dirEntries)
00283 {
00284 static Mutex readdirGuard;
00285 MutexLock lock(readdirGuard);
00286
00287 #ifdef BLOCXX_WIN32
00288 struct _finddata_t dentry;
00289 long hFile;
00290 String _path = path;
00291
00292
00293 if (!_path.endsWith(BLOCXX_FILENAME_SEPARATOR))
00294 {
00295 _path += BLOCXX_FILENAME_SEPARATOR;
00296 }
00297 _path += "*";
00298 if ((hFile = _findfirst( _path.c_str(), &dentry)) == -1L)
00299 {
00300 return false;
00301 }
00302 dirEntries.clear();
00303 while (_findnext(hFile, &dentry) == 0)
00304 {
00305 dirEntries.append(String(dentry.name));
00306 }
00307 _findclose(hFile);
00308 #else
00309 DIR* dp(0);
00310 struct dirent* dentry(0);
00311 if ((dp = opendir(path.c_str())) == NULL)
00312 {
00313 return false;
00314 }
00315 dirEntries.clear();
00316 while ((dentry = readdir(dp)) != NULL)
00317 {
00318 dirEntries.append(String(dentry->d_name));
00319 }
00320 closedir(dp);
00321 #endif
00322 return true;
00323 }
00325 bool
00326 renameFile(const String& oldFileName,
00327 const String& newFileName)
00328 {
00329 return ::rename(oldFileName.c_str(), newFileName.c_str()) == 0;
00330 }
00332 size_t
00333 read(const FileHandle& hdl, void* bfr, size_t numberOfBytes,
00334 off_t offset)
00335 {
00336 #ifdef BLOCXX_WIN32
00337 OVERLAPPED ov = { 0, 0, 0, 0, NULL };
00338 OVERLAPPED *pov = NULL;
00339 if(offset != -1L)
00340 {
00341 ov.Offset = (DWORD) offset;
00342 pov = &ov;
00343 }
00344
00345 DWORD bytesRead;
00346 size_t cc = (size_t)-1;
00347 if(::ReadFile(hdl, bfr, (DWORD)numberOfBytes, &bytesRead, pov))
00348 {
00349 cc = (size_t)bytesRead;
00350 }
00351
00352 return cc;
00353 #else
00354 if (offset != -1L)
00355 {
00356 ::lseek(hdl, offset, SEEK_SET);
00357 }
00358 return ::read(hdl, bfr, numberOfBytes);
00359 #endif
00360 }
00362 size_t
00363 write(FileHandle& hdl, const void* bfr, size_t numberOfBytes,
00364 off_t offset)
00365 {
00366 #ifdef BLOCXX_WIN32
00367 OVERLAPPED ov = { 0, 0, 0, 0, NULL };
00368 OVERLAPPED *pov = NULL;
00369 if(offset != -1L)
00370 {
00371 ov.Offset = (DWORD) offset;
00372 pov = &ov;
00373 }
00374
00375 DWORD bytesWritten;
00376 size_t cc = (size_t)-1;
00377 if(::WriteFile(hdl, bfr, (DWORD)numberOfBytes, &bytesWritten, pov))
00378 {
00379 cc = (size_t)bytesWritten;
00380 }
00381 return cc;
00382 #else
00383
00384 if (offset != -1L)
00385 {
00386 ::lseek(hdl, offset, SEEK_SET);
00387 }
00388 return ::write(hdl, bfr, numberOfBytes);
00389 #endif
00390 }
00392 off_t
00393 seek(const FileHandle& hdl, off_t offset, int whence)
00394 {
00395 #ifdef BLOCXX_WIN32
00396 DWORD moveMethod;
00397 switch(whence)
00398 {
00399 case SEEK_END: moveMethod = FILE_END; break;
00400 case SEEK_CUR: moveMethod = FILE_CURRENT; break;
00401 default: moveMethod = FILE_BEGIN; break;
00402 }
00403 return (off_t) ::SetFilePointer(hdl, (LONG)offset, NULL, moveMethod);
00404 #else
00405 return ::lseek(hdl, offset, whence);
00406 #endif
00407 }
00409 off_t
00410 tell(const FileHandle& hdl)
00411 {
00412 #ifdef BLOCXX_WIN32
00413 return (off_t) ::SetFilePointer(hdl, 0L, NULL, FILE_CURRENT);
00414 #else
00415 return ::lseek(hdl, 0, SEEK_CUR);
00416 #endif
00417 }
00419 void
00420 rewind(const FileHandle& hdl)
00421 {
00422 #ifdef BLOCXX_WIN32
00423 ::SetFilePointer(hdl, 0L, NULL, FILE_BEGIN);
00424 #else
00425 ::lseek(hdl, 0, SEEK_SET);
00426 #endif
00427 }
00429 int
00430 close(const FileHandle& hdl)
00431 {
00432 #ifdef BLOCXX_WIN32
00433 return (::CloseHandle(hdl)) ? 0 : -1;
00434 #else
00435 return ::close(hdl);
00436 #endif
00437 }
00439 int
00440 flush(FileHandle& hdl)
00441 {
00442 #ifdef BLOCXX_WIN32
00443 return (::FlushFileBuffers(hdl)) ? 0 : -1;
00444 #else
00445 #ifdef BLOCXX_DARWIN
00446 return ::fsync(hdl);
00447 #else
00448 return 0;
00449 #endif
00450 #endif
00451 }
00453 void
00454 initRandomFile(const String& filename)
00455 {
00456 #ifdef BLOCXX_WIN32
00457 char bfr[1024];
00458 RandomNumber rnum(0, 0xFF);
00459 for (size_t i = 0; i < 1024; ++i)
00460 {
00461 bfr[i] = (char)rnum.getNextNumber();
00462 }
00463 HANDLE fh = ::CreateFile(filename.c_str(), GENERIC_WRITE,
00464 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
00465 FILE_ATTRIBUTE_NORMAL, NULL);
00466 if(fh == INVALID_HANDLE_VALUE)
00467 {
00468 BLOCXX_THROW(FileSystemException,
00469 Format("Can't open random file %1 for writing",
00470 filename).c_str());
00471 }
00472 DWORD bytesWritten;
00473 size_t cc = (size_t)-1;
00474 bool success = (::WriteFile(fh, bfr, (DWORD)1024, &bytesWritten, NULL) != 0);
00475 ::CloseHandle(fh);
00476 if(!success || bytesWritten < 1024)
00477 {
00478 BLOCXX_THROW(FileSystemException,
00479 Format("Failed writing data to random file %1", filename).c_str());
00480 }
00481 #else
00482 int hdl = ::open(filename.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0600);
00483 if (hdl == -1)
00484 {
00485 BLOCXX_THROW(FileSystemException, Format("Can't open random file %1 for writing", filename).c_str());
00486 }
00487 RandomNumber rnum(0, 0xFF);
00488 for (size_t i = 0; i < 1024; ++i)
00489 {
00490 char c = rnum.getNextNumber();
00491 ::write(hdl, &c, 1);
00492 }
00493 ::close(hdl);
00494 #endif
00495 }
00496
00498 String getFileContents(const String& filename)
00499 {
00500 std::ifstream in(filename.c_str());
00501 if (!in)
00502 {
00503 BLOCXX_THROW(FileSystemException, Format("Failed to open file %1", filename).c_str());
00504 }
00505 OStringStream ss;
00506 ss << in.rdbuf();
00507 return ss.toString();
00508 }
00509
00511 StringArray getFileLines(const String& filename)
00512 {
00513 return getFileContents(filename).tokenize("\r\n");
00514 }
00515
00517 String readSymbolicLink(const String& path)
00518 {
00519 #ifdef BLOCXX_WIN32
00520 return Path::realPath(path);
00521 #else
00522 std::vector<char> buf(MAXPATHLEN);
00523 int rc;
00524 do
00525 {
00526 rc = ::readlink(path.c_str(), &buf[0], buf.size());
00527 if (rc >= 0)
00528 {
00529 buf.resize(rc);
00530 buf.push_back('\0');
00531 return String(&buf[0]);
00532 }
00533 buf.resize(buf.size() * 2);
00534 } while (rc < 0 && errno == ENAMETOOLONG);
00535 BLOCXX_THROW_ERRNO(FileSystemException);
00536 #endif
00537 }
00538
00540 namespace Path
00541 {
00542
00544 String realPath(const String& path)
00545 {
00546 #ifdef BLOCXX_WIN32
00547 char c, *bfr, *pname;
00548 const char *pathcstr;
00549 DWORD cc;
00550
00551 pathcstr = path.c_str();
00552 while (*pathcstr == '/' || *pathcstr == '\\')
00553 {
00554 ++pathcstr;
00555 }
00556
00557
00558
00559 if(pathcstr != path.c_str())
00560 {
00561 --pathcstr;
00562 }
00563
00564 cc = GetFullPathName(path.c_str(), 1, &c, &pname);
00565 if(!cc)
00566 {
00567 BLOCXX_THROW(FileSystemException, Format("Can't get full path name for path %s", path).c_str());
00568 }
00569 bfr = new char[cc];
00570 cc = GetFullPathName(path.c_str(), cc, bfr, &pname);
00571 if(!cc)
00572 {
00573 delete [] bfr;
00574 BLOCXX_THROW(FileSystemException, Format("Can't get full path name for path %s", path).c_str());
00575 }
00576 String rstr(bfr);
00577 delete [] bfr;
00578 return rstr;
00579 #else
00580 String workingPath(path);
00581 String resolvedPath;
00582 int numLinks = 0;
00583
00584
00585 if (workingPath.length() > 0 && workingPath[0] != '/')
00586 {
00587
00588 resolvedPath = getCurrentWorkingDirectory();
00589 }
00590
00591 const char* pathCompBegin(workingPath.c_str());
00592 const char* pathCompEnd(pathCompBegin);
00593 while (*pathCompBegin != '\0')
00594 {
00595
00596 while (*pathCompBegin == '/')
00597 {
00598 ++pathCompBegin;
00599 }
00600
00601
00602 pathCompEnd = pathCompBegin;
00603 while (*pathCompEnd != '\0' && *pathCompEnd != '/')
00604 {
00605 ++pathCompEnd;
00606 }
00607
00608 if (pathCompEnd - pathCompBegin == 0)
00609 {
00610 break;
00611 }
00612 else if (pathCompEnd - pathCompBegin == 1 && pathCompBegin[0] == '.')
00613 {
00614 ;
00615 }
00616 else if (pathCompEnd - pathCompBegin == 2 && pathCompBegin[0] == '.' && pathCompBegin[1] == '.')
00617 {
00618
00619 size_t lastSlash = resolvedPath.lastIndexOf('/');
00620 if (lastSlash != String::npos)
00621 {
00622 resolvedPath.erase(lastSlash);
00623 }
00624 }
00625 else
00626 {
00627 resolvedPath += '/';
00628 resolvedPath += String(pathCompBegin, pathCompEnd - pathCompBegin);
00629
00630
00631 struct stat pathStats;
00632 #ifdef BLOCXX_NETWARE
00633 if (::stat(resolvedPath.c_str(), &pathStats) < 0)
00634 {
00635 BLOCXX_THROW_ERRNO_MSG(FileSystemException, resolvedPath);
00636 }
00637 #else
00638 if (::lstat(resolvedPath.c_str(), &pathStats) < 0)
00639 {
00640 BLOCXX_THROW_ERRNO_MSG(FileSystemException, resolvedPath);
00641 }
00642 if (S_ISLNK(pathStats.st_mode))
00643 {
00644 ++numLinks;
00645 if (numLinks > MAXSYMLINKS)
00646 {
00647 errno = ELOOP;
00648 BLOCXX_THROW_ERRNO_MSG(FileSystemException, resolvedPath);
00649 }
00650 String linkTarget(readSymbolicLink(resolvedPath));
00651
00652 if (linkTarget.length() > 0 && linkTarget[0] != '/')
00653 {
00654
00655 BLOCXX_ASSERT(resolvedPath.lastIndexOf('/') != String::npos);
00656 resolvedPath.erase(resolvedPath.lastIndexOf('/'));
00657
00658 resolvedPath += '/';
00659 resolvedPath += linkTarget;
00660 }
00661 else
00662 {
00663
00664 resolvedPath = linkTarget;
00665 }
00666
00667
00668 resolvedPath += pathCompEnd;
00669 workingPath = resolvedPath;
00670 pathCompBegin = pathCompEnd = workingPath.c_str();
00671 resolvedPath.erase();
00672 }
00673 #endif
00674 }
00675
00676
00677 pathCompBegin = pathCompEnd;
00678 }
00679
00680 if (resolvedPath.empty())
00681 {
00682 resolvedPath = "/";
00683 }
00684
00685 return resolvedPath;
00686 #endif
00687 }
00688
00690 String dirname(const String& filename)
00691 {
00692
00693 size_t lastSlash = filename.length() - 1;
00694 while (lastSlash > 0
00695 && filename[lastSlash] == BLOCXX_FILENAME_SEPARATOR_C)
00696 {
00697 --lastSlash;
00698 }
00699
00700 lastSlash = filename.lastIndexOf(BLOCXX_FILENAME_SEPARATOR_C, lastSlash);
00701
00702 if (lastSlash == String::npos)
00703 {
00704 return ".";
00705 }
00706
00707 while (lastSlash > 0 && filename[lastSlash - 1] == BLOCXX_FILENAME_SEPARATOR_C)
00708 {
00709 --lastSlash;
00710 }
00711
00712 if (lastSlash == 0)
00713 {
00714 return BLOCXX_FILENAME_SEPARATOR;
00715 }
00716
00717 return filename.substring(0, lastSlash);
00718 }
00719
00721 String getCurrentWorkingDirectory()
00722 {
00723 std::vector<char> buf(MAXPATHLEN);
00724 char* p;
00725 do
00726 {
00727 p = ::getcwd(&buf[0], buf.size());
00728 if (p != 0)
00729 {
00730 return p;
00731 }
00732 buf.resize(buf.size() * 2);
00733 } while (p == 0 && errno == ERANGE);
00734
00735 BLOCXX_THROW_ERRNO(FileSystemException);
00736 }
00737
00738 }
00739 }
00740 }
00741