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
00175 #ifndef BLOCXX_WIN32
00176 bool
00177 isExecutable(const String& path)
00178 {
00179 return _ACCESS(path.c_str(), X_OK) == 0;
00180 }
00181 #endif
00182
00184 bool
00185 canRead(const String& path)
00186 {
00187 return _ACCESS(path.c_str(), R_OK) == 0;
00188 }
00190 bool
00191 canWrite(const String& path)
00192 {
00193 return _ACCESS(path.c_str(), W_OK) == 0;
00194 }
00196 #ifndef BLOCXX_WIN32
00197 bool
00198 isLink(const String& path)
00199 {
00200 struct stat st;
00201 if ( ::lstat(path.c_str(), &st) != 0)
00202 {
00203 return false;
00204 }
00205 return S_ISLNK(st.st_mode);
00206 }
00207 #endif
00208
00209 bool
00210 isDirectory(const String& path)
00211 {
00212 #ifdef BLOCXX_WIN32
00213 struct _stat st;
00214 if (_stat(path.c_str(), &st) != 0)
00215 {
00216 return false;
00217 }
00218 return ((st.st_mode & _S_IFDIR) != 0);
00219 #else
00220 struct stat st;
00221 if ( ::stat(path.c_str(), &st) != 0)
00222 {
00223 return false;
00224 }
00225 return S_ISDIR(st.st_mode);
00226 #endif
00227 }
00229 bool
00230 changeDirectory(const String& path)
00231 {
00232 return _CHDIR(path.c_str()) == 0;
00233 }
00235 bool
00236 makeDirectory(const String& path, int mode)
00237 {
00238 return _MKDIR(path.c_str(), mode) == 0;
00239 }
00241 bool
00242 getFileSize(const String& path, off_t& size)
00243 {
00244 #ifdef BLOCXX_WIN32
00245 struct _stat st;
00246 if (_stat(path.c_str(), &st) != 0)
00247 {
00248 return false;
00249 }
00250 #else
00251 struct stat st;
00252 if (::stat(path.c_str(), &st) != 0)
00253 {
00254 return false;
00255 }
00256 #endif
00257 size = st.st_size;
00258 return true;
00259 }
00261 bool
00262 removeDirectory(const String& path)
00263 {
00264 return _RMDIR(path.c_str()) == 0;
00265 }
00267 bool
00268 removeFile(const String& path)
00269 {
00270 return _UNLINK(path.c_str()) == 0;
00271 }
00273 bool
00274 getDirectoryContents(const String& path,
00275 StringArray& dirEntries)
00276 {
00277 static Mutex readdirGuard;
00278 MutexLock lock(readdirGuard);
00279
00280 #ifdef BLOCXX_WIN32
00281 struct _finddata_t dentry;
00282 long hFile;
00283 String _path = path;
00284
00285
00286 if (!_path.endsWith(BLOCXX_FILENAME_SEPARATOR))
00287 {
00288 _path += BLOCXX_FILENAME_SEPARATOR;
00289 }
00290 _path += "*";
00291 if ((hFile = _findfirst( _path.c_str(), &dentry)) == -1L)
00292 {
00293 return false;
00294 }
00295 dirEntries.clear();
00296 while (_findnext(hFile, &dentry) == 0)
00297 {
00298 dirEntries.append(String(dentry.name));
00299 }
00300 _findclose(hFile);
00301 #else
00302 DIR* dp(0);
00303 struct dirent* dentry(0);
00304 if ((dp = opendir(path.c_str())) == NULL)
00305 {
00306 return false;
00307 }
00308 dirEntries.clear();
00309 while ((dentry = readdir(dp)) != NULL)
00310 {
00311 dirEntries.append(String(dentry->d_name));
00312 }
00313 closedir(dp);
00314 #endif
00315 return true;
00316 }
00318 bool
00319 renameFile(const String& oldFileName,
00320 const String& newFileName)
00321 {
00322 return ::rename(oldFileName.c_str(), newFileName.c_str()) == 0;
00323 }
00325 size_t
00326 read(const FileHandle& hdl, void* bfr, size_t numberOfBytes,
00327 off_t offset)
00328 {
00329 #ifdef BLOCXX_WIN32
00330 OVERLAPPED ov = { 0, 0, 0, 0, NULL };
00331 OVERLAPPED *pov = NULL;
00332 if(offset != -1L)
00333 {
00334 ov.Offset = (DWORD) offset;
00335 pov = &ov;
00336 }
00337
00338 DWORD bytesRead;
00339 size_t cc = (size_t)-1;
00340 if(::ReadFile(hdl, bfr, (DWORD)numberOfBytes, &bytesRead, pov))
00341 {
00342 cc = (size_t)bytesRead;
00343 }
00344
00345 return cc;
00346 #else
00347 if (offset != -1L)
00348 {
00349 ::lseek(hdl, offset, SEEK_SET);
00350 }
00351 return ::read(hdl, bfr, numberOfBytes);
00352 #endif
00353 }
00355 size_t
00356 write(FileHandle& hdl, const void* bfr, size_t numberOfBytes,
00357 off_t offset)
00358 {
00359 #ifdef BLOCXX_WIN32
00360 OVERLAPPED ov = { 0, 0, 0, 0, NULL };
00361 OVERLAPPED *pov = NULL;
00362 if(offset != -1L)
00363 {
00364 ov.Offset = (DWORD) offset;
00365 pov = &ov;
00366 }
00367
00368 DWORD bytesWritten;
00369 size_t cc = (size_t)-1;
00370 if(::WriteFile(hdl, bfr, (DWORD)numberOfBytes, &bytesWritten, pov))
00371 {
00372 cc = (size_t)bytesWritten;
00373 }
00374 return cc;
00375 #else
00376
00377 if (offset != -1L)
00378 {
00379 ::lseek(hdl, offset, SEEK_SET);
00380 }
00381 return ::write(hdl, bfr, numberOfBytes);
00382 #endif
00383 }
00385 off_t
00386 seek(const FileHandle& hdl, off_t offset, int whence)
00387 {
00388 #ifdef BLOCXX_WIN32
00389 DWORD moveMethod;
00390 switch(whence)
00391 {
00392 case SEEK_END: moveMethod = FILE_END; break;
00393 case SEEK_CUR: moveMethod = FILE_CURRENT; break;
00394 default: moveMethod = FILE_BEGIN; break;
00395 }
00396 return (off_t) ::SetFilePointer(hdl, (LONG)offset, NULL, moveMethod);
00397 #else
00398 return ::lseek(hdl, offset, whence);
00399 #endif
00400 }
00402 off_t
00403 tell(const FileHandle& hdl)
00404 {
00405 #ifdef BLOCXX_WIN32
00406 return (off_t) ::SetFilePointer(hdl, 0L, NULL, FILE_CURRENT);
00407 #else
00408 return ::lseek(hdl, 0, SEEK_CUR);
00409 #endif
00410 }
00412 void
00413 rewind(const FileHandle& hdl)
00414 {
00415 #ifdef BLOCXX_WIN32
00416 ::SetFilePointer(hdl, 0L, NULL, FILE_BEGIN);
00417 #else
00418 ::lseek(hdl, 0, SEEK_SET);
00419 #endif
00420 }
00422 int
00423 close(const FileHandle& hdl)
00424 {
00425 #ifdef BLOCXX_WIN32
00426 return (::CloseHandle(hdl)) ? 0 : -1;
00427 #else
00428 return ::close(hdl);
00429 #endif
00430 }
00432 int
00433 flush(FileHandle& hdl)
00434 {
00435 #ifdef BLOCXX_WIN32
00436 return (::FlushFileBuffers(hdl)) ? 0 : -1;
00437 #else
00438 #ifdef BLOCXX_DARWIN
00439 return ::fsync(hdl);
00440 #else
00441 return 0;
00442 #endif
00443 #endif
00444 }
00446 void
00447 initRandomFile(const String& filename)
00448 {
00449 #ifdef BLOCXX_WIN32
00450 char bfr[1024];
00451 RandomNumber rnum(0, 0xFF);
00452 for (size_t i = 0; i < 1024; ++i)
00453 {
00454 bfr[i] = (char)rnum.getNextNumber();
00455 }
00456 HANDLE fh = ::CreateFile(filename.c_str(), GENERIC_WRITE,
00457 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
00458 FILE_ATTRIBUTE_NORMAL, NULL);
00459 if(fh == INVALID_HANDLE_VALUE)
00460 {
00461 BLOCXX_THROW(FileSystemException,
00462 Format("Can't open random file %1 for writing",
00463 filename).c_str());
00464 }
00465 DWORD bytesWritten;
00466 size_t cc = (size_t)-1;
00467 bool success = (::WriteFile(fh, bfr, (DWORD)1024, &bytesWritten, NULL) != 0);
00468 ::CloseHandle(fh);
00469 if(!success || bytesWritten < 1024)
00470 {
00471 BLOCXX_THROW(FileSystemException,
00472 Format("Failed writing data to random file %1", filename).c_str());
00473 }
00474 #else
00475 int hdl = ::open(filename.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0600);
00476 if (hdl == -1)
00477 {
00478 BLOCXX_THROW(FileSystemException, Format("Can't open random file %1 for writing", filename).c_str());
00479 }
00480 RandomNumber rnum(0, 0xFF);
00481 for (size_t i = 0; i < 1024; ++i)
00482 {
00483 char c = rnum.getNextNumber();
00484 ::write(hdl, &c, 1);
00485 }
00486 ::close(hdl);
00487 #endif
00488 }
00489
00491 String getFileContents(const String& filename)
00492 {
00493 std::ifstream in(filename.c_str());
00494 if (!in)
00495 {
00496 BLOCXX_THROW(FileSystemException, Format("Failed to open file %1", filename).c_str());
00497 }
00498 OStringStream ss;
00499 ss << in.rdbuf();
00500 return ss.toString();
00501 }
00502
00504 StringArray getFileLines(const String& filename)
00505 {
00506 return getFileContents(filename).tokenize("\r\n");
00507 }
00508
00510 String readSymbolicLink(const String& path)
00511 {
00512 #ifdef BLOCXX_WIN32
00513 return Path::realPath(path);
00514 #else
00515 std::vector<char> buf(MAXPATHLEN);
00516 int rc;
00517 do
00518 {
00519 rc = ::readlink(path.c_str(), &buf[0], buf.size());
00520 if (rc >= 0)
00521 {
00522 buf.resize(rc);
00523 buf.push_back('\0');
00524 return String(&buf[0]);
00525 }
00526 buf.resize(buf.size() * 2);
00527 } while (rc < 0 && errno == ENAMETOOLONG);
00528 BLOCXX_THROW_ERRNO(FileSystemException);
00529 #endif
00530 }
00531
00533 namespace Path
00534 {
00535
00537 String realPath(const String& path)
00538 {
00539 #ifdef BLOCXX_WIN32
00540 char c, *bfr, *pname;
00541 const char *pathcstr;
00542 DWORD cc;
00543
00544 pathcstr = path.c_str();
00545 while (*pathcstr == '/' || *pathcstr == '\\')
00546 {
00547 ++pathcstr;
00548 }
00549
00550
00551
00552 if(pathcstr != path.c_str())
00553 {
00554 --pathcstr;
00555 }
00556
00557 cc = GetFullPathName(path.c_str(), 1, &c, &pname);
00558 if(!cc)
00559 {
00560 BLOCXX_THROW(FileSystemException, Format("Can't get full path name for path %s", path).c_str());
00561 }
00562 bfr = new char[cc];
00563 cc = GetFullPathName(path.c_str(), cc, bfr, &pname);
00564 if(!cc)
00565 {
00566 delete [] bfr;
00567 BLOCXX_THROW(FileSystemException, Format("Can't get full path name for path %s", path).c_str());
00568 }
00569 String rstr(bfr);
00570 delete [] bfr;
00571 return rstr;
00572 #else
00573 String workingPath(path);
00574 String resolvedPath;
00575 int numLinks = 0;
00576
00577
00578 if (workingPath.length() > 0 && workingPath[0] != '/')
00579 {
00580
00581 resolvedPath = getCurrentWorkingDirectory();
00582 }
00583
00584 const char* pathCompBegin(workingPath.c_str());
00585 const char* pathCompEnd(pathCompBegin);
00586 while (*pathCompBegin != '\0')
00587 {
00588
00589 while (*pathCompBegin == '/')
00590 {
00591 ++pathCompBegin;
00592 }
00593
00594
00595 pathCompEnd = pathCompBegin;
00596 while (*pathCompEnd != '\0' && *pathCompEnd != '/')
00597 {
00598 ++pathCompEnd;
00599 }
00600
00601 if (pathCompEnd - pathCompBegin == 0)
00602 {
00603 break;
00604 }
00605 else if (pathCompEnd - pathCompBegin == 1 && pathCompBegin[0] == '.')
00606 {
00607 ;
00608 }
00609 else if (pathCompEnd - pathCompBegin == 2 && pathCompBegin[0] == '.' && pathCompBegin[1] == '.')
00610 {
00611
00612 size_t lastSlash = resolvedPath.lastIndexOf('/');
00613 if (lastSlash != String::npos)
00614 {
00615 resolvedPath.erase(lastSlash);
00616 }
00617 }
00618 else
00619 {
00620 resolvedPath += '/';
00621 resolvedPath += String(pathCompBegin, pathCompEnd - pathCompBegin);
00622
00623
00624 struct stat pathStats;
00625 #ifdef BLOCXX_NETWARE
00626 if (::stat(resolvedPath.c_str(), &pathStats) < 0)
00627 {
00628 BLOCXX_THROW_ERRNO_MSG(FileSystemException, resolvedPath);
00629 }
00630 #else
00631 if (::lstat(resolvedPath.c_str(), &pathStats) < 0)
00632 {
00633 BLOCXX_THROW_ERRNO_MSG(FileSystemException, resolvedPath);
00634 }
00635 if (S_ISLNK(pathStats.st_mode))
00636 {
00637 ++numLinks;
00638 if (numLinks > MAXSYMLINKS)
00639 {
00640 errno = ELOOP;
00641 BLOCXX_THROW_ERRNO_MSG(FileSystemException, resolvedPath);
00642 }
00643 String linkTarget(readSymbolicLink(resolvedPath));
00644
00645 if (linkTarget.length() > 0 && linkTarget[0] != '/')
00646 {
00647
00648 BLOCXX_ASSERT(resolvedPath.lastIndexOf('/') != String::npos);
00649 resolvedPath.erase(resolvedPath.lastIndexOf('/'));
00650
00651 resolvedPath += '/';
00652 resolvedPath += linkTarget;
00653 }
00654 else
00655 {
00656
00657 resolvedPath = linkTarget;
00658 }
00659
00660
00661 resolvedPath += pathCompEnd;
00662 workingPath = resolvedPath;
00663 pathCompBegin = pathCompEnd = workingPath.c_str();
00664 resolvedPath.erase();
00665 }
00666 #endif
00667 }
00668
00669
00670 pathCompBegin = pathCompEnd;
00671 }
00672
00673 if (resolvedPath.empty())
00674 {
00675 resolvedPath = "/";
00676 }
00677
00678 return resolvedPath;
00679 #endif
00680 }
00681
00683 String dirname(const String& filename)
00684 {
00685
00686 size_t lastSlash = filename.length() - 1;
00687 while (lastSlash > 0
00688 && filename[lastSlash] == BLOCXX_FILENAME_SEPARATOR_C)
00689 {
00690 --lastSlash;
00691 }
00692
00693 lastSlash = filename.lastIndexOf(BLOCXX_FILENAME_SEPARATOR_C, lastSlash);
00694
00695 if (lastSlash == String::npos)
00696 {
00697 return ".";
00698 }
00699
00700 while (lastSlash > 0 && filename[lastSlash - 1] == BLOCXX_FILENAME_SEPARATOR_C)
00701 {
00702 --lastSlash;
00703 }
00704
00705 if (lastSlash == 0)
00706 {
00707 return BLOCXX_FILENAME_SEPARATOR;
00708 }
00709
00710 return filename.substring(0, lastSlash);
00711 }
00712
00714 String getCurrentWorkingDirectory()
00715 {
00716 std::vector<char> buf(MAXPATHLEN);
00717 char* p;
00718 do
00719 {
00720 p = ::getcwd(&buf[0], buf.size());
00721 if (p != 0)
00722 {
00723 return p;
00724 }
00725 buf.resize(buf.size() * 2);
00726 } while (p == 0 && errno == ERANGE);
00727
00728 BLOCXX_THROW_ERRNO(FileSystemException);
00729 }
00730
00731 }
00732 }
00733 }
00734