src/corelib/ncbifile.cpp

Go to the documentation of this file.
00001 /*  $Id: ncbifile.cpp 170959 2009-09-18 13:43:55Z lavr $
00002  * ===========================================================================
00003  *
00004  *                            PUBLIC DOMAIN NOTICE
00005  *               National Center for Biotechnology Information
00006  *
00007  *  This software/database is a "United States Government Work" under the
00008  *  terms of the United States Copyright Act.  It was written as part of
00009  *  the author's official duties as a United States Government employee and
00010  *  thus cannot be copyrighted.  This software/database is freely available
00011  *  to the public for use. The National Library of Medicine and the U.S.
00012  *  Government have not placed any restriction on its use or reproduction.
00013  *
00014  *  Although all reasonable efforts have been taken to ensure the accuracy
00015  *  and reliability of the software and data, the NLM and the U.S.
00016  *  Government do not and cannot warrant the performance or results that
00017  *  may be obtained by using this software or data. The NLM and the U.S.
00018  *  Government disclaim all warranties, express or implied, including
00019  *  warranties of performance, merchantability or fitness for any particular
00020  *  purpose.
00021  *
00022  *  Please cite the author in any work or product based on this material.
00023  *
00024  * ===========================================================================
00025  *
00026  * Author:  Vladimir Ivanov
00027  *
00028  * File Description:   Files and directories accessory functions
00029  *
00030  */
00031 
00032 #include <ncbi_pch.hpp>
00033 #include <corelib/ncbifile.hpp>
00034 #include <corelib/ncbi_limits.h>
00035 #include <corelib/ncbi_system.hpp>
00036 #include <corelib/ncbi_safe_static.hpp>
00037 #include <corelib/error_codes.hpp>
00038 
00039 #include <stdio.h>
00040 
00041 #if defined(NCBI_OS_MSWIN)
00042 #  include "ncbi_os_mswin_p.hpp"
00043 #  include <corelib/ncbi_limits.hpp>
00044 #  include <io.h>
00045 #  include <direct.h>
00046 #  include <sys/utime.h>
00047 #  include <fcntl.h> // for _O_* flags
00048 
00049 #elif defined(NCBI_OS_UNIX)
00050 #  include <unistd.h>
00051 #  include <dirent.h>
00052 #  include <pwd.h>
00053 #  include <fcntl.h>
00054 #  include <sys/time.h>
00055 #  include <sys/mman.h>
00056 #  ifdef HAVE_SYS_STATVFS_H
00057 #    include <sys/statvfs.h>
00058 #  endif
00059 #  include <sys/param.h>
00060 #  ifdef HAVE_SYS_MOUNT_H
00061 #    include <sys/mount.h>
00062 #  endif
00063 #  ifdef HAVE_SYS_VFS_H
00064 #    include <sys/vfs.h>
00065 #  endif
00066 #  include <utime.h>
00067 #  include <pwd.h>
00068 #  include <grp.h>
00069 #  if !defined(MAP_FAILED)
00070 #    define MAP_FAILED ((void *) -1)
00071 #  endif
00072 
00073 #else
00074 #  error "File API defined for MS Windows and UNIX platforms only"
00075 
00076 #endif  /* NCBI_OS_MSWIN, NCBI_OS_UNIX */
00077 
00078 
00079 #define NCBI_USE_ERRCODE_X   Corelib_File
00080 
00081 
00082 BEGIN_NCBI_SCOPE
00083 
00084 
00085 // Path separators
00086 
00087 #undef  DIR_SEPARATOR
00088 #undef  DIR_SEPARATOR_ALT
00089 #undef  DIR_SEPARATORS
00090 #undef  DISK_SEPARATOR
00091 #undef  ALL_SEPARATORS
00092 #undef  ALL_OS_SEPARATORS
00093 
00094 #define DIR_PARENT  ".."
00095 #define DIR_CURRENT "."
00096 #define ALL_OS_SEPARATORS   ":/\\"
00097 
00098 #if defined(NCBI_OS_MSWIN)
00099 #  define DIR_SEPARATOR     '\\'
00100 #  define DIR_SEPARATOR_ALT '/'
00101 #  define DISK_SEPARATOR    ':'
00102 #  define DIR_SEPARATORS    "/\\"
00103 #  define ALL_SEPARATORS    ":/\\"
00104 #elif defined(NCBI_OS_UNIX)
00105 #  define DIR_SEPARATOR     '/'
00106 #  define DIR_SEPARATORS    "/"
00107 #  define ALL_SEPARATORS    "/"
00108 #endif
00109 
00110 // Macro to check bits
00111 #define F_ISSET(flags, mask) ((flags & (mask)) == (mask))
00112 
00113 // Default buffer size, used to read/write files
00114 const size_t kDefaultBufferSize = 32*1024;
00115 
00116 // List of files for CFileDeleteAtExit class
00117 static CSafeStaticRef< CFileDeleteList > s_DeleteAtExitFileList;
00118 
00119 
00120 // Declare the parameter to get directory for temporary files.
00121 // Registry file:
00122 //     [NCBI]
00123 //     TmpDir = ...
00124 // Environment variable:
00125 //     NCBI_CONFIG__TmpDir
00126 //
00127 NCBI_PARAM_DECL(string, NCBI, TmpDir); 
00128 NCBI_PARAM_DEF (string, NCBI, TmpDir, kEmptyStr);
00129 
00130 
00131 // Define how read-only files are treated on Windows.
00132 // Registry file:
00133 //     [NCBI]
00134 //     DeleteReadOnlyFiles = true/false
00135 // Environment variable:
00136 //     NCBI_CONFIG__DeleteReadOnlyFiles
00137 //
00138 NCBI_PARAM_DECL(bool, NCBI, DeleteReadOnlyFiles);
00139 NCBI_PARAM_DEF_EX(bool, NCBI, DeleteReadOnlyFiles, false,
00140     eParam_NoThread, NCBI_CONFIG__DeleteReadOnlyFiles);
00141 
00142 
00143 // Declare the parameter to turn on logging from CFile,
00144 // CDirEntry, etc. classes.
00145 // Registry file:
00146 //     [NCBI]
00147 //     FileAPILogging = true/false
00148 // Environment variable:
00149 //     NCBI_CONFIG__FileAPILogging
00150 //
00151 #define DEFAULT_LOGGING_VALUE false
00152 
00153 NCBI_PARAM_DECL(bool, NCBI, FileAPILogging);
00154 NCBI_PARAM_DEF_EX(bool, NCBI, FileAPILogging, DEFAULT_LOGGING_VALUE,
00155     eParam_NoThread, NCBI_CONFIG__FileAPILogging);
00156 
00157 #define LOG_ERROR(log_message) \
00158     { \
00159         int saved_error = errno; \
00160         if (NCBI_PARAM_TYPE(NCBI, FileAPILogging)::GetDefault()) { \
00161             ERR_POST(log_message << ": " << strerror(saved_error)); \
00162         } \
00163         errno = saved_error; \
00164     }
00165 
00166 #define LOG_ERROR_AND_RETURN(log_message) \
00167     { \
00168         if (NCBI_PARAM_TYPE(NCBI, FileAPILogging)::GetDefault()) { \
00169             ERR_POST(log_message); \
00170         } \
00171         return false; \
00172     }
00173 
00174 #define LOG_ERROR_AND_RETURN_ERRNO(log_message) \
00175     { \
00176         int saved_error = errno; \
00177         if (NCBI_PARAM_TYPE(NCBI, FileAPILogging)::GetDefault()) { \
00178             ERR_POST(log_message << ": " << strerror(saved_error)); \
00179         } \
00180         errno = saved_error; \
00181         return false; \
00182     }
00183 
00184 //////////////////////////////////////////////////////////////////////////////
00185 //
00186 // CDirEntry
00187 //
00188 
00189 CDirEntry::CDirEntry(const string& name)
00190 {
00191     Reset(name);
00192     m_DefaultMode[eUser]    = m_DefaultModeGlobal[eFile][eUser];
00193     m_DefaultMode[eGroup]   = m_DefaultModeGlobal[eFile][eGroup];
00194     m_DefaultMode[eOther]   = m_DefaultModeGlobal[eFile][eOther];
00195     m_DefaultMode[eSpecial] = m_DefaultModeGlobal[eFile][eSpecial];
00196 }
00197 
00198 
00199 CDirEntry::CDirEntry(const CDirEntry& other)
00200     : m_Path(other.m_Path)
00201 {
00202     m_DefaultMode[eUser]    = other.m_DefaultMode[eUser];
00203     m_DefaultMode[eGroup]   = other.m_DefaultMode[eGroup];
00204     m_DefaultMode[eOther]   = other.m_DefaultMode[eOther];
00205     m_DefaultMode[eSpecial] = other.m_DefaultMode[eSpecial];
00206 }
00207 
00208 
00209 CDirEntry::~CDirEntry(void)
00210 {
00211     return;
00212 }
00213 
00214 
00215 CDirEntry* CDirEntry::CreateObject(EType type, const string& path)
00216 {
00217     CDirEntry *ptr = 0;
00218     switch ( type ) {
00219         case eFile:
00220             ptr = new CFile(path);
00221             break;
00222         case eDir:
00223             ptr = new CDir(path);
00224             break;
00225         case eLink:
00226             ptr = new CSymLink(path);
00227             break;
00228         default:
00229             ptr = new CDirEntry(path);
00230             break;
00231     }
00232     return ptr;
00233 }
00234 
00235 
00236 void CDirEntry::Reset(const string& path)
00237 {
00238     m_Path = path;
00239     size_t len = path.length();
00240     // Root dir
00241     if ((len == 1)  &&  IsPathSeparator(path[0])) {
00242         return;
00243     }
00244     // Disk name
00245 #  if defined(DISK_SEPARATOR)
00246     if ( (len == 2 || len == 3) && (path[1] == DISK_SEPARATOR) ) {
00247         return;
00248     }
00249 #  endif
00250     m_Path = DeleteTrailingPathSeparator(path);
00251 }
00252 
00253 
00254 CDirEntry::TMode CDirEntry::m_DefaultModeGlobal[eUnknown][4] =
00255 {
00256     // eFile
00257     { CDirEntry::fDefaultUser, CDirEntry::fDefaultGroup, 
00258           CDirEntry::fDefaultOther, 0 },
00259     // eDir
00260     { CDirEntry::fDefaultDirUser, CDirEntry::fDefaultDirGroup, 
00261           CDirEntry::fDefaultDirOther, 0 },
00262     // ePipe
00263     { CDirEntry::fDefaultUser, CDirEntry::fDefaultGroup, 
00264           CDirEntry::fDefaultOther, 0 },
00265     // eLink
00266     { CDirEntry::fDefaultUser, CDirEntry::fDefaultGroup, 
00267           CDirEntry::fDefaultOther, 0 },
00268     // eSocket
00269     { CDirEntry::fDefaultUser, CDirEntry::fDefaultGroup, 
00270           CDirEntry::fDefaultOther, 0 },
00271     // eDoor
00272     { CDirEntry::fDefaultUser, CDirEntry::fDefaultGroup, 
00273           CDirEntry::fDefaultOther, 0 },
00274     // eBlockSpecial
00275     { CDirEntry::fDefaultUser, CDirEntry::fDefaultGroup, 
00276           CDirEntry::fDefaultOther, 0 },
00277     // eCharSpecial
00278     { CDirEntry::fDefaultUser, CDirEntry::fDefaultGroup, 
00279           CDirEntry::fDefaultOther, 0 }
00280 };
00281 
00282 
00283 // Default backup suffix
00284 const char* CDirEntry::m_BackupSuffix = ".bak";
00285 
00286 
00287 CDirEntry& CDirEntry::operator= (const CDirEntry& other)
00288 {
00289     if (this != &other) {
00290         m_Path                  = other.m_Path;
00291         m_DefaultMode[eUser]    = other.m_DefaultMode[eUser];
00292         m_DefaultMode[eGroup]   = other.m_DefaultMode[eGroup];
00293         m_DefaultMode[eOther]   = other.m_DefaultMode[eOther];
00294         m_DefaultMode[eSpecial] = other.m_DefaultMode[eSpecial];
00295     }
00296     return *this;
00297 }
00298 
00299 
00300 void CDirEntry::SplitPath(const string& path, string* dir,
00301                           string* base, string* ext)
00302 {
00303     // Get file name
00304     size_t pos = path.find_last_of(ALL_SEPARATORS);
00305     string filename = (pos == NPOS) ? path : path.substr(pos+1);
00306 
00307     // Get dir
00308     if ( dir ) {
00309         *dir = (pos == NPOS) ? kEmptyStr : path.substr(0, pos+1);
00310     }
00311     // Split file name to base and extension
00312     pos = filename.rfind('.');
00313     if ( base ) {
00314         *base = (pos == NPOS) ? filename : filename.substr(0, pos);
00315     }
00316     if ( ext ) {
00317         *ext = (pos == NPOS) ? kEmptyStr : filename.substr(pos);
00318     }
00319 }
00320 
00321 
00322 void CDirEntry::SplitPathEx(const string& path,
00323                             string* disk, string* dir,
00324                             string* base, string* ext)
00325 {
00326     size_t start_pos = 0;
00327 
00328     // Get disk
00329     if ( disk ) {
00330         if ( isalpha((unsigned char)path[0])  &&  path[1] == ':' ) {
00331             *disk = path.substr(0, 2);
00332             start_pos = 2;
00333         } else {
00334             *disk = kEmptyStr;
00335         }
00336     }
00337     // Get file name
00338     size_t pos = path.find_last_of(ALL_OS_SEPARATORS);
00339     string filename = (pos == NPOS) ? path : path.substr(pos+1);
00340     // Get dir
00341     if ( dir ) {
00342         *dir = (pos == NPOS) ? kEmptyStr : 
00343                                path.substr(start_pos, pos - start_pos + 1);
00344     }
00345     // Split file name to base and extension
00346     pos = filename.rfind('.');
00347     if ( base ) {
00348         *base = (pos == NPOS) ? filename : filename.substr(0, pos);
00349     }
00350     if ( ext ) {
00351         *ext = (pos == NPOS) ? kEmptyStr : filename.substr(pos);
00352     }
00353 }
00354 
00355 
00356 string CDirEntry::MakePath(const string& dir, const string& base, 
00357                            const string& ext)
00358 {
00359     string path;
00360 
00361     // Adding "dir" and file base
00362     if ( dir.length() ) {
00363         path = AddTrailingPathSeparator(dir);
00364     }
00365     path += base;
00366     // Adding extension
00367     if ( ext.length()  &&  ext.at(0) != '.' ) {
00368         path += '.';
00369     }
00370     path += ext;
00371     // Return result
00372     return path;
00373 }
00374 
00375 
00376 char CDirEntry::GetPathSeparator(void) 
00377 {
00378     return DIR_SEPARATOR;
00379 }
00380 
00381 
00382 bool CDirEntry::IsPathSeparator(const char c)
00383 {
00384 #if defined(DISK_SEPARATOR)
00385     if ( c == DISK_SEPARATOR ) {
00386         return true;
00387     }
00388 #endif
00389 #if defined(DIR_SEPARATOR_ALT)
00390     if ( c == DIR_SEPARATOR_ALT ) {
00391         return true;
00392     }
00393 #endif
00394     return c == DIR_SEPARATOR;
00395 }
00396 
00397 
00398 string CDirEntry::AddTrailingPathSeparator(const string& path)
00399 {
00400     size_t len = path.length();
00401     if (len  &&  string(ALL_SEPARATORS).rfind(path.at(len - 1)) == NPOS) {
00402         return path + GetPathSeparator();
00403     }
00404     return path;
00405 }
00406 
00407 
00408 string CDirEntry::DeleteTrailingPathSeparator(const string& path)
00409 {
00410     size_t pos = path.find_last_not_of(DIR_SEPARATORS);
00411     if (pos + 1 < path.length()) {
00412         return path.substr(0, pos + 1);
00413     }
00414     return path;
00415 }
00416 
00417 
00418 string CDirEntry::GetDir(EIfEmptyPath mode) const
00419 {
00420     string dir;
00421     SplitPath(GetPath(), &dir);
00422     if ( dir.empty() &&  mode == eIfEmptyPath_Current  &&
00423          !GetPath().empty() ) {
00424         return string(DIR_CURRENT) + DIR_SEPARATOR;
00425     }
00426     return dir;
00427 }
00428 
00429 
00430 bool CDirEntry::IsAbsolutePath(const string& path)
00431 {
00432     if ( path.empty() )
00433         return false;
00434 
00435 #if defined(NCBI_OS_MSWIN)
00436     // Absolute path is a path started with 'd:\', 'd:/' or  '\\' only.
00437     if ( ( isalpha((unsigned char)path[0])  &&  path[1] == ':'  &&
00438            (path[2] == '/'  || path[2] == '\\') )  ||
00439            (path[0] == '\\'  &&  path[1] == '\\') ) {
00440         return true;
00441     }
00442 #elif defined(NCBI_OS_UNIX)
00443     if ( path[0] == '/' )
00444         return true;
00445 #endif
00446     return false;
00447 }
00448 
00449 
00450 bool CDirEntry::IsAbsolutePathEx(const string& path)
00451 {
00452     if ( path.empty() )
00453         return false;
00454 
00455     // Windows: 
00456     // Absolute path is a path started with 'd:\', 'd:/' or  '\\' only.
00457     if ( ( isalpha((unsigned char)path[0])  &&  path[1] == ':'  &&
00458            (path[2] == '/'  || path[2] == '\\') )  ||
00459            (path[0] == '\\'  &&  path[1] == '\\') ) {
00460         return true;
00461     }
00462     // Unix
00463     if ( path[0] == '/' )
00464         // FIXME:
00465         // This is an Unix absolute path or MS Windows relative.
00466         // But Unix have favor here, because '/' is native dir separator.
00467         return true;
00468 
00469     // Else - relative
00470     return false;
00471 }
00472 
00473 
00474 /// Helper : strips dir to parts:
00475 ///     c:\a\b\     will be <c:><a><b>
00476 ///     /usr/bin/   will be </><usr><bin>
00477 static void s_StripDir(const string& dir, vector<string> * dir_parts)
00478 {
00479     dir_parts->clear();
00480     if ( dir.empty() ) 
00481         return;
00482 
00483     const char sep = CDirEntry::GetPathSeparator();
00484 
00485     size_t sep_pos = 0;
00486     size_t last_ind = dir.length() - 1;
00487     size_t part_start = 0;
00488     for (;;) {
00489         sep_pos = dir.find(sep, sep_pos);
00490         if (sep_pos == NPOS) {
00491             dir_parts->push_back(string(dir, part_start,
00492                                         dir.length() - part_start));
00493             break;
00494         }
00495 
00496         // If path starts from '/' - it's a root directory
00497         if (sep_pos == 0) {
00498             dir_parts->push_back(string(1, sep));
00499         } else {
00500             dir_parts->push_back(string(dir, part_start, sep_pos -part_start));
00501         }
00502 
00503         sep_pos++;
00504         part_start = sep_pos;
00505         if (sep_pos >= last_ind) 
00506             break;
00507     }
00508 
00509 }
00510 
00511 
00512 string CDirEntry::CreateRelativePath( const string& path_from, 
00513                                       const string& path_to )
00514 {
00515     string path; // the result    
00516     
00517     if ( !IsAbsolutePath(path_from) ) {
00518         NCBI_THROW(CFileException, eRelativePath, 
00519                    "path_from is not absolute path");
00520     }
00521 
00522     if ( !IsAbsolutePath(path_to) ) {
00523         NCBI_THROW(CFileException, eRelativePath, 
00524                    "path_to is not absolute path");
00525     }
00526 
00527     // Split and strip FROM
00528     string dir_from;
00529     SplitPath(AddTrailingPathSeparator(path_from), &dir_from);
00530     vector<string> dir_from_parts;
00531     s_StripDir(dir_from, &dir_from_parts);
00532     if ( dir_from_parts.empty() ) {
00533         NCBI_THROW(CFileException, eRelativePath, 
00534                    "path_from is empty path");
00535     }
00536 
00537     // Split and strip TO
00538     string dir_to;
00539     string base_to;
00540     string ext_to;
00541     SplitPath(path_to, &dir_to, &base_to, &ext_to);    
00542     vector<string> dir_to_parts;
00543     s_StripDir(dir_to, &dir_to_parts);
00544     if ( dir_to_parts.empty() ) {
00545         NCBI_THROW(CFileException, eRelativePath, 
00546                    "path_to is empty path");
00547     }
00548 
00549     // Platform-dependent compare mode
00550 #ifdef NCBI_OS_MSWIN
00551 #  define DIR_PARTS_CMP_MODE NStr::eNocase
00552 #else /*NCBI_OS_UNIX*/
00553 #  define DIR_PARTS_CMP_MODE NStr::eCase
00554 #endif
00555     // Roots must be the same to create relative path from one to another
00556 
00557     if (NStr::Compare(dir_from_parts.front(), 
00558                       dir_to_parts.front(), 
00559                       DIR_PARTS_CMP_MODE) != 0) {
00560         NCBI_THROW(CFileException, eRelativePath, 
00561                    "roots of input paths are different");
00562     }
00563 
00564     size_t min_parts = min(dir_from_parts.size(), dir_to_parts.size());
00565     size_t common_length = min_parts;
00566     for (size_t i = 0; i < min_parts; i++) {
00567         if (NStr::Compare(dir_from_parts[i], 
00568                           dir_to_parts[i],
00569                           DIR_PARTS_CMP_MODE) != 0) {
00570             common_length = i;
00571             break;
00572         }
00573     }
00574 
00575     for (size_t i = common_length; i < dir_from_parts.size(); i++) {
00576         path += "..";
00577         path += GetPathSeparator();
00578     }
00579     for (size_t i = common_length; i < dir_to_parts.size(); i++) {
00580         path += dir_to_parts[i];
00581         path += GetPathSeparator();
00582     }
00583     
00584     return path + base_to + ext_to;
00585 }
00586 
00587 
00588 string CDirEntry::CreateAbsolutePath(const string& path)
00589 {
00590     if ( IsAbsolutePath(path) ) {
00591         return path;
00592     }
00593 #if defined(NCBI_OS_MSWIN)
00594     if ( path.find(DISK_SEPARATOR) != NPOS ) {
00595         NCBI_THROW(CFileException, eRelativePath, 
00596                    "Path must not contain disk separator: " + path);
00597     }
00598     if ( !path.empty()  &&  (path[0] == '/'  || path[0] == '\\') ) {
00599         NCBI_THROW(CFileException, eRelativePath, 
00600                    "Path that starts with slash is not absolute"
00601                    " on MS Windows: " + path);
00602     }
00603 #endif
00604     string result = CDirEntry::ConcatPath(CDir::GetCwd(), path);
00605     result = CDirEntry::NormalizePath(result);
00606     return result;
00607 }
00608 
00609 
00610 string CDirEntry::ConvertToOSPath(const string& path)
00611 {
00612     // Not process empty or absolute path
00613     if ( path.empty() || IsAbsolutePathEx(path) ) {
00614         return path;
00615     }
00616     // Now we have relative "path"
00617     string xpath = path;
00618     // Add trailing separator if path ends with DIR_PARENT or DIR_CURRENT
00619 #if defined(DIR_PARENT)
00620     if ( NStr::EndsWith(xpath, DIR_PARENT) )  {
00621         xpath += DIR_SEPARATOR;
00622     }
00623 #endif
00624 #if defined(DIR_CURRENT)
00625     if ( NStr::EndsWith(xpath, DIR_CURRENT) )  {
00626         xpath += DIR_SEPARATOR;
00627     }
00628 #endif
00629     // Replace each path separator with the current OS separator character
00630     for (size_t i = 0; i < xpath.length(); i++) {
00631         char c = xpath[i];
00632         if ( c == '\\' || c == '/' || c == ':') {
00633             xpath[i] = DIR_SEPARATOR;
00634         }
00635     }
00636     // Replace something like "../aaa/../bbb/ccc" with "../bbb/ccc"
00637     xpath = NormalizePath(xpath);
00638 
00639     return xpath;
00640 }
00641 
00642 
00643 string CDirEntry::ConcatPath(const string& first, const string& second)
00644 {
00645     // Prepare first part of path
00646     string path = AddTrailingPathSeparator(NStr::TruncateSpaces(first));
00647     // Remove leading separator in "second" part
00648     string part = NStr::TruncateSpaces(second);
00649     if ( !path.empty()  &&  part.length() > 0  &&  part[0] == DIR_SEPARATOR ) {
00650         part.erase(0,1);
00651     }
00652     // Add second part
00653     path += part;
00654     return path;
00655 }
00656 
00657 
00658 string CDirEntry::ConcatPathEx(const string& first, const string& second)
00659 {
00660     // Prepare first part of path
00661     string path = NStr::TruncateSpaces(first);
00662 
00663     // Add trailing path separator to first part (OS independence)
00664 
00665     size_t pos = path.length();
00666     if ( pos  &&  string(ALL_OS_SEPARATORS).find(path.at(pos-1)) == NPOS ) {
00667         // Find used path separator
00668         char sep = GetPathSeparator();
00669         size_t sep_pos = path.find_last_of(ALL_OS_SEPARATORS);
00670         if ( sep_pos != NPOS ) {
00671             sep = path.at(sep_pos);
00672         }
00673         path += sep;
00674     }
00675     // Remove leading separator in "second" part
00676     string part = NStr::TruncateSpaces(second);
00677     if ( part.length() > 0  &&
00678          string(ALL_OS_SEPARATORS).find(part[0]) != NPOS ) {
00679         part.erase(0,1);
00680     }
00681     // Add second part
00682     path += part;
00683     return path;
00684 }
00685 
00686 
00687 string CDirEntry::NormalizePath(const string& path, EFollowLinks follow_links)
00688 {
00689     if ( path.empty() ) {
00690         return path;
00691     }
00692     static const char kSeps[] = { DIR_SEPARATOR,
00693 #ifdef DIR_SEPARATOR_ALT
00694                                   DIR_SEPARATOR_ALT,
00695 #endif
00696                                   '\0' };
00697 
00698     list<string> head;            // already resolved to our satisfaction
00699     list<string> tail;            // to resolve afterwards
00700     string       current;         // to resolve next
00701     int          link_depth = 0;
00702 
00703     // Delete trailing slash for all paths except similar to 'd:\'
00704 #  ifdef DISK_SEPARATOR
00705     if ( path.find(DISK_SEPARATOR) == NPOS ) {
00706         current = DeleteTrailingPathSeparator(path);
00707     } else {
00708         current = path;
00709     }
00710 #  else
00711     current = DeleteTrailingPathSeparator(path);
00712 #  endif
00713     if ( current.empty() ) {
00714         // root dir
00715         return string(1, DIR_SEPARATOR);
00716     }
00717 
00718     while ( !current.empty()  ||  !tail.empty() ) {
00719         list<string> pretail;
00720         if ( !current.empty() ) {
00721             NStr::Split(current, kSeps, pretail, NStr::eNoMergeDelims);
00722             current.erase();
00723             if (pretail.front().empty()
00724 #ifdef DISK_SEPARATOR
00725                 ||  pretail.front().find(DISK_SEPARATOR) != NPOS
00726 #endif
00727                 ) {
00728                 // Absolute path
00729                 head.clear();
00730 #ifdef NCBI_OS_MSWIN
00731                 // Remove leading "\\?\". Replace leading "\\?\UNC\" with "\\".
00732                 static const char* const kUNC[] = { "", "", "?", "UNC" };
00733                 list<string>::iterator it = pretail.begin();
00734                 unsigned int matched = 0;
00735                 while (matched < 4  &&  it != pretail.end()
00736                        &&  !NStr::CompareNocase(*it, kUNC[matched])) {
00737                     ++it;
00738                     ++matched;
00739                 }
00740                 pretail.erase(pretail.begin(), it);
00741                 switch (matched) {
00742                 case 2: case 4: // got a UNC path (\\... or \\?\UNC\...)
00743                     head.push_back(kEmptyStr);
00744                     // fall through
00745                 case 1:         // normal volume-less absolute path
00746                     head.push_back(kEmptyStr);
00747                     break;
00748                 case 3/*?*/:    // normal path, absolute or relative
00749                     break;
00750                 }
00751 #endif
00752             }
00753             tail.splice(tail.begin(), pretail);
00754         }
00755 
00756         string next;
00757         if (!tail.empty()) {
00758             next = tail.front();
00759             tail.pop_front();
00760         }
00761         if ( !head.empty() ) { // empty heads should accept anything
00762             string& last = head.back();
00763             if (last == DIR_CURRENT) {
00764                 if (!next.empty()) {
00765                     head.pop_back();
00766                     _ASSERT(head.empty());
00767                 }
00768             } else if (next == DIR_CURRENT) {
00769                 // Leave out, since we already have content
00770                 continue;
00771 #ifdef DISK_SEPARATOR
00772             } else if (!last.empty() && last[last.size()-1] == DISK_SEPARATOR) {
00773                 // Allow almost anything right after a volume specification
00774 #endif
00775             } else if (next.empty()) {
00776                 continue; // leave out empty components in most cases
00777             } else if (next == DIR_PARENT) {
00778 #ifdef DISK_SEPARATOR
00779                 SIZE_TYPE pos;
00780 #endif
00781                 // Back up if possible, assuming existing path to be "physical"
00782                 if (last.empty()) {
00783                     // Already at the root; .. is a no-op
00784                     continue;
00785 #ifdef DISK_SEPARATOR
00786                 } else if ((pos = last.find(DISK_SEPARATOR) != NPOS)) {
00787                     last.erase(pos + 1);
00788 #endif
00789                 } else if (last != DIR_PARENT) {
00790                     head.pop_back();
00791                     continue;
00792                 }
00793             }
00794         }
00795 #ifdef NCBI_OS_UNIX
00796         // Is there a Windows equivalent for readlink?
00797         if ( follow_links ) {
00798             string s(head.empty() ? next
00799                      : NStr::Join(head, string(1, DIR_SEPARATOR))
00800                      + DIR_SEPARATOR + next);
00801             char buf[PATH_MAX];
00802             int  length = (int)readlink(s.c_str(), buf, sizeof(buf));
00803             if (length > 0) {
00804                 current.assign(buf, length);
00805                 if (++link_depth >= 1024) {
00806                     ERR_POST_X(1, Warning << "CDirEntry::NormalizePath():"
00807                                " Reached symlink depth limit " <<
00808                                link_depth << " when resolving " << path);
00809                     follow_links = eIgnoreLinks;
00810                 }
00811                 continue;
00812             }
00813         }
00814 #endif
00815         // Normal case: just append the next element to head
00816         head.push_back(next);
00817     }
00818 
00819     // Special cases
00820     if ( (head.size() == 0)  ||
00821          (head.size() == 2  &&  head.front() == DIR_CURRENT  &&
00822          head.back().empty()) ) {
00823         // current dir
00824         return DIR_CURRENT;
00825     }
00826     if (head.size() == 1  &&  head.front().empty()) {
00827         // root dir
00828         return string(1, DIR_SEPARATOR);
00829     }
00830     // Compose path
00831     return NStr::Join(head, string(1, DIR_SEPARATOR));
00832 }
00833 
00834 
00835 bool CDirEntry::GetMode(TMode* usr_mode, TMode* grp_mode,
00836                         TMode* oth_mode, TSpecialModeBits* special) const
00837 {
00838     struct stat st;
00839     if (stat(GetPath().c_str(), &st) != 0) {
00840         LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::GetMode():"
00841                                    " stat() failed for " << GetPath());
00842     }
00843     // Owner
00844     if (usr_mode) {
00845         *usr_mode = (
00846 #if   defined(S_IRUSR)
00847                      (st.st_mode & S_IRUSR  ? fRead              : 0) |
00848 #elif defined(S_IREAD)
00849                      (st.st_mode & S_IREAD  ? fRead              : 0) |
00850 #endif
00851 #if   defined(S_IWUSR)
00852                      (st.st_mode & S_IWUSR  ? fWrite             : 0) |
00853 #elif defined(S_IWRITE)
00854                      (st.st_mode & S_IWRITE ? fWrite             : 0) |
00855 #endif
00856 #if   defined(S_IXUSR)
00857                      (st.st_mode & S_IXUSR  ? fExecute           : 0) |
00858 #elif defined(S_IEXEC)
00859                      (st.st_mode & S_IEXEC  ? fExecute           : 0) |
00860 #endif
00861                      0);
00862     }
00863 
00864 #ifdef NCBI_OS_MSWIN
00865     if (grp_mode) *grp_mode = 0;
00866     if (oth_mode) *oth_mode = 0;
00867     if (special)  *special  = 0;
00868 
00869 #else
00870 
00871     // Group
00872     if (grp_mode) {
00873         *grp_mode = (
00874 #ifdef S_IRGRP
00875                      (st.st_mode & S_IRGRP  ? fRead              : 0) |
00876 #endif
00877 #ifdef S_IWGRP
00878                      (st.st_mode & S_IWGRP  ? fWrite             : 0) |
00879 #endif
00880 #ifdef S_IXGRP
00881                      (st.st_mode & S_IXGRP  ? fExecute           : 0) |
00882 #endif
00883                      0);
00884     }
00885     // Others
00886     if (oth_mode) {
00887         *oth_mode = (
00888 #ifdef S_IROTH
00889                      (st.st_mode & S_IROTH  ? fRead              : 0) |
00890 #endif
00891 #ifdef S_IWOTH
00892                      (st.st_mode & S_IWOTH  ? fWrite             : 0) |
00893 #endif
00894 #ifdef S_IXOTH
00895                      (st.st_mode & S_IXOTH  ? fExecute           : 0) |
00896 #endif
00897                      0);
00898     }
00899     // Special bits
00900     if (special) {
00901         *special = (
00902 #ifdef S_ISUID
00903                     (st.st_mode & S_ISUID   ? CDirEntry::fSetUID : 0) |
00904 #endif
00905 #ifdef S_ISGID
00906                     (st.st_mode & S_ISGID   ? CDirEntry::fSetGID : 0) |
00907 #endif
00908 #ifdef S_ISVTX
00909                     (st.st_mode & S_ISVTX   ? CDirEntry::fSticky : 0) |
00910 #endif
00911                     0);
00912     }
00913 #endif // NCBI_OS_MSWIN
00914 
00915     return true;
00916 }
00917 
00918 
00919 bool CDirEntry::SetMode(TMode user_mode, TMode group_mode,
00920                         TMode other_mode, TSpecialModeBits special) const
00921 {
00922     if (user_mode == fDefault) {
00923         user_mode = m_DefaultMode[eUser];
00924     }
00925     if (group_mode == fDefault) {
00926         group_mode = m_DefaultMode[eGroup];
00927     }
00928     if (other_mode == fDefault) {
00929         other_mode = m_DefaultMode[eOther];
00930     }
00931     if (special == 0) {
00932         special = m_DefaultMode[eSpecial];
00933     }
00934     mode_t mode = MakeModeT(user_mode, group_mode, other_mode, special);
00935 
00936     if ( chmod(GetPath().c_str(), mode) != 0 ) {
00937         LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::SetMode():"
00938                                    " chmod() failed for " << GetPath());
00939     }
00940     return true;
00941 }
00942 
00943 
00944 void CDirEntry::SetDefaultModeGlobal(EType entry_type, TMode user_mode, 
00945                                      TMode group_mode, TMode other_mode,
00946                                      TSpecialModeBits special)
00947 {
00948     if ( entry_type >= eUnknown ) {
00949         return;
00950     }
00951     if ( entry_type == eDir ) {
00952         if ( user_mode == fDefault ) {
00953             user_mode = fDefaultDirUser;
00954         }
00955         if ( group_mode == fDefault ) {
00956             group_mode = fDefaultDirGroup;
00957         }
00958         if ( other_mode == fDefault ) {
00959             other_mode = fDefaultDirOther;
00960         }
00961     } else {
00962         if ( user_mode == fDefault ) {
00963             user_mode = fDefaultUser;
00964         }
00965         if ( group_mode == fDefault ) {
00966             group_mode = fDefaultGroup;
00967         }
00968         if ( other_mode == fDefault ) {
00969             other_mode = fDefaultOther;
00970         }
00971     }
00972     if ( special == 0 ) {
00973         special = m_DefaultModeGlobal[entry_type][eSpecial];
00974     }
00975     m_DefaultModeGlobal[entry_type][eUser]    = user_mode;
00976     m_DefaultModeGlobal[entry_type][eGroup]   = group_mode;
00977     m_DefaultModeGlobal[entry_type][eOther]   = other_mode;
00978     m_DefaultModeGlobal[entry_type][eSpecial] = special;
00979 }
00980 
00981 
00982 void CDirEntry::SetDefaultMode(EType entry_type, TMode user_mode, 
00983                                TMode group_mode, TMode other_mode,
00984                                TSpecialModeBits special)
00985 {
00986     if ( user_mode == fDefault ) {
00987         user_mode  = m_DefaultModeGlobal[entry_type][eUser];
00988     }
00989     if ( group_mode == fDefault ) {
00990         group_mode = m_DefaultModeGlobal[entry_type][eGroup];
00991     }
00992     if ( other_mode == fDefault ) {
00993         other_mode = m_DefaultModeGlobal[entry_type][eOther];
00994     }
00995     if ( special == 0 ) {
00996         special = m_DefaultModeGlobal[entry_type][eSpecial];
00997     }
00998     m_DefaultMode[eUser]    = user_mode;
00999     m_DefaultMode[eGroup]   = group_mode;
01000     m_DefaultMode[eOther]   = other_mode;
01001     m_DefaultMode[eSpecial] = special;
01002 }
01003 
01004 
01005 void CDirEntry::GetDefaultModeGlobal(EType  entry_type, TMode* user_mode,
01006                                      TMode* group_mode, TMode* other_mode,
01007                                      TSpecialModeBits* special)
01008 {
01009     if ( user_mode ) {
01010         *user_mode  = m_DefaultModeGlobal[entry_type][eUser];
01011     }
01012     if ( group_mode ) {
01013         *group_mode = m_DefaultModeGlobal[entry_type][eGroup];
01014     }
01015     if ( other_mode ) {
01016         *other_mode = m_DefaultModeGlobal[entry_type][eOther];
01017     }
01018     if ( special ) {
01019         *special  = m_DefaultModeGlobal[entry_type][eSpecial];
01020     }
01021 }
01022 
01023 
01024 void CDirEntry::GetDefaultMode(TMode* user_mode, TMode* group_mode,
01025                                TMode* other_mode,
01026                                TSpecialModeBits* special) const
01027 {
01028     if ( user_mode ) {
01029         *user_mode  = m_DefaultMode[eUser];
01030     }
01031     if ( group_mode ) {
01032         *group_mode = m_DefaultMode[eGroup];
01033     }
01034     if ( other_mode ) {
01035         *other_mode = m_DefaultMode[eOther];
01036     }
01037     if ( special ) {
01038         *special   = m_DefaultMode[eSpecial];
01039     }
01040 }
01041 
01042 
01043 // Construct real entry mode from parts.
01044 // Parameters must not have "fDefault" value.
01045 mode_t CDirEntry::MakeModeT(TMode            usr_mode,
01046                             TMode            grp_mode,
01047                             TMode            oth_mode,
01048                             TSpecialModeBits special)
01049 {
01050     mode_t mode = (
01051     // special bits
01052 #ifdef S_ISUID
01053                    (special & fSetUID   ? S_ISUID    : 0) |
01054 #endif
01055 #ifdef S_ISGID
01056                    (special & fSetGID   ? S_ISGID    : 0) |
01057 #endif
01058 #ifdef S_ISVTX
01059                    (special & fSticky   ? S_ISVTX    : 0) |
01060 #endif
01061     // modes
01062 #if   defined(S_IRUSR)
01063                    (usr_mode & fRead    ? S_IRUSR    : 0) |
01064 #elif defined(S_IREAD)
01065                    (usr_mode & fRead    ? S_IREAD    : 0) |
01066 #endif
01067 #if   defined(S_IWUSR)
01068                    (usr_mode & fWrite   ? S_IWUSR    : 0) |
01069 #elif defined(S_IWRITE)
01070                    (usr_mode & fWrite   ? S_IWRITE   : 0) |
01071 #endif
01072 #if   defined(S_IXUSR)
01073                    (usr_mode & fExecute ? S_IXUSR    : 0) |
01074 #elif defined(S_IEXEC)
01075                    (usr_mode & fExecute ? S_IEXEC    : 0) |
01076 #endif
01077 #ifdef S_IRGRP
01078                    (grp_mode & fRead    ? S_IRGRP    : 0) |
01079 #endif
01080 #ifdef S_IWGRP
01081                    (grp_mode & fWrite   ? S_IWGRP    : 0) |
01082 #endif
01083 #ifdef S_IXGRP
01084                    (grp_mode & fExecute ? S_IXGRP    : 0) |
01085 #endif
01086 #ifdef S_IROTH
01087                    (oth_mode & fRead    ? S_IROTH    : 0) |
01088 #endif
01089 #ifdef S_IWOTH
01090                    (oth_mode & fWrite   ? S_IWOTH    : 0) |
01091 #endif
01092 #ifdef S_IXOTH
01093                    (oth_mode & fExecute ? S_IXOTH    : 0) |
01094 #endif
01095                    0);
01096     return mode;
01097 }
01098 
01099 
01100 #if defined(NCBI_OS_UNIX) && !defined(HAVE_EUIDACCESS) && !defined(EFF_ONLY_OK)
01101 
01102 // Work around a weird GCC 2.95 glitch which can result in confusing
01103 // calls to stat() with invocations of a (nonexistent) constructor.
01104 # if defined(NCBI_COMPILER_GCC)  &&  NCBI_COMPILER_VERSION < 300
01105 #    define CAS_ARG1 void
01106 #    define CAS_CAST static_cast<const struct stat*>
01107 #  else
01108 #    define CAS_ARG1 struct stat
01109 #    define CAS_CAST
01110 #endif
01111 
01112 static bool s_CheckAccessStat(const CAS_ARG1* p, int amode)
01113 {
01114     const struct stat& st = *CAS_CAST(p);
01115     uid_t uid = geteuid();
01116 
01117     // Check user permissions
01118     if (uid == st.st_uid) {
01119         if ( (!(amode & R_OK)  ||  (st.st_mode & S_IRUSR))  &&
01120              (!(amode & W_OK)  ||  (st.st_mode & S_IWUSR))  &&
01121              (!(amode & X_OK)  ||  (st.st_mode & S_IXUSR)) )
01122             return true;
01123     }
01124 
01125     // Initialize list of group IDs for effective user
01126     int ngroups = 0;
01127     gid_t gids[NGROUPS_MAX + 1];
01128     gids[0] = getegid();
01129     ngroups = getgroups((int)(sizeof(gids)/sizeof(gids[0])-1), gids+1);
01130     if (ngroups < 0) {
01131         ngroups = 1;
01132     } else {
01133         ngroups++;
01134     }
01135     for (int i = 1; i < ngroups; i++) {
01136         if (gids[i] == uid) {
01137             if (i < --ngroups) {
01138                 memmove(&gids[i], &gids[i+1], sizeof(gids[0])*(ngroups-i));
01139             }
01140             break;
01141         }
01142     }
01143 
01144     // Check group permissions
01145     for (int i = 0; i < ngroups; i++) {
01146         if (gids[i] == st.st_gid) {
01147             if ( (!(amode & R_OK)  ||  (st.st_mode & S_IRGRP))  &&
01148                  (!(amode & W_OK)  ||  (st.st_mode & S_IWGRP))  &&
01149                  (!(amode & X_OK)  ||  (st.st_mode & S_IXGRP)) )
01150                 return true;
01151         }
01152     }
01153 
01154     // Check other permissions
01155     if ( (!(amode & R_OK)  ||  (st.st_mode & S_IROTH))  &&
01156          (!(amode & W_OK)  ||  (st.st_mode & S_IWOTH))  &&
01157          (!(amode & X_OK)  ||  (st.st_mode & S_IXOTH)) )
01158         return true;
01159 
01160     // Permissions not granted
01161     return false;
01162 }
01163 
01164 
01165 static bool s_CheckAccessPath(const char* path, int amode)
01166 {
01167     if (!path) {
01168         errno = 0;
01169         return false;
01170     }
01171     if (!*path) {
01172         errno = ENOENT;
01173         return false;
01174     }
01175     struct stat st;
01176     if (stat(path, &st) != 0)
01177         return false;
01178 
01179     if (!s_CheckAccessStat(&st, amode)) {
01180         errno = EACCES;
01181         return false;
01182     }
01183     // Permissions granted
01184     return true;
01185 }
01186 
01187 #endif  // defined(NCBI_OS_UNIX)
01188 
01189 
01190 bool CDirEntry::CheckAccess(TMode access_mode) const
01191 {
01192 #if defined(NCBI_OS_MSWIN)
01193     // Try to get effective access rights on this file object for
01194     // the current process owner.
01195     ACCESS_MASK mask = 0;
01196     if ( CWinSecurity::GetFilePermissions(GetPath(), &mask) ) {
01197         TMode perm = ( (mask & FILE_READ_DATA  ? fRead    : 0) |
01198                        (mask & FILE_WRITE_DATA ? fWrite   : 0) |
01199                        (mask & FILE_EXECUTE    ? fExecute : 0) );
01200         return (access_mode & perm) > 0;
01201      }
01202      return false;
01203 
01204 #elif defined(NCBI_OS_UNIX)
01205     int amode = F_OK;
01206     if ( access_mode & fRead)    amode |= R_OK;
01207     if ( access_mode & fWrite)   amode |= W_OK;
01208     if ( access_mode & fExecute) amode |= X_OK;
01209     
01210     // Use euidaccess() where possible
01211 #  if defined(HAVE_EUIDACCESS)
01212     return euidaccess(GetPath().c_str(), amode) == 0;
01213 
01214 #  elif defined(EFF_ONLY_OK)
01215     // Some Unix have special flag for access() to use effective user ID.
01216     amode |= EFF_ONLY_OK;
01217     return access(GetPath().c_str(), amode) == 0;
01218 
01219 #  else
01220     // We can use access() only if effective and real user/group IDs are equal.
01221     // access() operate with real IDs only, but we should check access
01222     // for effective IDs.
01223     if (getuid() == geteuid()  &&  getgid() == getegid()) {
01224         return access(GetPath().c_str(), amode) == 0;
01225     }
01226     // Otherwise, try to check permissions itself.
01227     // Note, that this function is not perfect, it doesn't work with ACL,
01228     // which implementation can differ for each platform.
01229     // But in most cases it works.
01230     return s_CheckAccessPath(GetPath().c_str(), amode);
01231 
01232 #  endif
01233 #endif
01234 }
01235 
01236 
01237 #ifdef NCBI_OS_MSWIN
01238 
01239 bool s_FileTimeToCTime(const FILETIME& filetime, CTime& t) 
01240 {
01241     // Clear time object
01242     t.Clear();
01243 
01244     if ( !filetime.dwLowDateTime  &&  !filetime.dwHighDateTime ) {
01245         // File time is undefined, just return "empty" time
01246         return true;
01247     }
01248     SYSTEMTIME system;
01249     FILETIME   local;
01250 
01251     // Convert the file time to local time
01252     if ( !FileTimeToLocalFileTime(&filetime, &local) ) {
01253         return false;
01254     }
01255     // Convert the local file time from UTC to system time.
01256     if ( !FileTimeToSystemTime(&local, &system) ) {
01257         return false;
01258     }
01259 
01260     // Construct new time
01261     CTime newtime(system.wYear,
01262                   system.wMonth,
01263                   system.wDay,
01264                   system.wHour,
01265                   system.wMinute,
01266                   system.wSecond,
01267                   system.wMilliseconds *
01268                          (kNanoSecondsPerSecond / kMilliSecondsPerSecond),
01269                   CTime::eLocal,
01270                   t.GetTimeZonePrecision());
01271 
01272     // And assign it
01273     if ( t.GetTimeZone() == CTime::eLocal ) {
01274         t = newtime;
01275     } else {
01276         t = newtime.GetGmtTime();
01277     }
01278     return true;
01279 }
01280 
01281 
01282 void s_UnixTimeToFileTime(time_t t, long nanosec, FILETIME& filetime) 
01283 {
01284     // Note that LONGLONG is a 64-bit value
01285     LONGLONG res;
01286     // This algorithm was found in MSDN
01287     res = Int32x32To64(t, 10000000) + 116444736000000000 + nanosec/100;
01288     filetime.dwLowDateTime  = (DWORD)res;
01289     filetime.dwHighDateTime = (DWORD)(res >> 32);
01290 }
01291 
01292 #endif // NCBI_OS_MSWIN
01293 
01294 
01295 bool CDirEntry::GetTime(CTime* modification,
01296                         CTime* last_access,
01297                         CTime* creation) const
01298 {
01299 #ifdef NCBI_OS_MSWIN
01300     HANDLE handle;
01301     WIN32_FIND_DATA buf;
01302 
01303     // Get file times using FindFile
01304     handle = FindFirstFile(GetPath().c_str(), &buf);
01305     if ( handle == INVALID_HANDLE_VALUE ) {
01306         //LOG_ERROR_AND_RETURN("CDirEntry::GetTime():"
01307         //" Cannot find " << GetPath());
01308         return false;
01309     }
01310     FindClose(handle);
01311 
01312     // Convert file UTC times into CTime format
01313     if ( modification  &&
01314         !s_FileTimeToCTime(buf.ftLastWriteTime, *modification) ) {
01315         LOG_ERROR_AND_RETURN("CDirEntry::GetTime():"
01316                              " Cannot get modification time for "
01317                              << GetPath());
01318     }
01319     if ( last_access  &&
01320          !s_FileTimeToCTime(buf.ftLastAccessTime, *last_access) ) {
01321         LOG_ERROR_AND_RETURN("CDirEntry::GetTime():"
01322                              " Cannot get access time for " << GetPath());
01323     }
01324     if ( creation  &&
01325         !s_FileTimeToCTime(buf.ftCreationTime, *creation) ) {
01326         LOG_ERROR_AND_RETURN("CDirEntry::GetTime():"
01327                              " Cannot get creation time for " << GetPath());
01328     }
01329     return true;
01330 
01331 #else // NCBI_OS_UNIX
01332 
01333     struct SStat st;
01334     if ( !Stat(&st) ) {
01335         //LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::GetTime(): Could not get time for " << GetPath());
01336         return false;
01337     }
01338     if ( modification ) {
01339         modification->SetTimeT(st.orig.st_mtime);
01340         if ( st.mtime_nsec )
01341             modification->SetNanoSecond(st.mtime_nsec);
01342     }
01343     if ( last_access ) {
01344         last_access->SetTimeT(st.orig.st_atime);
01345         if ( st.atime_nsec )
01346             last_access->SetNanoSecond(st.atime_nsec);
01347     }
01348     if ( creation ) {
01349         creation->SetTimeT(st.orig.st_ctime);
01350         if ( st.ctime_nsec )
01351             creation->SetNanoSecond(st.ctime_nsec);
01352     }
01353     return true;
01354 #endif
01355 }
01356 
01357 
01358 bool CDirEntry::SetTime(CTime* modification,
01359                         CTime* last_access,
01360                         CTime* creation) const
01361 {
01362 #ifdef NCBI_OS_MSWIN
01363     if ( !modification  &&  !last_access  &&  !creation ) {
01364         return true;
01365     }
01366 
01367     FILETIME   x_modification,        x_last_access,        x_creation;
01368     LPFILETIME p_modification = NULL, p_last_access = NULL, p_creation = NULL;
01369 
01370     // Convert times to FILETIME format
01371     if ( modification ) {
01372         s_UnixTimeToFileTime(modification->GetTimeT(),
01373                              modification->NanoSecond(), x_modification);
01374         p_modification = &x_modification;
01375     }
01376     if ( last_access ) {
01377         s_UnixTimeToFileTime(last_access->GetTimeT(),
01378                              last_access->NanoSecond(), x_last_access);
01379         p_last_access = &x_last_access;
01380     }
01381     if ( creation ) {
01382         s_UnixTimeToFileTime(creation->GetTimeT(),
01383                              creation->NanoSecond(), x_creation);
01384         p_creation = &x_creation;
01385     }
01386 
01387     // Change times
01388     HANDLE h = CreateFile(GetPath().c_str(), FILE_WRITE_ATTRIBUTES,
01389                           FILE_SHARE_READ, NULL, OPEN_EXISTING,
01390                           FILE_FLAG_BACKUP_SEMANTICS /*for dirs*/, NULL); 
01391     if ( h == INVALID_HANDLE_VALUE ) {
01392         LOG_ERROR_AND_RETURN("CDirEntry::SetTime():"
01393                              " Cannot open " << GetPath());
01394     }
01395     if ( !SetFileTime(h, p_creation, p_last_access, p_modification) ) {
01396         CloseHandle(h);
01397         LOG_ERROR_AND_RETURN("CDirEntry::SetTime():"
01398                              " Cannot set new time for " << GetPath());
01399     }
01400     CloseHandle(h);
01401 
01402     return true;
01403 
01404 #else // NCBI_OS_UNIX
01405     if ( !modification  &&  !last_access  /*&&  !creation*/ ) {
01406         return true;
01407     }
01408 
01409 #  ifdef HAVE_UTIMES
01410     // Get current times
01411     CTime x_modification, x_last_access;
01412 
01413     if ( !modification  ||  !last_access ) {
01414         if ( !GetTime(modification ? 0 : &x_modification,
01415                       last_access  ? 0 : &x_last_access,
01416                       0 /* creation */) ) {
01417             return false;
01418         }
01419         if (!modification) {
01420             modification = &x_modification;
01421         } else {
01422             last_access = &x_last_access;
01423         }
01424     }
01425 
01426     // Change times
01427     struct timeval tvp[2];
01428     tvp[0].tv_sec  = last_access->GetTimeT();
01429     tvp[0].tv_usec = last_access->NanoSecond() / 1000;
01430     tvp[1].tv_sec  = modification->GetTimeT();
01431     tvp[1].tv_usec = modification->NanoSecond() / 1000;
01432 
01433 #    ifdef HAVE_LUTIMES
01434     bool ut_res = lutimes(GetPath().c_str(), tvp) == 0;
01435 #    else
01436     bool ut_res = utimes(GetPath().c_str(), tvp) == 0;
01437 #    endif
01438     if ( !ut_res ) {
01439         LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::SetTime():"
01440                                    " Cannot change time for " << GetPath());
01441     }
01442     return true;
01443 
01444 # else
01445     // utimes() does not exist on current platform,
01446     // so use less accurate utime().
01447 
01448     // Get current times
01449     time_t x_modification, x_last_access;
01450 
01451     if ((!modification  ||  !last_access)
01452         &&  !GetTimeT(&x_modification,
01453                       &x_last_access,
01454                       0 /* creation */)) {
01455         return false;
01456     }
01457 
01458     // Change times to new
01459     struct utimbuf times;
01460     times.modtime  = modification ? modification->GetTimeT() : x_modification;
01461     times.actime   = last_access  ? last_access->GetTimeT()  : x_last_access;
01462     if ( utime(GetPath().c_str(), &times) != 0 ) {
01463         LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::SetTime():"
01464                                    " Cannot change time for " << GetPath());
01465     }
01466     return true;
01467 
01468 #  endif // HAVE_UTIMES
01469 
01470 #endif
01471 }
01472 
01473 
01474 bool CDirEntry::GetTimeT(time_t* modification,
01475                          time_t* last_access,
01476                          time_t* creation) const
01477 {
01478     struct stat st;
01479     if (stat(GetPath().c_str(), &st) != 0) {
01480         LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::GetTimeT():"
01481                                    " stat() failed for " << GetPath());
01482     }
01483     if ( modification ) {
01484         *modification = st.st_mtime;
01485     }
01486     if ( last_access ) {
01487         *last_access = st.st_atime;
01488     }
01489     if ( creation ) {
01490         *creation = st.st_ctime;
01491     }
01492     return true;
01493 }
01494 
01495 
01496 bool CDirEntry::SetTimeT(time_t* modification,
01497                          time_t* last_access,
01498                          time_t* creation) const
01499 {
01500 #ifdef NCBI_OS_MSWIN
01501     if ( !modification  &&  !last_access  &&  !creation ) {
01502         return true;
01503     }
01504 
01505     FILETIME   x_modification,        x_last_access,        x_creation;
01506     LPFILETIME p_modification = NULL, p_last_access = NULL, p_creation = NULL;
01507 
01508     // Convert times to FILETIME format
01509     if ( modification ) {
01510         s_UnixTimeToFileTime(*modification, 0, x_modification);
01511         p_modification = &x_modification;
01512     }
01513     if ( last_access ) {
01514         s_UnixTimeToFileTime(*last_access, 0, x_last_access);
01515         p_last_access = &x_last_access;
01516     }
01517     if ( creation ) {
01518         s_UnixTimeToFileTime(*creation, 0, x_creation);
01519         p_creation = &x_creation;
01520     }
01521 
01522     // Change times
01523     HANDLE h = CreateFile(GetPath().c_str(), FILE_WRITE_ATTRIBUTES,
01524                           FILE_SHARE_READ, NULL, OPEN_EXISTING,
01525                           FILE_FLAG_BACKUP_SEMANTICS /*for dirs*/, NULL); 
01526     if ( h == INVALID_HANDLE_VALUE ) {
01527         LOG_ERROR_AND_RETURN("CDirEntry::SetTimeT():"
01528                              " Cannot open " << GetPath());
01529         return false;
01530     }
01531     if ( !SetFileTime(h, p_creation, p_last_access, p_modification) ) {
01532         CloseHandle(h);
01533         LOG_ERROR_AND_RETURN("CDirEntry::SetTimeT():"
01534                              " Cannot set new time for " << GetPath());
01535     }
01536     CloseHandle(h);
01537 
01538     return true;
01539 
01540 #else // NCBI_OS_UNIX
01541     if ( !modification  &&  !last_access  /*&&  !creation*/ )
01542         return true;
01543 
01544     time_t x_modification, x_last_access;
01545     if ((!modification  ||  !last_access)
01546         &&  !GetTimeT(&x_modification,
01547                       &x_last_access,
01548                       0 /* creation */)) {
01549         return false;
01550     }
01551 
01552     // Change times to new
01553     struct utimbuf times;
01554     times.modtime = modification ? *modification : x_modification;
01555     times.actime  = last_access  ? *last_access  : x_last_access;
01556     if ( utime(GetPath().c_str(), &times) != 0 ) {
01557         LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::SetTimeT():"
01558                                    " Cannot change time for " << GetPath());
01559     }
01560     return true;
01561 
01562 #endif
01563 }
01564 
01565 
01566 bool CDirEntry::Stat(struct SStat *buffer, EFollowLinks follow_links) const
01567 {
01568     if ( !buffer ) {
01569         errno = EFAULT;
01570         LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::Stat():"
01571                                    " NULL stat buffer passed for "
01572                                    << GetPath());
01573     }
01574 
01575     int errcode;
01576 #ifdef NCBI_OS_MSWIN
01577     errcode = stat(GetPath().c_str(), &buffer->orig);
01578 #else // NCBI_OS_UNIX
01579     if (follow_links == eFollowLinks) {
01580         errcode = stat(GetPath().c_str(), &buffer->orig);
01581     } else {
01582         errcode = lstat(GetPath().c_str(), &buffer->orig);
01583     }
01584 #endif
01585     if (errcode != 0) {
01586         return false;
01587     }
01588    
01589     // Assign additional fields
01590     buffer->atime_nsec = 0;
01591     buffer->mtime_nsec = 0;
01592     buffer->ctime_nsec = 0;
01593     
01594 #ifdef NCBI_OS_UNIX
01595     // UNIX:
01596     // Some systems have additional fields in the stat structure to store
01597     // nanoseconds. If you know one more platform which have nanoseconds
01598     // support for file times, add it here.
01599 
01600 #  if !defined(__GLIBC_PREREQ)
01601 #    define __GLIBC_PREREQ(x, y) 0
01602 #  endif
01603 
01604 #  if defined(NCBI_OS_LINUX)  &&  __GLIBC_PREREQ(2,3)
01605 #    if defined(__USE_MISC)
01606     buffer->atime_nsec = buffer->orig.st_atim.tv_nsec;
01607     buffer->mtime_nsec = buffer->orig.st_mtim.tv_nsec;
01608     buffer->ctime_nsec = buffer->orig.st_ctim.tv_nsec;
01609 #    else
01610     buffer->atime_nsec = buffer->orig.st_atimensec;
01611     buffer->mtime_nsec = buffer->orig.st_mtimensec;
01612     buffer->ctime_nsec = buffer->orig.st_ctimensec;
01613 #    endif
01614 #  endif
01615 
01616 
01617 #  if defined(NCBI_OS_SOLARIS)
01618 #    if !defined(_XOPEN_SOURCE) && !defined(_POSIX_C_SOURCE) || \
01619      defined(__EXTENSIONS__)
01620     buffer->atime_nsec = buffer->orig.st_atim.tv_nsec;
01621     buffer->mtime_nsec = buffer->orig.st_mtim.tv_nsec;
01622     buffer->ctime_nsec = buffer->orig.st_ctim.tv_nsec;
01623 #    else
01624     buffer->atime_nsec = buffer->orig.st_atim.__tv_nsec;
01625     buffer->mtime_nsec = buffer->orig.st_mtim.__tv_nsec;
01626     buffer->ctime_nsec = buffer->orig.st_ctim.__tv_nsec;
01627 #    endif
01628 #  endif
01629 
01630    
01631 #  if defined(NCBI_OS_BSD) || defined(NCBI_OS_DARWIN)
01632 #    if defined(_POSIX_SOURCE)
01633     buffer->atime_nsec = buffer->orig.st_atimensec;
01634     buffer->mtime_nsec = buffer->orig.st_mtimensec;
01635     buffer->ctime_nsec = buffer->orig.st_ctimensec;
01636 #    else
01637     buffer->atime_nsec = buffer->orig.st_atimespec.tv_nsec;
01638     buffer->mtime_nsec = buffer->orig.st_mtimespec.tv_nsec;
01639     buffer->ctime_nsec = buffer->orig.st_ctimespec.tv_nsec;
01640 #    endif
01641 #  endif
01642 
01643 
01644 #  if defined(NCBI_OS_IRIX)
01645 #    if defined(tv_sec)
01646     buffer->atime_nsec = buffer->orig.st_atim.__tv_nsec;
01647     buffer->mtime_nsec = buffer->orig.st_mtim.__tv_nsec;
01648     buffer->ctime_nsec = buffer->orig.st_ctim.__tv_nsec;
01649 #    else
01650     buffer->atime_nsec = buffer->orig.st_atim.tv_nsec;
01651     buffer->mtime_nsec = buffer->orig.st_mtim.tv_nsec;
01652     buffer->ctime_nsec = buffer->orig.st_ctim.tv_nsec;
01653 #    endif
01654 #  endif
01655     
01656 #endif  // NCBI_OS_UNIX
01657 
01658     return true;
01659 }
01660 
01661 
01662 CDirEntry::EType CDirEntry::GetType(EFollowLinks follow) const
01663 {
01664     struct stat st;
01665     int errcode;
01666 
01667 #if defined(NCBI_OS_MSWIN)
01668     errcode = stat(GetPath().c_str(), &st);
01669 #else // NCBI_OS_UNIX
01670     if (follow == eFollowLinks) {
01671         errcode = stat(GetPath().c_str(), &st);
01672     } else {
01673         errcode = lstat(GetPath().c_str(), &st);
01674     }
01675 #endif
01676     if (errcode != 0) {
01677         return eUnknown;
01678     }
01679     return GetType(st);
01680 }
01681 
01682 /// Test macro for file types
01683 #define NCBI_IS_TYPE(mode, mask)  (((mode) & S_IFMT) == (mask))
01684 
01685 CDirEntry::EType CDirEntry::GetType(const struct stat& st)
01686 {
01687     unsigned int mode = (unsigned int)st.st_mode;
01688 
01689 #ifdef S_ISDIR
01690     if (S_ISDIR(mode))
01691 #else
01692     if (NCBI_IS_TYPE(mode, S_IFDIR))
01693 #endif
01694         return eDir;
01695 
01696 #ifdef S_ISCHR
01697     if (S_ISCHR(mode))
01698 #else
01699     if (NCBI_IS_TYPE(mode, S_IFCHR))
01700 #endif
01701         return eCharSpecial;
01702 
01703 #ifdef NCBI_OS_MSWIN
01704     if (NCBI_IS_TYPE(mode, _S_IFIFO))
01705         return ePipe;
01706 #else
01707     // NCBI_OS_UNIX
01708 #  ifdef S_ISFIFO
01709     if (S_ISFIFO(mode))
01710 #  else
01711     if (NCBI_IS_TYPE(mode, S_IFIFO))
01712 #  endif
01713         return ePipe;
01714 
01715 #  ifdef S_ISLNK
01716     if (S_ISLNK(mode))
01717 #  else
01718     if (NCBI_IS_TYPE(mode, S_IFLNK))
01719 #  endif
01720         return eLink;
01721 
01722 #  ifdef S_ISSOCK
01723     if (S_ISSOCK(mode))
01724 #  else
01725     if (NCBI_IS_TYPE(mode, S_IFSOCK))
01726 #  endif
01727         return eSocket;
01728 
01729 #  ifdef S_ISBLK
01730     if (S_ISBLK(mode))
01731 #  else
01732     if (NCBI_IS_TYPE(mode, S_IFBLK))
01733 #  endif
01734         return eBlockSpecial;
01735 
01736 #  ifdef S_IFDOOR 
01737     // only Solaris seems to have this one
01738 #    ifdef S_ISDOOR
01739     if (S_ISDOOR(mode))
01740 #    else
01741     if (NCBI_IS_TYPE(mode, S_IFDOOR))
01742 #    endif
01743         return eDoor;
01744 #  endif
01745 
01746 #endif //NCBI_OS_MSWIN
01747 
01748     // Check on regular file last
01749 #ifdef S_ISREG
01750     if (S_ISREG(mode))
01751 #else
01752     if (NCBI_IS_TYPE(mode, S_IFREG))
01753 #endif
01754         return eFile;
01755 
01756     return eUnknown;
01757 }
01758 
01759 
01760 string CDirEntry::LookupLink(void) const
01761 {
01762 #ifdef NCBI_OS_MSWIN
01763     return kEmptyStr;
01764 
01765 #else  // NCBI_OS_UNIX
01766     char buf[PATH_MAX];
01767     string name;
01768     int length = (int)readlink(GetPath().c_str(), buf, sizeof(buf));
01769     if (length > 0) {
01770         name.assign(buf, length);
01771     }
01772     return name;
01773 #endif
01774 }
01775 
01776 
01777 void CDirEntry::DereferenceLink(void)
01778 {
01779 #ifdef NCBI_OS_MSWIN
01780     // Not impemented
01781     return;
01782 #endif
01783     string prev;
01784     while ( IsLink() ) {
01785         string name = LookupLink();
01786         if ( name.empty() ||  name == prev ) {
01787             return;
01788         }
01789         prev = name;
01790         if ( IsAbsolutePath(name) ) {
01791             Reset(name);
01792         } else {
01793             string path = NormalizePath(MakePath(GetDir(), name));
01794             Reset(path);
01795         }
01796     }
01797 }
01798 
01799 
01800 void CDirEntry::DereferencePath(void)
01801 {
01802 #ifdef NCBI_OS_MSWIN
01803     // Not impemented
01804     return;
01805 #endif
01806     // Dereference each path components starting from last one
01807     DereferenceLink();
01808 
01809     // Get dir and file names
01810     string path = GetPath();
01811     size_t pos = path.find_last_of(ALL_SEPARATORS);
01812     if (pos == NPOS) {
01813         return; 
01814     }
01815     string filename = path.substr(pos+1);
01816     string dirname  = path.substr(0, pos);
01817     if ( dirname.empty() ) {
01818         return;
01819     }
01820     // Dereference path one level up
01821     CDirEntry e(dirname);
01822     e.DereferencePath();
01823     Reset(MakePath(e.GetPath(), filename));
01824 }
01825 
01826 
01827 bool CDirEntry::Copy(const string& path, TCopyFlags flags, size_t buf_size)
01828     const
01829 {
01830     // Dereference link if specified
01831     bool follow = F_ISSET(flags, fCF_FollowLinks);
01832     EType type = GetType(follow ? eFollowLinks : eIgnoreLinks);
01833     switch (type) {
01834         case eFile:
01835             {
01836                 CFile entry(GetPath());
01837                 return entry.Copy(path, flags, buf_size);
01838             }
01839         case eDir: 
01840             {
01841                 CDir entry(GetPath());
01842                 return entry.Copy(path, flags, buf_size);
01843             }
01844         case eLink:
01845             {
01846                 CSymLink entry(GetPath());
01847                 return entry.Copy(path, flags, buf_size);
01848             }
01849         case eUnknown:
01850             {
01851                 return false;
01852             }
01853         default:
01854             break;
01855     }
01856     // We "don't know" how to copy entry of other type, by default.
01857     // Use overloaded Copy() method in derived classes.
01858     return (flags & fCF_SkipUnsupported) == fCF_SkipUnsupported;
01859 }
01860 
01861 
01862 bool CDirEntry::Rename(const string& newname, TRenameFlags flags)
01863 {
01864     CDirEntry src(*this);
01865     CDirEntry dst(newname);
01866 
01867     // Dereference links
01868     if ( F_ISSET(flags, fRF_FollowLinks) ) {
01869         src.DereferenceLink();
01870         dst.DereferenceLink();
01871     }
01872     // The source entry must exists
01873     EType src_type = src.GetType();
01874     if ( src_type == eUnknown )  {
01875         LOG_ERROR_AND_RETURN("CDirEntry::Rename():"
01876                              " Source path does not exist: " << src.GetPath());
01877     }
01878     EType dst_type = dst.GetType();
01879     
01880     // If destination exists...
01881     if ( dst_type != eUnknown ) {
01882         // Can rename entries with different types?
01883         if ( F_ISSET(flags, fRF_EqualTypes)  &&  (src_type != dst_type) ) {
01884             LOG_ERROR_AND_RETURN("CDirEntry::Rename():"
01885                                  " Both source and destination exist"
01886                                  " and have different types: "
01887                                  << src.GetPath() << " and " << dst.GetPath());
01888         }
01889         // Can overwrite entry?
01890         if ( !F_ISSET(flags, fRF_Overwrite) ) {
01891             LOG_ERROR_AND_RETURN("CDirEntry::Rename():"
01892                                  " Destination path already exists: "
01893                                  << dst.GetPath());
01894         }
01895         // Rename only if destination is older, otherwise just remove source
01896         if ( F_ISSET(flags, fRF_Update)  &&  !src.IsNewer(dst.GetPath(), 0)) {
01897             return src.Remove();
01898         }
01899         // Backup destination entry first
01900         if ( F_ISSET(flags, fRF_Backup) ) {
01901             // Use new CDirEntry object for 'dst', because its path
01902             // will be changed after backup
01903             CDirEntry dst_tmp(dst);
01904             if ( !dst_tmp.Backup(GetBackupSuffix(), eBackup_Rename) ) {
01905                 LOG_ERROR_AND_RETURN("CDirEntry::Rename():"
01906                                      " Cannot backup " << dst.GetPath());
01907             }
01908         }
01909         // Overwrite destination entry
01910         if ( F_ISSET(flags, fRF_Overwrite) ) {
01911             if ( dst.Exists() ) {
01912                 dst.Remove();
01913             }
01914         }
01915     }
01916 
01917     // On some platform rename() fails if destination entry exists, 
01918     // on others it can overwrite destination.
01919     // For consistency return FALSE if destination already exists.
01920     if ( dst.Exists() ) {
01921         LOG_ERROR_AND_RETURN("CDirEntry::Rename():"
01922                              " Destination path exists: " << GetPath());
01923     }
01924     // Rename
01925     if ( rename(src.GetPath().c_str(), dst.GetPath().c_str()) != 0 ) {
01926 #ifdef NCBI_OS_MSWIN
01927         if ( errno != EACCES ) {
01928             LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::Rename():"
01929                                        " rename() failed for " << GetPath());
01930         }
01931 #else  // NCBI_OS_UNIX
01932         if ( errno != EXDEV ) {
01933             LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::Rename():"
01934                                        " rename() failed for " << GetPath());
01935         }
01936 #endif // NCBI_OS_...
01937         // Note that rename() fails in the case of cross-device renaming.
01938         // So, try to copy, instead, then remove the original.
01939         auto_ptr<CDirEntry>
01940             e(CDirEntry::CreateObject(src_type, src.GetPath()));
01941         if ( !e->Copy(dst.GetPath(), fCF_Recursive | fCF_PreserveAll ) ) {
01942             auto_ptr<CDirEntry> 
01943                 tmp(CDirEntry::CreateObject(src_type, dst.GetPath()));
01944             tmp->Remove(eRecursive);
01945             return false;
01946         }
01947         // Remove 'src'
01948         if ( !e->Remove(eRecursive) ) {
01949             // Do not delete 'dst' here because in case of directories the
01950             // source may be already partially removed, so we can lose data.
01951             return false;
01952         }
01953     }
01954     Reset(newname);
01955     return true;
01956 }
01957 
01958 
01959 bool CDirEntry::Remove(EDirRemoveMode mode) const
01960 {
01961     // This is a directory ?
01962     if ( IsDir(eIgnoreLinks) ) {
01963         CDir dir(GetPath());
01964         return dir.Remove(mode);
01965     }
01966     // Other entries
01967     if ( remove(GetPath().c_str()) != 0 ) {
01968         switch (errno) {
01969         case ENOENT:
01970             if (mode == eRecursiveIgnoreMissing)
01971                 return true;
01972             break;
01973 
01974 #if defined(NCBI_OS_MSWIN)
01975         case EACCES:
01976             if (NCBI_PARAM_TYPE(NCBI, DeleteReadOnlyFiles)::GetDefault()) {
01977                 if (!SetMode(eDefault))
01978                     return false;
01979                 if (remove(GetPath().c_str()) == 0)
01980                     return true;
01981             }
01982 #endif
01983         }
01984         LOG_ERROR_AND_RETURN_ERRNO(
01985             "CDirEntry::Remove():"
01986             " remove() failed for " << GetPath());
01987     }
01988     return true;
01989 }
01990 
01991 
01992 bool CDirEntry::Backup(const string& suffix, EBackupMode mode,
01993                        TCopyFlags copyflags, size_t copybufsize)
01994 {
01995     string backup_name = DeleteTrailingPathSeparator(GetPath()) +
01996                          (suffix.empty() ? string(GetBackupSuffix()) : suffix);
01997     switch (mode) {
01998         case eBackup_Copy:
01999             {
02000                 TCopyFlags flags = copyflags;
02001                 flags &= ~(fCF_Update | fCF_Backup);
02002                 flags |=  (fCF_Overwrite | fCF_TopDirOnly);
02003                 return Copy(backup_name, flags, copybufsize);
02004             }
02005         case eBackup_Rename:
02006             return Rename(backup_name, fRF_Overwrite);
02007         default:
02008             _TROUBLE;
02009     }
02010     return false;
02011 }
02012 
02013 
02014 bool CDirEntry::IsNewer(const string& entry_name, TIfAbsent2 if_absent) const
02015 {
02016     CDirEntry entry(entry_name);
02017     CTime this_time;
02018     CTime entry_time;
02019     int v = 0;
02020 
02021     if ( !GetTime(&this_time) ) {
02022         v += 1;
02023     }
02024     if ( !entry.GetTime(&entry_time) ) {
02025         v += 2;
02026     }
02027     if ( v == 0 ) {
02028         return this_time > entry_time;
02029     }
02030     if ( if_absent ) {
02031         switch(v) {
02032         case 1:  // NoThis - HasPath
02033             if ( if_absent &
02034                  (fNoThisHasPath_Newer | fNoThisHasPath_NotNewer) )
02035                 return (if_absent & fNoThisHasPath_Newer) > 0;
02036             break;
02037         case 2:  // HasThis - NoPath
02038             if ( if_absent &
02039                  (fHasThisNoPath_Newer | fHasThisNoPath_NotNewer) )
02040                 return (if_absent & fHasThisNoPath_Newer) > 0;
02041             break;
02042         case 3:  // NoThis - NoPath
02043             if ( if_absent &
02044                  (fNoThisNoPath_Newer | fNoThisNoPath_NotNewer) )
02045                 return (if_absent & fNoThisNoPath_Newer) > 0;
02046             break;
02047         }
02048     }
02049     // throw an exception by default
02050     NCBI_THROW(CFileException, eNotExists, 
02051                "Directory entry does not exist");
02052     /*NOTREACHED*/
02053     return false;
02054 }
02055 
02056 
02057 bool CDirEntry::IsNewer(time_t tm, EIfAbsent if_absent) const
02058 {
02059     time_t current;
02060     if ( !GetTimeT(&current) ) {
02061         switch(if_absent) {
02062         case eIfAbsent_Newer:
02063             return true;
02064         case eIfAbsent_NotNewer:
02065             return false;
02066         case eIfAbsent_Throw:
02067         default:
02068             NCBI_THROW(CFileException, eNotExists,
02069                        "Directory entry does not exist");
02070         }
02071     }
02072     return current > tm;
02073 }
02074 
02075 
02076 bool CDirEntry::IsNewer(const CTime& tm, EIfAbsent if_absent) const
02077 {
02078     CTime current;
02079     if ( !GetTime(&current) ) {
02080         switch(if_absent) {
02081         case eIfAbsent_Newer:
02082             return true;
02083         case eIfAbsent_NotNewer:
02084             return false;
02085         case eIfAbsent_Throw:
02086         default:
02087             NCBI_THROW(CFileException, eNotExists, 
02088                        "Directory entry does not exist");
02089         }
02090     }
02091     return current > tm;
02092 }
02093 
02094 
02095 bool CDirEntry::IsIdentical(const string& entry_name,
02096                             EFollowLinks  follow_links) const
02097 {
02098 #if defined(NCBI_OS_UNIX)
02099     struct SStat st1, st2;
02100     if ( !Stat(&st1, follow_links) ) {
02101         LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::IsIdentical():"
02102                                    " Cannot find " << GetPath());
02103     }
02104     if ( !CDirEntry(entry_name).Stat(&st2, follow_links) ) {
02105         LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::IsIdentical():"
02106                                    " Cannot find " << entry_name);
02107     }
02108     return st1.orig.st_dev == st2.orig.st_dev  &&
02109            st1.orig.st_ino == st2.orig.st_ino;
02110 #else
02111     return NormalizePath(GetPath(),  follow_links) ==
02112            NormalizePath(entry_name, follow_links);
02113 #endif
02114 }
02115 
02116 
02117 bool CDirEntry::GetOwner(string* owner, string* group,
02118                          EFollowLinks follow) const
02119 {
02120     if ( !owner  &&  !group ) {
02121         return false;
02122     }
02123 
02124 #if defined(NCBI_OS_MSWIN)
02125 
02126     return CWinSecurity::GetFileOwner(GetPath(), owner, group);
02127 
02128 #elif defined(NCBI_OS_UNIX)
02129 
02130     struct stat st;
02131     int errcode;
02132     
02133     if ( follow == eFollowLinks ) {
02134         errcode = stat(GetPath().c_str(), &st);
02135     } else {
02136         errcode = lstat(GetPath().c_str(), &st);
02137     }
02138     if ( errcode != 0 ) {
02139         LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::GetOwner():"
02140                                    " stat() failed for " << GetPath());
02141     }
02142     
02143     if ( owner ) {
02144         struct passwd *pw = getpwuid(st.st_uid);
02145         if (pw) {
02146             (*owner).assign(pw->pw_name);
02147         } else {
02148             NStr::UIntToString(*owner, st.st_uid);
02149         }
02150     }
02151     if ( group ) {
02152         struct group *gr = getgrgid(st.st_gid);
02153         if ( gr ) {
02154             (*group).assign(gr->gr_name);
02155         } else {
02156             NStr::UIntToString(*group, st.st_gid);
02157         }
02158     }
02159     return true;
02160 #endif
02161 }
02162 
02163 
02164 bool CDirEntry::SetOwner(const string& owner, const string& group,
02165                          EFollowLinks follow) const
02166 {
02167 #if defined(NCBI_OS_MSWIN)
02168 
02169     // On MS Windows change we can change file owner only
02170     return CWinSecurity::SetFileOwner(GetPath(), owner);
02171 
02172 #elif defined(NCBI_OS_UNIX)
02173 
02174     if ( owner.empty()  &&  group.empty() ) {
02175         return false;
02176     }
02177 
02178     struct stat st;
02179     int errcode;
02180     
02181     if ( follow == eFollowLinks ) {
02182         errcode = stat(GetPath().c_str(), &st);
02183     } else {
02184         errcode = lstat(GetPath().c_str(), &st);
02185     }
02186     if ( errcode != 0 ) {
02187         LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::GetOwner():"
02188                                    " stat() failed for " << GetPath());
02189     }
02190     
02191     uid_t uid = uid_t(-1);
02192     gid_t gid = gid_t(-1);
02193     
02194     if ( !owner.empty() ) {
02195         struct passwd *pw = getpwnam(owner.c_str());
02196         if ( !pw ) {
02197             uid = (uid_t) NStr::StringToUInt(owner.c_str(),
02198                                              NStr::fConvErr_NoThrow, 0);
02199             if ( errno )
02200                 LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::SetOwner():"
02201                                            " Invalid owner name " << owner);
02202         } else {
02203             uid = pw->pw_uid;
02204         }
02205     }
02206     if ( !group.empty() ) {
02207         struct group *gr = getgrnam(group.c_str());
02208         if ( !gr ) {
02209             gid = (gid_t) NStr::StringToUInt(group.c_str(),
02210                                              NStr::fConvErr_NoThrow, 0);
02211             if ( errno )
02212                 LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::SetOwner():"
02213                                            " Invalid group name " << group);
02214         } else {
02215             gid = gr->gr_gid;
02216         }
02217     }
02218     
02219     if ( follow == eFollowLinks ) {
02220         if ( chown(GetPath().c_str(), uid, gid) ) {
02221             LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::SetOwner():"
02222                                        " Cannot change owner for "
02223                                        << GetPath());
02224         }
02225     } else {
02226 #  if defined(HAVE_LCHOWN)
02227         if ( lchown(GetPath().c_str(), uid, gid) ) {
02228             LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::SetOwner():"
02229                                        " Cannot change owner for "
02230                                        << GetPath());
02231         }
02232 #  endif
02233     }
02234     return true;
02235 
02236 #endif
02237 }
02238 
02239 
02240 string CDirEntry::GetTmpName(ETmpFileCreationMode mode)
02241 {
02242 #if defined(NCBI_OS_MSWIN)  ||  defined(NCBI_OS_UNIX)
02243     return GetTmpNameEx(kEmptyStr, kEmptyStr, mode);
02244 #else
02245     if (mode == eTmpFileCreate) {
02246         ERR_POST_X(2, Warning << 
02247                    "Temporary file cannot be auto-created on this platform,"
02248                    " so return its name only");
02249     }
02250     char* filename = tempnam(0,0);
02251     if ( !filename ) {
02252         return kEmptyStr;
02253     }
02254     string res(filename);
02255     free(filename);
02256     return res;
02257 #endif
02258 }
02259 
02260 
02261 #if !defined(NCBI_OS_UNIX)
02262 
02263 static string s_StdGetTmpName(const char* dir, const char* prefix)
02264 {
02265     char* filename = tempnam(dir, prefix);
02266     if ( !filename ) {
02267         return kEmptyStr;
02268     }
02269     string str(filename);
02270     free(filename);
02271     return str;
02272 }
02273 
02274 #endif
02275 
02276 
02277 string CDirEntry::GetTmpNameEx(const string&        dir, 
02278                                const string&        prefix,
02279                                ETmpFileCreationMode mode)
02280 {
02281 #if defined(NCBI_OS_MSWIN)  ||  defined(NCBI_OS_UNIX)
02282     string x_dir = dir;
02283     if ( x_dir.empty() ) {
02284         // Get application specific temporary directory name (see CParam)
02285         x_dir = NCBI_PARAM_TYPE(NCBI,TmpDir)::GetThreadDefault();
02286         if ( x_dir.empty() ) {
02287             // Use default TMP directory specified by OS
02288             x_dir = CDir::GetTmpDir();
02289         }
02290     }
02291     if ( !x_dir.empty() ) {
02292         x_dir = AddTrailingPathSeparator(x_dir);
02293     }
02294     string fn;
02295 
02296 #  if defined(NCBI_OS_UNIX)
02297     string pattern = x_dir + prefix + "XXXXXX";
02298     AutoPtr<char, CDeleter<char> > filename(strdup(pattern.c_str()));
02299     int fd = mkstemp(filename.get());
02300     close(fd);
02301     if (mode != eTmpFileCreate) {
02302         remove(filename.get());
02303     }
02304     fn = filename.get();
02305 
02306 #  elif defined(NCBI_OS_MSWIN)
02307     char buffer[MAX_PATH];
02308     HANDLE hFile = INVALID_HANDLE_VALUE;
02309     srand((unsigned)time(0));
02310     unsigned long ofs = rand();
02311 
02312     while ( ofs < numeric_limits<unsigned long>::max() ) {
02313         _ultoa((unsigned long)ofs, buffer, 24);
02314         fn = x_dir + prefix + buffer;
02315         hFile = CreateFile(fn.c_str(), GENERIC_ALL, 0, NULL,
02316                             CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY, NULL);
02317         if (hFile != INVALID_HANDLE_VALUE) {
02318             break;
02319         }
02320         ofs++;
02321     }
02322     CloseHandle(hFile);
02323     if (ofs == numeric_limits<unsigned long>::max() ) {
02324         return kEmptyStr;
02325     }
02326     if (mode != eTmpFileCreate) {
02327         remove(fn.c_str());
02328     }
02329 
02330 #  endif
02331 
02332 #else // defined(NCBI_OS_MSWIN)  ||  defined(NCBI_OS_UNIX)
02333     if (mode == eTmpFileCreate) {
02334         ERR_POST_X(3, Warning << "CFile::GetTmpNameEx():"
02335                    " Temporary file cannot be auto-created on this platform,"
02336                    " so return its name only");
02337     }
02338     fn = s_StdGetTmpName(dir.c_str(), prefix.c_str());
02339 #endif
02340     return fn;
02341 }
02342 
02343 
02344 class CTmpStream : public fstream
02345 {
02346 public:
02347 
02348     CTmpStream(const char* s, IOS_BASE::openmode mode) : fstream(s, mode)
02349     {
02350         m_FileName = s; 
02351         // Try to remove file and OS will automatically delete it after
02352         // the last file descriptor to it is closed (works only on UNIXes)
02353         CFile(s).Remove();
02354     }
02355 
02356 #if defined(NCBI_OS_MSWIN)
02357     CTmpStream(const char* s, FILE* file) : fstream(file)
02358     {
02359         m_FileName = s; 
02360     }
02361 #endif    
02362 
02363     virtual ~CTmpStream(void) 
02364     { 
02365         close();
02366         if ( !m_FileName.empty() ) {
02367             CFile(m_FileName).Remove();
02368         }
02369     }
02370 
02371 protected:
02372     string m_FileName;  // Temporary file name
02373 };
02374 
02375 
02376 fstream* CDirEntry::CreateTmpFile(const string& filename, 
02377                                   ETextBinary text_binary,
02378                                   EAllowRead  allow_read)
02379 {
02380     string tmpname = filename.empty() ? GetTmpName(eTmpFileCreate) : filename;
02381     if ( tmpname.empty() ) {
02382         LOG_ERROR("CDirEntry::CreateTmpFile():"
02383                   " Cannot get temporary file name");
02384         return 0;
02385     }
02386 #if defined(NCBI_OS_MSWIN)
02387     // Open file manually, because we cannot say to fstream
02388     // to use some specific flags for file opening.
02389     // MS Windows should delete created file automaticaly
02390     // after closing all opened file descriptors.
02391 
02392     // We cannot enable "only write" mode here,
02393     // so ignore 'allow_read' flag.
02394     // Specify 'TD' (_O_SHORT_LIVED | _O_TEMPORARY)
02395     char mode[6] = "w+TDb";
02396     if (text_binary != eBinary) {
02397         mode[4] = '\0';
02398     }
02399     FILE* file = fopen(tmpname.c_str(), mode);
02400     if ( !file ) {
02401         return 0;
02402     }
02403     // Create FILE* based fstream.
02404     fstream* stream = new CTmpStream(tmpname.c_str(), file);
02405     // We dont need to close FILE*, it will be closed in the fstream
02406 
02407 #else
02408     // Create filename based fstream
02409     ios::openmode mode = ios::out | ios::trunc;
02410     if ( text_binary == eBinary ) {
02411         mode = mode | ios::binary;
02412     }
02413     if ( allow_read == eAllowRead ) {
02414         mode = mode | ios::in;
02415     }
02416     fstream* stream = new CTmpStream(tmpname.c_str(), mode);
02417 #endif
02418 
02419     if ( !stream->good() ) {
02420         delete stream;
02421         return 0;
02422     }
02423     return stream;
02424 }
02425 
02426 
02427 fstream* CDirEntry::CreateTmpFileEx(const string& dir, const string& prefix,
02428                                     ETextBinary text_binary, 
02429                                     EAllowRead allow_read)
02430 {
02431     return CreateTmpFile(GetTmpNameEx(dir, prefix, eTmpFileCreate),
02432                          text_binary, allow_read);
02433 }
02434 
02435 
02436 // Helper: Copy attributes (owner/date/time) from one entry to another.
02437 // Both entries should have equal type.
02438 //
02439 // UNIX:
02440 //     In mostly cases only super-user can change owner for
02441 //     destination entry.  The owner of a file may change the group of
02442 //     the file to any group of which that owner is a member.
02443 // WINDOWS:
02444 //     This function doesn't support ownership change yet.
02445 //
02446 static bool s_CopyAttrs(const char* from, const char* to,
02447                         CDirEntry::EType type, CDirEntry::TCopyFlags flags)
02448 {
02449 #if defined(NCBI_OS_UNIX)
02450 
02451     CDirEntry::SStat st;
02452     if ( !CDirEntry(from).Stat(&st) ) {
02453         LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::s_CopyAttr():"
02454                                    " stat() failed for " << from);
02455     }
02456 
02457     // Date/time.
02458     // Set time before chmod() call, because on some platforms
02459     // setting time can affect file mode also.
02460     if ( F_ISSET(flags, CDirEntry::fCF_PreserveTime) ) {
02461 #  if defined(HAVE_UTIMES)
02462         struct timeval tvp[2];
02463         tvp[0].tv_sec  = st.orig.st_atime;
02464         tvp[0].tv_usec = st.atime_nsec / 1000;
02465         tvp[1].tv_sec  = st.orig.st_mtime;
02466         tvp[1].tv_usec = st.mtime_nsec / 1000;
02467 #    if defined(HAVE_LUTIMES)
02468         if (lutimes(to, tvp)) {
02469             LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::s_CopyAttr():"
02470                                        " lutimes() failed for " << to);
02471         }
02472 #    else
02473         if (utimes(to, tvp)) {
02474             LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::s_CopyAttr():"
02475                                        " utimes() failed for " << to);
02476         }
02477 #    endif
02478 # else  // !HAVE_UTIMES
02479         // utimes() does not exists on current platform,
02480         // so use less accurate utime().
02481         struct utimbuf times;
02482         times.actime  = st.orig.st_atime;
02483         times.modtime = st.orig.st_mtime;
02484         if (utime(to, &times)) {
02485             LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::s_CopyAttr():"
02486                                        " utime() failed for " << to);
02487         }
02488 #  endif // HAVE_UTIMES
02489     }
02490 
02491     // Owner. 
02492     // To improve performance change it right here,
02493     // do not use GetOwner/SetOwner.
02494 
02495     if ( F_ISSET(flags, CDirEntry::fCF_PreserveOwner) ) {
02496         if ( type == CDirEntry::eLink ) {
02497 #  if defined(HAVE_LCHOWN)
02498             if ( lchown(to, st.orig.st_uid, st.orig.st_gid) ) {
02499                 if (errno != EPERM) {
02500                     LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::s_CopyAttr():"
02501                                                " lchown() failed for " << to);
02502                 }
02503             }
02504 #  endif
02505             // We cannot change permissions for sym.links (below),
02506             // so just exit from the function.
02507             return true;
02508         } else {
02509             // Changing the ownership will probably fail, unless we're root.
02510             // The setuid/gid bits can be cleared by OS.  If chown() fails,
02511             // strip the setuid/gid bits.
02512             if ( chown(to, st.orig.st_uid, st.orig.st_gid) ) {
02513                 if ( errno != EPERM ) {
02514                     LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::s_CopyAttr():"
02515                                                " chown() failed for " << to);
02516                 }
02517                 st.orig.st_mode &= ~(S_ISUID | S_ISGID);
02518             }
02519         }
02520     }
02521 
02522     // Permissions
02523     if ( F_ISSET(flags, CDirEntry::fCF_PreservePerm)  &&
02524         type != CDirEntry::eLink ) {
02525         if ( chmod(to, st.orig.st_mode) ) {
02526             LOG_ERROR_AND_RETURN_ERRNO("CDirEntry::s_CopyAttr():"
02527                                        " chmod() failed for " << to);
02528         }
02529     }
02530     return true;
02531 
02532 
02533 #elif defined(NCBI_OS_MSWIN)
02534 
02535     CDirEntry efrom(from), eto(to);
02536 
02537     WIN32_FILE_ATTRIBUTE_DATA attr;
02538     if ( !::GetFileAttributesEx(from, GetFileExInfoStandard, &attr) ) {
02539         return false;
02540     }
02541 
02542     // Date/time
02543     if ( F_ISSET(flags, CDirEntry::fCF_PreserveTime) ) {
02544         HANDLE h = CreateFile(to, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ, NULL,
02545                               OPEN_EXISTING,
02546                               FILE_FLAG_BACKUP_SEMANTICS /*for dirs*/, NULL); 
02547         if ( h == INVALID_HANDLE_VALUE ) {
02548             LOG_ERROR_AND_RETURN("CDirEntry::s_CopyAttr():"
02549                                  " Cannot open " << to);
02550         }
02551         if ( !SetFileTime(h, &attr.ftCreationTime, &attr.ftLastAccessTime,
02552                         &attr.ftLastWriteTime) ) {
02553             CloseHandle(h);
02554             LOG_ERROR_AND_RETURN("CDirEntry::s_CopyAttr():"
02555                                  " Cannot change time for " << to);
02556         }
02557         CloseHandle(h);
02558     }
02559 
02560     // Permissions
02561     if ( F_ISSET(flags, CDirEntry::fCF_PreservePerm) ) {
02562         if ( !::SetFileAttributes(to, attr.dwFileAttributes) ) {
02563             LOG_ERROR_AND_RETURN("CDirEntry::s_CopyAttr():"
02564                                  " Cannot change pemissions for " << to);
02565         }
02566     }
02567 
02568     // Owner
02569     if ( F_ISSET(flags, CDirEntry::fCF_PreserveOwner) ) {
02570         string owner, group;
02571         // We dont check result here, because often is not impossible
02572         // to save an original owner name without administators rights.
02573         if ( efrom.GetOwner(&owner, &group) ) {
02574             eto.SetOwner(owner, group);
02575         }
02576     }
02577 
02578     return true;
02579 #endif
02580 }
02581 
02582 
02583 //////////////////////////////////////////////////////////////////////////////
02584 //
02585 // CFile
02586 //
02587 
02588 
02589 CFile::~CFile(void)
02590 { 
02591     return;
02592 }
02593 
02594 
02595 Int8 CFile::GetLength(void) const
02596 {
02597 #if defined(NCBI_OS_MSWIN)
02598     struct __stat64 buf;
02599     if ( _stat64(GetPath().c_str(), &buf) != 0 ) {
02600         //LOG_ERROR("CFile:GetLength(): _stat64() failed for " << GetPath());
02601         return -1;
02602     }
02603 #else
02604     struct stat buf;
02605     if ( stat(GetPath().c_str(), &buf) != 0 ) {
02606         //LOG_ERROR("CFile:GetLength(): stat() failed for " << GetPath());
02607         return -1;
02608     }
02609 #endif
02610     return buf.st_size;
02611 }
02612 
02613 
02614 #if !defined(NCBI_OS_MSWIN)
02615 
02616 // Auxiliary function to copy a file
02617 bool s_CopyFile(const char* src, const char* dst, size_t buf_size)
02618 {
02619     CNcbiIfstream is(src, IOS_BASE::binary | IOS_BASE::in);
02620     CNcbiOfstream os(dst, IOS_BASE::binary | IOS_BASE::out | IOS_BASE::trunc);
02621 
02622     if ( !buf_size ) {
02623         if (CFile(src).GetLength() == 0) {
02624             return true;
02625         }
02626         // Next operation fails if the source file have zero size
02627         os << is.rdbuf();
02628         return os.good() ? true : false;
02629     }
02630 
02631     AutoPtr< char, ArrayDeleter<char> > buf_ptr(new char[buf_size]);
02632     char* buf = buf_ptr.get();
02633     bool failed = false;
02634  
02635     streamsize nread;
02636     do {
02637         is.read(buf, buf_size);
02638         nread = is.gcount();
02639         if ( nread ) {
02640             if (!os.write(buf, nread)) {
02641                 failed = true;
02642                 break;
02643             }
02644         }
02645     } while (is  &&  nread);
02646 
02647     return !failed;
02648 }
02649 
02650 #endif
02651 
02652 
02653 bool CFile::Copy(const string& newname, TCopyFlags flags, size_t buf_size) const
02654 {
02655     CFile src(*this);
02656     CFile dst(newname);
02657 
02658     // Dereference links
02659     if ( F_ISSET(flags, fCF_FollowLinks) ) {
02660         src.DereferenceLink();
02661         dst.DereferenceLink();
02662     }
02663     // The source file must exists
02664     EType src_type = src.GetType();
02665     if ( src_type != eFile )  {
02666         LOG_ERROR_AND_RETURN("CFile::Copy():"
02667                              " Source is not a file: " << GetPath());
02668     }
02669 
02670     EType dst_type   = dst.GetType();
02671     bool  dst_exists = (dst_type != eUnknown);
02672     
02673     // If destination exists...
02674     if ( dst_exists ) {
02675         // UNIX: check on copying file into yourself.
02676         // MS Window's ::CopyFile() can recognise such case.
02677 #if defined(NCBI_OS_UNIX)
02678         if ( src.IsIdentical(dst.GetPath()) ) {
02679             return false;
02680         }
02681 #endif
02682         // Can copy entries with different types?
02683         // The Destination must be a file too.
02684         if ( F_ISSET(flags, fCF_EqualTypes)  &&  (src_type != dst_type) ) {
02685             LOG_ERROR_AND_RETURN("CFile::Copy():"
02686                                  " Destination is not a file: "
02687                                  << dst.GetPath());
02688         }
02689         // Can overwrite entry?
02690         if ( !F_ISSET(flags, fCF_Overwrite) ) {
02691             LOG_ERROR_AND_RETURN("CFile::Copy():"
02692                                  " Destination file exists: "
02693                                  << dst.GetPath());
02694         }
02695         // Copy only if destination is older
02696         if ( F_ISSET(flags, fCF_Update)  &&  !src.IsNewer(dst.GetPath(),0) ) {
02697             return true;
02698         }
02699         // Backup destination entry first
02700         if ( F_ISSET(flags, fCF_Backup) ) {
02701             // Use new CDirEntry object for 'dst', because its path
02702             // will be changed after backup
02703             CDirEntry dst_tmp(dst);
02704             if ( !dst_tmp.Backup(GetBackupSuffix(), eBackup_Rename) ) {
02705                 LOG_ERROR_AND_RETURN("CFile::Copy():"
02706                                      " Cannot backup " << dst.GetPath());
02707             }
02708         }
02709     }
02710 
02711     // Copy
02712 #if defined(NCBI_OS_MSWIN)
02713     if ( !::CopyFile(src.GetPath().c_str(), dst.GetPath().c_str(), FALSE) )
02714         LOG_ERROR_AND_RETURN("CFile::Copy():"
02715                              " Cannot copy "
02716                              << src.GetPath() << " to " << dst.GetPath());
02717 #else
02718     if ( !s_CopyFile(src.GetPath().c_str(), dst.GetPath().c_str(), buf_size) ){
02719         LOG_ERROR_AND_RETURN("CFile::Copy():"
02720                              " Cannot copy "
02721                              << src.GetPath() << " to " << dst.GetPath());
02722     }
02723 #endif
02724 
02725     // Verify copied data
02726     if ( F_ISSET(flags, fCF_Verify)  &&  !src.Compare(dst.GetPath()) ) {
02727         LOG_ERROR_AND_RETURN("CFile::Copy():"
02728                              " Copy verification for "
02729                              << src.GetPath() << " and " << dst.GetPath()
02730                              << " failed");
02731     }
02732 
02733     // Preserve attributes.
02734 #if defined(NCBI_OS_MSWIN)
02735     // On MS Windows ::CopyFile() already preserved file attributes
02736     // and all date/times.
02737     flags &= ~(fCF_PreservePerm | fCF_PreserveTime);
02738 #endif
02739     if ( flags & fCF_PreserveAll ) {
02740         if ( !s_CopyAttrs(src.GetPath().c_str(),
02741                           dst.GetPath().c_str(), eFile, flags) ) {
02742             return false;
02743         }
02744     } else {
02745         if ( !dst.SetMode(fDefault, fDefault, fDefault) ) {
02746             return false;
02747         }
02748     }
02749     return true;
02750 }
02751 
02752 
02753  bool CFile::Compare(const string& file, size_t buf_size) const
02754 {
02755     if ( CFile(GetPath()).GetLength() != CFile(file).GetLength() ) {
02756         return false;
02757     }
02758     CNcbiIfstream f1(GetPath().c_str(), IOS_BASE::binary | IOS_BASE::in);
02759     CNcbiIfstream f2(file.c_str(),      IOS_BASE::binary | IOS_BASE::in);
02760 
02761     if ( !buf_size ) {
02762         buf_size = kDefaultBufferSize;
02763     }
02764     char* buf1  = new char[buf_size];
02765     char* buf2  = new char[buf_size];
02766     bool  equal = true;
02767 
02768     while ( f1.good()  &&  f2.good() ) {
02769         // Fill buffers
02770         f1.read(buf1, buf_size);
02771         f2.read(buf2, buf_size);
02772         if ( f1.gcount() != f2.gcount() ) {
02773             equal = false;
02774             break;
02775         }
02776         // Compare
02777         if ( memcmp(buf1, buf2, f1.gcount()) != 0 ) {
02778             equal = false;
02779             break;
02780         }
02781     }
02782     // Clean memory
02783     delete[] buf1;
02784     delete[] buf2;
02785 
02786     // Both files should be in the EOF state
02787     return equal  &&  f1.eof()  &&  f2.eof();
02788 }
02789 
02790 static inline char
02791 x_GetChar(CNcbiIfstream& f, CFile::ECompareText mode,
02792     char* buf, size_t buf_size, char*& pos, streamsize& sizeleft)
02793 {
02794     char c = '\0';
02795     do {
02796         if (sizeleft == 0) {
02797             f.read(buf, buf_size);
02798             sizeleft = f.gcount();
02799             pos = buf;
02800         }
02801         if (sizeleft > 0) {
02802             c = *pos;
02803             ++pos;
02804             --sizeleft;
02805         } else {
02806             return '\0';
02807         }
02808     } while ( (mode == CFile::eIgnoreEol && (c == '\n' || c == '\r')) ||
02809               (mode == CFile::eIgnoreWs  && isspace((unsigned char)c))
02810             );
02811     return c;
02812 }
02813 
02814 bool CFile::CompareTextContents(const string& file, ECompareText mode,
02815                              size_t buf_size) const
02816 {
02817     CNcbiIfstream f1(GetPath().c_str(), IOS_BASE::in);
02818     CNcbiIfstream f2(file.c_str(),      IOS_BASE::in);
02819     if ( !buf_size ) {
02820         buf_size = kDefaultBufferSize;
02821     }
02822     char* buf1  = new char[buf_size];
02823     char* buf2  = new char[buf_size];
02824     streamsize size1=0, size2=0;
02825     char *pos1=0, *pos2=0;
02826     bool  equal = true;
02827     while ( equal ) {
02828         char c1 = x_GetChar(f1,mode,buf1,buf_size,pos1,size1);
02829         char c2 = x_GetChar(f2,mode,buf2,buf_size,pos2,size2);
02830         equal = c1 == c2;
02831         if (!c1 || !c2) {
02832             break;
02833         }
02834     }
02835     delete[] buf1;
02836     delete[] buf2;
02837     return equal && f1.eof() && f2.eof();
02838 }
02839 
02840 //////////////////////////////////////////////////////////////////////////////
02841 //
02842 // CDir
02843 //
02844 
02845 #if defined(NCBI_OS_UNIX)
02846 
02847 static bool s_GetHomeByUID(string& home)
02848 {
02849     // Get the info using user ID
02850     struct passwd* pwd;
02851 
02852     if ((pwd = getpwuid(getuid())) == 0) {
02853         LOG_ERROR_AND_RETURN_ERRNO("s_GetHomeByUID(): getpwuid() failed");
02854     }
02855     home = pwd->pw_dir;
02856     return true;
02857 }
02858 
02859 static bool s_GetHomeByLOGIN(string& home)
02860 {
02861     char* ptr = 0;
02862     // Get user name
02863     if ( !(ptr = getenv("USER")) ) {
02864         if ( !(ptr = getenv("LOGNAME")) ) {
02865             if ( !(ptr = getlogin()) ) {
02866                 LOG_ERROR_AND_RETURN_ERRNO("s_GetHomeByLOGIN():"
02867                                            " Unable to get user name");
02868             }
02869         }
02870     }
02871     // Get home dir for this user
02872     struct passwd* pwd = getpwnam(ptr);
02873     if ( !pwd ||  pwd->pw_dir[0] == '\0') {
02874         LOG_ERROR_AND_RETURN_ERRNO("s_GetHomeByLOGIN():"
02875                                    " getpwnam() failed");
02876     }
02877     home = pwd->pw_dir;
02878     return true;
02879 }
02880 
02881 #endif // NCBI_OS_UNIX
02882 
02883 
02884 string CDir::GetHome(void)
02885 {
02886     char*  str;
02887     string home;
02888 
02889 #if defined(NCBI_OS_MSWIN)
02890     // Get home dir from environment variables
02891     // like - C:\Documents and Settings\user\Application Data
02892     str = getenv("APPDATA");
02893     if ( str ) {
02894         home = str;
02895     } else {
02896         // like - C:\Documents and Settings\user
02897         str = getenv("USERPROFILE");
02898         if ( str ) {
02899             home = str;
02900         }
02901     }
02902 #elif defined(NCBI_OS_UNIX)
02903     // Try get home dir from environment variable
02904     str = getenv("HOME");
02905     if ( str ) {
02906         home = str;
02907     } else {
02908         // Try to retrieve the home dir -- first use user's ID,
02909         //  and if failed, then use user's login name.
02910         if ( !s_GetHomeByUID(home) ) { 
02911             s_GetHomeByLOGIN(home);
02912         }
02913     }
02914 #endif 
02915 
02916     // Add trailing separator if needed
02917     return AddTrailingPathSeparator(home);
02918 }
02919 
02920 
02921 string CDir::GetTmpDir(void)
02922 {
02923     string tmp;
02924 
02925 #if defined(NCBI_OS_UNIX)
02926 
02927     char* tmpdir = getenv("TMPDIR");
02928     if ( tmpdir ) {
02929         tmp = tmpdir;
02930     } else  {
02931 #  if defined(P_tmpdir)
02932         tmp = P_tmpdir;
02933 #  else
02934         tmp = "/tmp";
02935 #  endif
02936     }
02937 
02938 #elif defined(NCBI_OS_MSWIN)
02939 
02940     char* tmpdir = getenv("TEMP");
02941     if ( tmpdir ) {
02942         tmp = tmpdir;
02943     } else  {
02944 #  if defined(P_tmpdir)
02945         tmp = P_tmpdir;
02946 #  else
02947         tmp = CDir::GetHome();
02948 #  endif
02949     }
02950 
02951 #endif
02952 
02953     return tmp;
02954 }
02955 
02956 
02957 string CDir::GetCwd()
02958 {
02959     string cwd;
02960 
02961 #if defined(NCBI_OS_UNIX)
02962     char buf[4096];
02963     if ( getcwd(buf, sizeof(buf) - 1) ) {
02964         cwd = buf;
02965     }
02966 #elif defined(NCBI_OS_MSWIN)
02967     char buf[4096];
02968     if ( _getcwd(buf, sizeof(buf) - 1) ) {
02969         cwd = buf;
02970     }
02971 #endif
02972     return cwd;
02973 }
02974 
02975 
02976 bool CDir::SetCwd(const string& dir)
02977 {
02978 #if defined(NCBI_OS_UNIX)
02979     if ( chdir(dir.c_str()) != 0 ) {
02980         LOG_ERROR_AND_RETURN_ERRNO("CDir::SetCwd():"
02981                                    " Cannot change directory to " << dir);
02982     }
02983     return true;
02984 #elif defined(NCBI_OS_MSWIN)
02985     if ( _chdir(dir.c_str()) != 0 ) {
02986         LOG_ERROR_AND_RETURN_ERRNO("CDir::SetCwd():"
02987                                    " Cannot change directory to " << dir);
02988     }
02989     return true;
02990 #else    
02991     return false;
02992 #endif
02993 }
02994 
02995 
02996 CDir::~CDir(void)
02997 {
02998     return;
02999 }
03000 
03001 
03002 bool CDirEntry::MatchesMask(const string& name,
03003                             const vector<string>& masks,
03004                             NStr::ECase use_case)
03005 {
03006     if ( masks.empty() ) {
03007         return true;
03008     }
03009     ITERATE(vector<string>, itm, masks) {
03010         const string& mask = *itm;
03011         if ( MatchesMask(name, mask, use_case) ) {
03012             return true;
03013         }
03014     }
03015     return false;
03016 }
03017 
03018 
03019 // Helpers functions and macro for GetEntries().
03020 
03021 #if defined(NCBI_OS_MSWIN)
03022 
03023 // Set errno for failed FindFirstFile/FindNextFile
03024 void s_SetFindFileError(void)
03025 {
03026     DWORD err = GetLastError();
03027     switch (err) {
03028         case ERROR_NO_MORE_FILES:
03029         case ERROR_FILE_NOT_FOUND:
03030         case ERROR_PATH_NOT_FOUND:
03031             errno = ENOENT;
03032             break;
03033         case ERROR_NOT_ENOUGH_MEMORY:
03034             errno = ENOMEM;
03035             break;
03036         default:
03037             errno = EINVAL;
03038             break;
03039     }
03040 }
03041 
03042 #  define IS_RECURSIVE_ENTRY                     \
03043     ( (flags & CDir::fIgnoreRecursive)  &&       \
03044       ((::strcmp(entry.cFileName, ".")  == 0) || \
03045        (::strcmp(entry.cFileName, "..") == 0)) )
03046 
03047 void s_AddEntry(CDir::TEntries* contents, const string& base_path,
03048                 const WIN32_FIND_DATA& entry, CDir::TGetEntriesFlags flags)
03049 {
03050     const string name = (flags & CDir::fIgnorePath) ?
03051                          entry.cFileName :
03052                          base_path + entry.cFileName;
03053         
03054     if (flags & CDir::fCreateObjects) {
03055         CDirEntry::EType type = (entry.dwFileAttributes &
03056                                  FILE_ATTRIBUTE_DIRECTORY) 
03057                                  ? CDirEntry::eDir : CDirEntry::eFile;
03058         contents->push_back(CDirEntry::CreateObject(type, name));
03059     } else {
03060         contents->push_back(new CDirEntry(name));
03061     }
03062 }
03063 
03064 #else // NCBI_OS_UNIX
03065 
03066 #  define IS_RECURSIVE_ENTRY                   \
03067     ( (flags & CDir::fIgnoreRecursive)  &&     \
03068       ((::strcmp(entry->d_name, ".")  == 0) || \
03069        (::strcmp(entry->d_name, "..") == 0)) )
03070 
03071 void s_AddEntry(CDir::TEntries* contents, const string& base_path,
03072                 const struct dirent* entry, CDir::TGetEntriesFlags flags)
03073 {
03074     const string name = (flags & CDir::fIgnorePath) ?
03075                          entry->d_name :
03076                          base_path + entry->d_name;
03077 
03078     if (flags & CDir::fCreateObjects) {
03079         CDirEntry::EType type = CDir::eUnknown;
03080 #  if defined(_DIRENT_HAVE_D_TYPE)
03081         struct stat st;
03082         if (entry->d_type) {
03083             st.st_mode = DTTOIF(entry->d_type);
03084             type = CDirEntry::GetType(st);
03085         }
03086 #  endif
03087         if (type == CDir::eUnknown) {
03088             if (flags & CDir::fIgnorePath) {
03089                 const string path = base_path + entry->d_name;
03090                 type = CDirEntry(path).GetType();
03091             } else {
03092                 type = CDirEntry(name).GetType();
03093             }
03094         }
03095         contents->push_back(CDirEntry::CreateObject(type, name));
03096     } else {
03097         contents->push_back(new CDirEntry(name));
03098     }
03099 }
03100 
03101 #endif
03102 
03103 
03104 CDir::TEntries CDir::GetEntries(const string& mask,
03105                                 TGetEntriesFlags flags) const
03106 {
03107     CMaskFileName masks;
03108     if ( !mask.empty() ) {
03109         masks.Add(mask);
03110     }
03111     return GetEntries(masks, flags);
03112 }
03113 
03114 
03115 CDir::TEntries* CDir::GetEntriesPtr(const string& mask,
03116                                    TGetEntriesFlags flags) const
03117 {
03118     CMaskFileName masks;
03119     if ( !mask.empty() ) {
03120         masks.Add(mask);
03121     }
03122     return GetEntriesPtr(masks, flags);
03123 }
03124 
03125 
03126 CDir::TEntries CDir::GetEntries(const vector<string>& masks,
03127                                 TGetEntriesFlags flags) const
03128 {
03129     auto_ptr<TEntries> contents(GetEntriesPtr(masks, flags));
03130     return *contents.get();
03131 }
03132 
03133 
03134 CDir::TEntries* CDir::GetEntriesPtr(const vector<string>& masks,
03135                                     TGetEntriesFlags flags) const
03136 {
03137     if ( masks.empty() ) {
03138         return GetEntriesPtr("", flags);
03139     }
03140     TEntries* contents = new(TEntries);
03141     string base_path = AddTrailingPathSeparator(GetPath().empty() ? DIR_CURRENT : GetPath());
03142     NStr::ECase use_case = (flags & fNoCase) ? NStr::eNocase : NStr::eCase;
03143 
03144 #if defined(NCBI_OS_MSWIN)
03145 
03146     // Append to the "path" mask for all files in directory
03147     string pattern = base_path + string("*");
03148 
03149     WIN32_FIND_DATA entry;
03150     HANDLE          handle;
03151 
03152     handle = FindFirstFile(pattern.c_str(), &entry);
03153     if (handle != INVALID_HANDLE_VALUE) {
03154         // Check all masks
03155         do {
03156             if (!IS_RECURSIVE_ENTRY) {
03157                 ITERATE(vector<string>, it, masks) {
03158                     const string& mask = *it;
03159                     if ( mask.empty()  ||
03160                         MatchesMask(entry.cFileName, mask, use_case) ) {
03161                         s_AddEntry(contents, base_path, entry, flags);
03162                         break;
03163                     }                
03164                 }
03165             }
03166         } while (FindNextFile(handle, &entry));
03167         FindClose(handle);
03168     } else {
03169         s_SetFindFileError();
03170     }
03171 
03172 #elif defined(NCBI_OS_UNIX)
03173     DIR* dir = opendir(base_path.c_str());
03174     if ( dir ) {
03175         while (struct dirent* entry = readdir(dir)) {
03176             if (IS_RECURSIVE_ENTRY) {
03177                 continue;
03178             }
03179             ITERATE(vector<string>, it, masks) {
03180                 const string& mask = *it;
03181                 if ( mask.empty()  ||
03182                     MatchesMask(entry->d_name, mask, use_case) ) {
03183                     s_AddEntry(contents, base_path, entry, flags);
03184                     break;
03185                 }
03186             } // ITERATE
03187         } // while
03188         closedir(dir);
03189     }
03190 #endif
03191     return contents;
03192 }
03193 
03194 
03195 CDir::TEntries CDir::GetEntries(const CMask& masks,
03196                                 TGetEntriesFlags flags) const
03197 {
03198     auto_ptr<TEntries> contents(GetEntriesPtr(masks, flags));
03199     return *contents.get();
03200 }
03201 
03202 
03203 CDir::TEntries* CDir::GetEntriesPtr(const CMask& masks,
03204                                     TGetEntriesFlags flags) const
03205 {
03206     TEntries* contents = new(TEntries);
03207     string base_path = AddTrailingPathSeparator(GetPath().empty() ? DIR_CURRENT : GetPath());
03208     NStr::ECase use_case = (flags & fNoCase) ? NStr::eNocase : NStr::eCase;
03209 
03210 #if defined(NCBI_OS_MSWIN)
03211     // Append to the "path" mask for all files in directory
03212     string pattern = base_path + "*";
03213 
03214     WIN32_FIND_DATA entry;
03215     HANDLE          handle;
03216 
03217     handle = FindFirstFile(pattern.c_str(), &entry);
03218     if (handle != INVALID_HANDLE_VALUE) {
03219         do {
03220             if ( !IS_RECURSIVE_ENTRY  &&
03221                  masks.Match(entry.cFileName, use_case) ) {
03222                 s_AddEntry(contents, base_path, entry, flags);
03223             }
03224         } while ( FindNextFile(handle, &entry) );
03225         FindClose(handle);
03226     } else {
03227         s_SetFindFileError();
03228     }
03229 
03230 #elif defined(NCBI_OS_UNIX)
03231     DIR* dir = opendir(base_path.c_str());
03232     if ( dir ) {
03233         while (struct dirent* entry = readdir(dir)) {
03234             if ( !IS_RECURSIVE_ENTRY  &&
03235                  masks.Match(entry->d_name, use_case) ) {
03236                 s_AddEntry(contents, base_path, entry, flags);
03237             }
03238         }
03239         closedir(dir);
03240     }
03241 #endif
03242     return contents;
03243 }
03244 
03245 
03246 bool CDir::Create(void) const
03247 {
03248     TMode user_mode, group_mode, other_mode;
03249     TSpecialModeBits special;
03250     GetDefaultMode(&user_mode, &group_mode, &other_mode, &special);
03251     mode_t mode = MakeModeT(user_mode, group_mode, other_mode, special);
03252 
03253 #if defined(NCBI_OS_MSWIN)
03254     errno = 0;
03255     if ( mkdir(GetPath().c_str()) != 0  &&  errno != EEXIST ) {
03256         LOG_ERROR_AND_RETURN_ERRNO("CDir::Create():"
03257                                    " Cannot create directory " << GetPath());
03258     }
03259 
03260 #elif defined(NCBI_OS_UNIX)
03261     errno = 0;
03262     // The permissions for the created directory are (mode & ~umask & 0777).
03263     if ( mkdir(GetPath().c_str(), mode) != 0  &&  errno != EEXIST ) {
03264         LOG_ERROR_AND_RETURN_ERRNO("CDir::Create():"
03265                                    " Cannot create directory " << GetPath());
03266     }
03267     // so we need to call chmod() directly
03268 #endif
03269     if ( chmod(GetPath().c_str(), mode) != 0 ) {
03270         LOG_ERROR_AND_RETURN_ERRNO("CDir::Create():"
03271                                    " Cannot set mode for directory "
03272                                    << GetPath());
03273     }
03274     return true;
03275 }
03276 
03277 
03278 bool CDir::CreatePath(void) const
03279 {
03280     if ( Exists() ) {
03281         return true;
03282     }
03283     string path(GetPath());
03284     if ( path.empty() ) {
03285         return true;
03286     }
03287     if ( path[path.length()-1] == GetPathSeparator() ) {
03288         path.erase(path.length() - 1);
03289     }
03290     string path_up = GetDir();
03291     if ( path_up == path ) {
03292         // special case: unknown disk name
03293         LOG_ERROR_AND_RETURN("CDir::CreatePath():"
03294                              " Disk name not specified: " << path);
03295     } 
03296     // Create a copy for this object to derive creation mode
03297     CDir dir_up(*this);
03298     dir_up.Reset(path_up);
03299     // Create upper level path
03300     if ( dir_up.CreatePath() ) {
03301         // Create current subdirectory
03302         return Create();
03303     }
03304     return false;
03305 }
03306 
03307 
03308 bool CDir::Copy(const string& newname, TCopyFlags flags, size_t buf_size) const
03309 {
03310     CDir src(*this);
03311     CDir dst(newname);
03312 
03313     // Dereference links
03314     bool follow = F_ISSET(flags, fCF_FollowLinks);
03315     if ( follow ) {
03316         src.DereferenceLink();
03317         dst.DereferenceLink();
03318     }
03319     // The source dir must exists
03320     EType src_type = src.GetType();
03321     if ( src_type != eDir )  {
03322         LOG_ERROR_AND_RETURN("CDir::Copy():"
03323                              " Source is not a directory: " << src.GetPath());
03324     }
03325     EType dst_type   = dst.GetType();
03326     bool  dst_exists = (dst_type != eUnknown);
03327     
03328     // If destination exists...
03329     if ( dst_exists ) {
03330         // Check on copying dir into yourself
03331         if ( src.IsIdentical(dst.GetPath()) ) {
03332             LOG_ERROR_AND_RETURN("CDir::Copy():"
03333                                  " Source and destination are the same: "
03334                                  << src.GetPath());
03335         }
03336         // Can rename entries with different types?
03337         if ( F_ISSET(flags, fCF_EqualTypes)  &&  (src_type != dst_type) ) {
03338             LOG_ERROR_AND_RETURN("CDir::Copy():"
03339                                  " Destination is not a directory: "
03340                                  << dst.GetPath());
03341         }
03342 
03343         // Some operation can be made for top directory only
03344 
03345         if ( F_ISSET(flags, fCF_TopDirOnly) ) {
03346             // Can overwrite entry?
03347             if ( !F_ISSET(flags, fCF_Overwrite) ) {
03348                 LOG_ERROR_AND_RETURN("CDir::Copy():"
03349                                      " Destination directory already exists: "
03350                                      << dst.GetPath());
03351             }
03352             // Copy only if destination is older
03353             if ( F_ISSET(flags, fCF_Update)  &&
03354                  !src.IsNewer(dst.GetPath(), 0) ) {
03355                 return true;
03356             }
03357             // Backup destination entry first
03358             if ( F_ISSET(flags, fCF_Backup) ) {
03359                 // Use new CDirEntry object for 'dst', because its path
03360                 // will be changed after backup
03361                 CDirEntry dst_tmp(dst);
03362                 if ( !dst_tmp.Backup(GetBackupSuffix(), eBackup_Rename) ) {
03363                     LOG_ERROR_AND_RETURN("CDir::Copy():"
03364                                          " Cannot backup destination"
03365                                          " directory: " << dst.GetPath());
03366                 }
03367                 // Create target directory
03368                 if ( !dst.CreatePath() ) {
03369                     LOG_ERROR_AND_RETURN("CDir::Copy():"
03370                                          " Cannot create target directory: "
03371                                          << dst.GetPath());
03372                 }
03373             }
03374             // Remove unneeded flags.
03375             // All dir entries can now be overwritten.
03376             flags &= ~(fCF_TopDirOnly | fCF_Update | fCF_Backup);
03377         }
03378     } else {
03379         // Create target directory
03380         if ( !dst.CreatePath() ) {
03381             LOG_ERROR_AND_RETURN("CDir::Copy():"
03382                                  " Cannot create target directory: "
03383                                  << dst.GetPath());
03384         }
03385     }
03386 
03387     // Read all entries in source directory
03388     auto_ptr<TEntries> contents(src.GetEntriesPtr("*", fIgnoreRecursive));
03389 
03390     // And copy each of them to target directory
03391     ITERATE(TEntries, e, *contents.get()) {
03392         CDirEntry& entry = **e;
03393         if ( !F_ISSET(flags, fCF_Recursive)  &&
03394              entry.IsDir(follow ? eFollowLinks : eIgnoreLinks)) {
03395             continue;
03396         }
03397         // Copy entry
03398         if ( !entry.CopyToDir(dst.GetPath(), flags, buf_size) ) {
03399             LOG_ERROR_AND_RETURN("CDir::Copy():"
03400                                  " Cannot copy " << entry.GetPath()
03401                                  << " to directory " << dst.GetPath());
03402         }
03403     }
03404 
03405     // Preserve attributes
03406     if ( flags & fCF_PreserveAll ) {
03407         if ( !s_CopyAttrs(src.GetPath().c_str(),
03408                           dst.GetPath().c_str(), eDir, flags) ) {
03409             return false;
03410         }
03411     } else {
03412         if ( !dst.SetMode(fDefault, fDefault, fDefault) ) {
03413             return false;
03414         }
03415     }
03416     return true;
03417 }
03418 
03419 
03420 bool CDir::Remove(EDirRemoveMode mode) const
03421 {
03422     // Remove directory as empty
03423     if ( mode == eOnlyEmpty ) {
03424         if ( rmdir(GetPath().c_str()) != 0 ) {
03425             LOG_ERROR_AND_RETURN_ERRNO("CDir::Remove():"
03426                                        " Cannot remove (by implication empty)"
03427                                        " directory " << GetPath());
03428         }
03429         return true;
03430     }
03431     // Read all entries in directory
03432     auto_ptr<TEntries> contents(GetEntriesPtr());
03433 
03434     // Remove each entry
03435     ITERATE(TEntries, entry, *contents.get()) {
03436         string name = (*entry)->GetName();
03437         if ( name == "."  ||  name == ".."  ||  
03438              name == string(1, GetPathSeparator()) ) {
03439             continue;
03440         }
03441         // Get entry item with full pathname
03442         CDirEntry item(GetPath() + GetPathSeparator() + name);
03443 
03444         if (mode == eRecursive || mode == eRecursiveIgnoreMissing) {
03445             if (!item.Remove(mode)) {
03446                 return false;
03447             }
03448         } else if ( item.IsDir(eIgnoreLinks) ) {
03449             // Empty subdirectory is essentially a file
03450             if ( mode != eTopDirOnly ) {
03451                 item.Remove(eOnlyEmpty);
03452             }
03453             continue;
03454         } else if ( !item.Remove() ) {
03455             return false;
03456         }
03457     }
03458 
03459     // Remove main directory
03460     if ( rmdir(GetPath().c_str()) != 0 ) {
03461         LOG_ERROR_AND_RETURN_ERRNO("CDir::Remove():"
03462                                    " Cannot remove directory " << GetPath());
03463     }
03464     return true;
03465 }
03466 
03467 
03468 //////////////////////////////////////////////////////////////////////////////
03469 //
03470 // CSymLink
03471 //
03472 
03473 CSymLink::~CSymLink(void)
03474 { 
03475     return;
03476 }
03477 
03478 
03479 bool CSymLink::Create(const string& path) const
03480 {
03481 #if defined(NCBI_OS_UNIX)
03482     char buf[PATH_MAX + 1];
03483     int len = (int) readlink(GetPath().c_str(), buf, sizeof(buf) - 1);
03484     if (len >= 0) {
03485         buf[len] = '\0';
03486         if (strcmp(buf, path.c_str()) == 0) {
03487             return true;
03488         }
03489     }
03490     // Leave it to the kernel to decide whether the symlink can be recreated
03491     return symlink(path.c_str(), GetPath().c_str()) != 0 ? false : true;
03492 #else
03493     LOG_ERROR_AND_RETURN("CSymLink::Create():"
03494                          " Symbolic links not supported on this platform: "
03495                          << path);
03496 #endif
03497 }
03498 
03499 
03500 bool CSymLink::Copy(const string& new_path, TCopyFlags flags, size_t buf_size) const
03501 {
03502 #if defined(NCBI_OS_UNIX)
03503 
03504     // Dereference link if specified
03505     if ( F_ISSET(flags, fCF_FollowLinks) ) {
03506         switch ( GetType(eFollowLinks) ) {
03507             case eFile:
03508                 return CFile(*this).Copy(new_path, flags, buf_size);
03509             case eDir:
03510                 return CDir(*this).Copy(new_path, flags, buf_size);
03511             case eLink:
03512                 return CSymLink(*this).Copy(new_path, flags, buf_size);
03513             default:
03514                 return CDirEntry(*this).Copy(new_path, flags, buf_size);
03515         }
03516         // not reached
03517     }
03518 
03519     // The source link must exists
03520     EType src_type = GetType(eIgnoreLinks);
03521     if ( src_type == eUnknown )  {
03522         LOG_ERROR_AND_RETURN("CSymLink::Copy():"
03523                              " Unknown entry type " << GetPath());
03524     }
03525     CSymLink dst(new_path);
03526     EType dst_type   = dst.GetType(eIgnoreLinks);
03527     bool  dst_exists = (dst_type != eUnknown);
03528 
03529     // If destination exists...
03530     if ( dst_exists ) {
03531         // Check on copying link into yourself.
03532         if ( IsIdentical(dst.GetPath()) ) {
03533             LOG_ERROR_AND_RETURN("CSymLink::Copy():"
03534                                  " Source and destination are the same: "
03535                                  << GetPath());
03536         }
03537         // Can copy entries with different types?
03538         if ( F_ISSET(flags, fCF_EqualTypes)  &&  (src_type != dst_type) ) {
03539             LOG_ERROR_AND_RETURN("CSymLink::Copy():"
03540                                  " Cannot copy entries with different types: "
03541                                  << GetPath());
03542         }
03543         // Can overwrite entry?
03544         if ( !F_ISSET(flags, fCF_Overwrite) ) {
03545             LOG_ERROR_AND_RETURN("CSymLink::Copy():"
03546                                  " Destination already exists: "
03547                                  << dst.GetPath());
03548         }
03549         // Copy only if destination is older
03550         if ( F_ISSET(flags, fCF_Update)  &&  !IsNewer(dst.GetPath(), 0)) {
03551             return true;
03552         }
03553         // Backup destination entry first
03554         if ( F_ISSET(flags, fCF_Backup) ) {
03555             // Use a new CDirEntry object for 'dst', because its path
03556             // will be changed after backup
03557             CDirEntry dst_tmp(dst);
03558             if ( !dst_tmp.Backup(GetBackupSuffix(), eBackup_Rename) ) {
03559                 LOG_ERROR_AND_RETURN("CSymLink::Copy():"
03560                                      " Cannot backup destination: "
03561                                      << dst.GetPath());
03562             }
03563         }
03564         // Overwrite destination entry
03565         if ( F_ISSET(flags, fCF_Overwrite) ) {
03566             dst.Remove();
03567         } 
03568     }
03569 
03570     // Copy symbolic link (create new one)
03571     char buf[PATH_MAX+1];
03572     int  len = (int)readlink(GetPath().c_str(), buf, sizeof(buf)-1);
03573     if ( len < 1 ) {
03574         LOG_ERROR_AND_RETURN("CSymLink::Copy():"
03575                              " Cannot create new symbolic link to "
03576                              << GetPath());
03577     }
03578     buf[len] = '\0';
03579     if ( symlink(buf, new_path.c_str()) ) {
03580         LOG_ERROR_AND_RETURN_ERRNO("CSymLink::Copy():"
03581                                    " Cannot create new symbolic link to "
03582                                    << GetPath());
03583     }
03584 
03585     // Preserve attributes
03586     if ( flags & fCF_PreserveAll ) {
03587         if (!s_CopyAttrs(GetPath().c_str(), new_path.c_str(), eLink, flags)) {
03588             return false;
03589         }
03590     }
03591     return true;
03592 #else
03593     return CParent::Copy(new_path, flags, buf_size);
03594 #endif
03595 }
03596 
03597 
03598 //////////////////////////////////////////////////////////////////////////////
03599 //
03600 // CFileUtil
03601 //
03602 
03603 /// Flags to get information about file system.
03604 /// Each flag corresponds to one or some fields in
03605 /// the CFileUtil::SFileSystemInfo structure.
03606 enum EFileSystemInfo {
03607     fFSI_Type        = (1<<1),    ///< fs_type
03608     fFSI_DiskSpace   = (1<<2),    ///< total_space, free_space
03609     fFSI_BlockSize   = (1<<3),    ///< block_size
03610     fFSI_FileNameMax = (1<<4),    ///< filename_max
03611     fFSI_All         = 0xFF       ///< get all possible information
03612 };
03613 typedef int TFileSystemInfo;      ///< Binary OR of "EFileSystemInfo"
03614 
03615 // File system identification strings
03616 struct SFileSystem {
03617     const char*                name;
03618     CFileUtil::EFileSystemType type;
03619 };
03620 
03621 // File system identification table
03622 static const SFileSystem s_FileSystem[] = {
03623     { "ADFS",    CFileUtil::eADFS    },
03624     { "ADVFS",   CFileUtil::eAdvFS   },
03625     { "AFFS",    CFileUtil::eAFFS    },
03626     { "AUTOFS",  CFileUtil::eAUTOFS  },
03627     { "CACHEFS", CFileUtil::eCacheFS },
03628     { "CD9669",  CFileUtil::eCDFS    },
03629     { "CDFS",    CFileUtil::eCDFS    },
03630     { "DEVFS",   CFileUtil::eDEVFS   },
03631     { "DFS",     CFileUtil::eDFS     },
03632     { "DOS",     CFileUtil::eFAT     },
03633     { "EXT",     CFileUtil::eExt     },
03634     { "EXT2",    CFileUtil::eExt2    },
03635     { "EXT3",    CFileUtil::eExt3    },
03636     { "FAT",     CFileUtil::eFAT     },
03637     { "FAT32",   CFileUtil::eFAT32   },
03638     { "FDFS",    CFileUtil::eFDFS    },
03639     { "FFM",     CFileUtil::eFFM     },
03640     { "FFS",     CFileUtil::eFFS     },
03641     { "HFS",     CFileUtil::eHFS     },
03642     { "HSFS",    CFileUtil::eHSFS    },
03643     { "HPFS",    CFileUtil::eHPFS    },
03644     { "JFS",     CFileUtil::eJFS     },
03645     { "LOFS",    CFileUtil::eLOFS    },
03646     { "MFS",     CFileUtil::eMFS     },
03647     { "MSFS",    CFileUtil::eMSFS    },
03648     { "NFS",     CFileUtil::eNFS     },
03649     { "NFS2",    CFileUtil::eNFS     },
03650     { "NFSV2",   CFileUtil::eNFS     },
03651     { "NFS3",    CFileUtil::eNFS     },
03652     { "NFSV3",   CFileUtil::eNFS     },
03653     { "NFS4",    CFileUtil::eNFS     },
03654     { "NFSV4",   CFileUtil::eNFS     },
03655     { "NTFS",    CFileUtil::eNTFS    },
03656     { "PCFS",    CFileUtil::eFAT     },
03657     { "PROC",    CFileUtil::ePROC    },
03658     { "PROCFS",  CFileUtil::ePROC    },
03659     { "RFS",     CFileUtil::eRFS     },
03660     { "SMBFS",   CFileUtil::eSMBFS   },
03661     { "SPECFS",  CFileUtil::eSPECFS  },
03662     { "TMP",     CFileUtil::eTMPFS   },
03663     { "UFS",     CFileUtil::eUFS     },
03664     { "VXFS",    CFileUtil::eVxFS    },
03665     { "XFS",     CFileUtil::eXFS     }
03666 };
03667 
03668 
03669 // Macros to get filesytem status information
03670 
03671 #define GET_STATVFS_INFO                                       \
03672     struct statvfs st;                                         \
03673     memset(&st, 0, sizeof(st));                                \
03674     if (statvfs(path.c_str(), &st) != 0) {                     \
03675         NCBI_THROW(CFileErrnoException, eFileSystemInfo, msg); \
03676     }                                                          \
03677     if (st.f_frsize) {                                         \
03678         info->free_space = (Uint8)st.f_frsize * st.f_bavail;   \
03679         info->block_size = (unsigned long)st.f_frsize;         \
03680     } else {                                                   \
03681         info->free_space = (Uint8)st.f_bsize * st.f_bavail;    \
03682         info->block_size = (unsigned long)st.f_bsize;          \
03683     }                                                          \
03684     info->total_space  = (Uint8)st.f_bsize * st.f_blocks
03685 
03686 
03687 #define GET_STATFS_INFO                                        \
03688     struct statfs st;                                          \
03689     memset(&st, 0, sizeof(st));                                \
03690     if (statfs(path.c_str(), &st) != 0) {                      \
03691         NCBI_THROW(CFileErrnoException, eFileSystemInfo, msg); \
03692     }                                                          \
03693     info->free_space   = (Uint8)st.f_bsize * st.f_bavail;      \
03694     info->total_space  = (Uint8)st.f_bsize * st.f_blocks;      \
03695     info->block_size   = (unsigned long)st.f_bsize
03696 
03697 
03698 void s_GetFileSystemInfo(const string&               path,
03699                          CFileUtil::SFileSystemInfo* info,
03700                          TFileSystemInfo             flags)
03701 {
03702     if ( !info ) {
03703         NCBI_THROW(CCoreException, eInvalidArg,
03704                    "s_GetFileSystemInfo(path, NULL) is not allowed");
03705     }
03706     memset(info, 0, sizeof(*info));
03707     string msg = string("Cannot get system information for ") + path;
03708     char* fs_name_ptr = 0;
03709 
03710 #if defined(NCBI_OS_MSWIN)
03711     // Try to get a root disk directory from given path
03712     string xpath = path;
03713     // Not UNC path
03714     if ( path[0] != '\\'  ||  path[1] != '\\' ) {
03715         if ( !isalpha((unsigned char)path[0]) || path[1] != DISK_SEPARATOR ) {
03716             // absolute or relative path without disk name -- current disk
03717             // dir entry should exists
03718             if ( CDirEntry(path).Exists() ) {
03719                 xpath = CDir::GetCwd();
03720             }
03721         }
03722         // Get disk root directory name from the path
03723         xpath[2] = '\\';
03724         xpath.resize(3);
03725     }
03726 
03727     // Get volume information
03728     char  fs_name[MAX_PATH+1];
03729     if (flags & (fFSI_Type | fFSI_FileNameMax))  {
03730         DWORD filename_max;
03731         DWORD fs_flags;
03732 
03733         if ( !::GetVolumeInformation(xpath.c_str(),
03734                                     NULL, 0, // Name of the specified volume
03735                                     NULL,    // Volume serial number
03736                                     &filename_max,
03737                                     &fs_flags,
03738                                     fs_name, sizeof(fs_name)) ) {
03739             NCBI_THROW(CFileErrnoException, eFileSystemInfo, msg);
03740         }
03741         info->filename_max = filename_max;
03742         fs_name_ptr = fs_name;
03743     }
03744         
03745     // Get disk spaces
03746     if (flags & fFSI_DiskSpace) {
03747         if ( !::GetDiskFreeSpaceEx(xpath.c_str(),
03748                                 (PULARGE_INTEGER)&info->free_space,
03749                                 (PULARGE_INTEGER)&info->total_space, 0) ) {
03750             NCBI_THROW(CFileErrnoException, eFileSystemInfo, msg);
03751         }
03752     }
03753 
03754     // Get volume cluster size
03755     if (flags & fFSI_BlockSize) {
03756         DWORD dwSectPerClust; 
03757         DWORD dwBytesPerSect;
03758         if ( !::GetDiskFreeSpace(xpath.c_str(),
03759                                  &dwSectPerClust, &dwBytesPerSect,
03760                                  NULL, NULL) ) {
03761             NCBI_THROW(CFileErrnoException, eFileSystemInfo, msg);
03762         }
03763         info->block_size = dwBytesPerSect * dwSectPerClust;
03764     }
03765 
03766 #else // defined(NCBI_OS_MSWIN)
03767 
03768 #  ifdef _PC_NAME_MAX
03769     info->filename_max = pathconf(path.c_str(), _PC_NAME_MAX);
03770 #  else
03771 #    define NEED_NAME_MAX
03772 #  endif
03773 
03774 #  if defined(NCBI_OS_LINUX)  &&  defined(HAVE_STATFS)
03775     
03776     GET_STATFS_INFO;
03777     if (flags & fFSI_Type) {
03778         switch (st.f_type) {
03779             case 0xADF5:      info->fs_type = CFileUtil::eADFS;     break;
03780             case 0xADFF:      info->fs_type = CFileUtil::eFFS;      break;
03781             case 0x012FF7B9:  info->fs_type = CFileUtil::eAFS;      break;
03782             case 0x0187:      info->fs_type = CFileUtil::eAUTOFS;   break;
03783             case 0x1BADFACE:  info->fs_type = CFileUtil::eBFS;      break;
03784             case 0xFF534D42:  info->fs_type = CFileUtil::eCIFS;     break;
03785             case 0x73757245:  info->fs_type = CFileUtil::eCODA;     break;
03786             case 0x012FF7B7:  info->fs_type = CFileUtil::eCOH;      break;
03787             case 0x28CD3D45:  info->fs_type = CFileUtil::eCRAMFS;   break;
03788             case 0x1373:      info->fs_type = CFileUtil::eDEVFS;    break;
03789             case 0x137D:      info->fs_type = CFileUtil::eExt;      break;
03790             case 0xEF51:
03791             case 0xEF53:      info->fs_type = CFileUtil::eExt2;     break;
03792             case 0x4244:      info->fs_type = CFileUtil::eHFS;      break;
03793             case 0xF995E849:  info->fs_type = CFileUtil::eHPFS;     break;
03794             case 0x4004:
03795             case 0x4000:
03796             case 0x9660:      info->fs_type = CFileUtil::eCDFS;     break;
03797             case 0x3153464A:  info->fs_type = CFileUtil::eJFS;      break;
03798             case 0x07C0:      info->fs_type = CFileUtil::eJFFS;     break;
03799             case 0x72B6:      info->fs_type = CFileUtil::eJFFS2;    break;
03800             case 0x137F:
03801             case 0x138F:      info->fs_type = CFileUtil::eMinix;    break;
03802             case 0x2468:
03803             case 0x2478:      info->fs_type = CFileUtil::eMinix2;   break;
03804             case 0x4d44:      info->fs_type = CFileUtil::eFAT;      break;
03805             case 0x564C:      info->fs_type = CFileUtil::eNCPFS;    break;
03806             case 0x6969:      info->fs_type = CFileUtil::eNFS;      break;
03807             case 0x5346544E:  info->fs_type = CFileUtil::eNTFS;     break;
03808             case 0x9fA1:      info->fs_type = CFileUtil::eOPENPROM; break;
03809             case 0x9fA0:      info->fs_type = CFileUtil::ePROC;     break;
03810             case 0x002F:      info->fs_type = CFileUtil::eQNX4;     break;
03811             case 0x7275:      info->fs_type = CFileUtil::eROMFS;    break;
03812             case 0x517B:      info->fs_type = CFileUtil::eSMBFS;    break;
03813             case 0x62656572:  info->fs_type = CFileUtil::eSYSFS;    break;
03814             case 0x012FF7B6:  info->fs_type = CFileUtil::eSYSV2;    break;
03815             case 0x012FF7B5:  info->fs_type = CFileUtil::eSYSV4;    break;
03816             case 0x01021994:  info->fs_type = CFileUtil::eTMPFS;    break;
03817             case 0x15013346:  info->fs_type = CFileUtil::eUDF;      break;
03818             case 0x00011954:  info->fs_type = CFileUtil::eUFS;      break;
03819             case 0x9fA2:      info->fs_type = CFileUtil::eUSBDEVICE;break;
03820             case 0x012FF7B8:  info->fs_type = CFileUtil::eV7;       break;
03821             case 0xa501FCF5:  info->fs_type = CFileUtil::eVxFS;     break;
03822             case 0x565a4653:  info->fs_type = CFileUtil::eVZFS;     break;
03823             case 0x012FF7B4:  info->fs_type = CFileUtil::eXENIX;    break;
03824             case 0x58465342:  info->fs_type = CFileUtil::eXFS;      break;
03825             case 0x012FD16D:  info->fs_type = CFileUtil::eXIAFS;    break;
03826             default:          info->fs_type = CFileUtil::eUnknown;  break;
03827         }
03828     }
03829 #ifdef NEED_NAME_MAX
03830     info->filename_max = (unsigned long)st.f_namelen;
03831 #endif
03832 
03833 #  elif (defined(NCBI_OS_SOLARIS) ||  defined(NCBI_OS_IRIX)  ||  \
03834          defined(NCBI_OS_OSF1)) &&  defined(HAVE_STATVFS)
03835 
03836     GET_STATVFS_INFO;
03837 #ifdef NEED_NAME_MAX
03838     info->filename_max = (unsigned long)st.f_namemax;
03839 #endif
03840     fs_name_ptr = st.f_basetype;
03841 
03842 #  elif (defined(NCBI_OS_BSD) || defined(NCBI_OS_DARWIN))  && \
03843          defined(HAVE_STATFS)
03844 
03845     GET_STATFS_INFO;
03846 #ifdef NEED_NAME_MAX
03847     info->filename_max = (unsigned long)st.f_namelen;
03848 #endif
03849     fs_name_ptr = st.f_fstypename;
03850 
03851 #  elif defined(NCBI_OS_OSF1)  &&  defined(HAVE_STATVFS)
03852 
03853     GET_STATVFS_INFO;
03854 #ifdef NEED_NAME_MAX
03855     info->filename_max = (unsigned long)st.f_namelen;
03856 #endif
03857     fs_name_ptr = st.f_fstypename;
03858 
03859 #  else
03860      // Unknown UNIX OS
03861 #    if defined(HAVE_STATVFS)
03862         GET_STATVFS_INFO;
03863 #    elif defined(HAVE_STATFS)
03864         GET_STATFS_INFO;
03865 #    endif
03866 #  endif
03867 #endif
03868 
03869     // Try to define file system type by name
03870     if ((flags & fFSI_Type)  &&  fs_name_ptr) {
03871         for (size_t i=0; 
03872              i < sizeof(s_FileSystem)/sizeof(s_FileSystem[0]); i++) {
03873             if ( NStr::EqualNocase(fs_name_ptr, s_FileSystem[i].name) ) {
03874                 info->fs_type = s_FileSystem[i].type;
03875                 break;
03876             }
03877         }
03878     }
03879 }
03880 
03881 
03882 void CFileUtil::GetFileSystemInfo(const string& path,
03883                                   CFileUtil::SFileSystemInfo* info)
03884 {
03885     s_GetFileSystemInfo(path, info, fFSI_All);
03886 }
03887 
03888 
03889 Uint8 CFileUtil::GetFreeDiskSpace(const string& path)
03890 {
03891     SFileSystemInfo info;
03892     s_GetFileSystemInfo(path, &info, fFSI_DiskSpace);
03893     return info.free_space;
03894 }
03895 
03896 
03897 Uint8 CFileUtil::GetTotalDiskSpace(const string& path)
03898 {
03899     SFileSystemInfo info;
03900     s_GetFileSystemInfo(path, &info, fFSI_DiskSpace);
03901     return info.total_space;
03902 }
03903 
03904 
03905 
03906 //////////////////////////////////////////////////////////////////////////////
03907 //
03908 // CFileDeleteList / CFileDeleteAtExit
03909 //
03910 
03911 CFileDeleteList::~CFileDeleteList()
03912 {
03913     ITERATE (TNames, name, m_Names) {
03914         CDirEntry entry(*name);
03915         if ( entry.IsDir()) {
03916             CDir(*name).Remove(CDir::eRecursiveIgnoreMissing);
03917         } else {
03918             entry.Remove();
03919         }
03920     }
03921 }
03922 
03923 
03924 void CFileDeleteAtExit::Add(const string& entryname)
03925 {
03926     s_DeleteAtExitFileList->Add(entryname);
03927 }
03928 
03929 const CFileDeleteList& CFileDeleteAtExit::GetDeleteList(void)
03930 {
03931     return *s_DeleteAtExitFileList;
03932 }
03933 
03934 void CFileDeleteAtExit::SetDeleteList(CFileDeleteList& list)
03935 {
03936     *s_DeleteAtExitFileList = list;
03937 }
03938 
03939 
03940 //////////////////////////////////////////////////////////////////////////////
03941 //
03942 // CTmpFile
03943 //
03944 
03945 
03946 CTmpFile::CTmpFile(ERemoveMode remove_file)
03947 {
03948     m_FileName = CFile::GetTmpName();
03949     if ( m_FileName.empty() ) {
03950         NCBI_THROW(CFileException, eTmpFile, 
03951                    "Cannot generate temporary file name");
03952     }
03953     m_RemoveOnDestruction = remove_file;
03954 }
03955 
03956 CTmpFile::CTmpFile(const string& file_name, ERemoveMode remove_file)
03957     : m_FileName(file_name), 
03958       m_RemoveOnDestruction(remove_file)
03959 {
03960     return;
03961 }
03962 
03963 CTmpFile::~CTmpFile()
03964 {
03965     // First, close and delete created streams.
03966     m_InFile.reset();
03967     m_OutFile.reset();
03968 
03969     // Remove file if specified
03970     if (m_RemoveOnDestruction == eRemove) {
03971         unlink(m_FileName.c_str());
03972     }
03973 }
03974 
03975     enum EIfExists {
03976         /// You can make call of AsInputFile/AsOutputFile only once,
03977         /// on each following call throws CFileException exception.
03978         eIfExists_Throw,
03979         /// Delete previous stream and return reference to new object.
03980         eIfExists_Reset,
03981         /// Return reference to current stream, or new if this is first call.
03982         eIfExists_ReturnCurrent
03983     };
03984 
03985     // CTmpFile
03986 
03987 const string& CTmpFile::GetFileName(void) const
03988 {
03989     return m_FileName;
03990 }
03991 
03992 
03993 CNcbiIstream& CTmpFile::AsInputFile(EIfExists if_exists,
03994                                     IOS_BASE::openmode mode)
03995 {
03996     if ( m_InFile.get() ) {
03997         switch (if_exists) {
03998         case eIfExists_Throw:
03999             NCBI_THROW(CFileException, eTmpFile, 
04000                        "AsInputFile() is already called");
04001             /*NOTREACHED*/
04002             break;
04003         case eIfExists_Reset:
04004             // see below
04005             break;
04006         case eIfExists_ReturnCurrent:
04007             return *m_InFile;
04008         }
04009     }
04010     mode |= IOS_BASE::in;
04011     m_InFile.reset(new CNcbiIfstream(m_FileName.c_str()));
04012     return *m_InFile;
04013 }
04014 
04015 
04016 CNcbiOstream& CTmpFile::AsOutputFile(EIfExists if_exists,
04017                                      IOS_BASE::openmode mode)
04018 {
04019     if ( m_OutFile.get() ) {
04020         switch (if_exists) {
04021         case eIfExists_Throw:
04022             NCBI_THROW(CFileException, eTmpFile, 
04023                        "AsOutputFile() is already called");
04024             /*NOTREACHED*/
04025             break;
04026         case eIfExists_Reset:
04027             // see below
04028             break;
04029         case eIfExists_ReturnCurrent:
04030             return *m_OutFile;
04031         }
04032     }
04033     mode |= IOS_BASE::out;
04034     m_OutFile.reset(new CNcbiOfstream(m_FileName.c_str()));
04035     return *m_OutFile;
04036 }
04037 
04038 
04039 //////////////////////////////////////////////////////////////////////////////
04040 //
04041 // CMemoryFile
04042 //
04043 
04044 // Cached system's memory virtual page size.
04045 static unsigned long s_VirtualMemoryPageSize = 0;  
04046 
04047 
04048 // Platform-dependent memory file handle definition
04049 struct SMemoryFileHandle {
04050 #if defined(NCBI_OS_MSWIN)
04051     HANDLE  hMap;   // File-mapping handle (see ::[Open/Create]FileMapping())
04052 #else /* UNIX */
04053     int     hMap;   // File handle
04054 #endif
04055     string  sFileName;
04056 };
04057 
04058 // Platform-dependent memory file attributes
04059 struct SMemoryFileAttrs {
04060 #if defined(NCBI_OS_MSWIN)
04061     DWORD map_protect;
04062     DWORD map_access;
04063     DWORD file_share;
04064     DWORD file_access;
04065 #else
04066     int   map_protect;
04067     int   map_access;
04068     int   file_access;
04069 #endif
04070 };
04071 
04072 
04073 // Translate memory mapping attributes into OS specific flags.
04074 static SMemoryFileAttrs*
04075 s_TranslateAttrs(CMemoryFile_Base::EMemMapProtect protect_attr, 
04076                  CMemoryFile_Base::EMemMapShare   share_attr)
04077 {
04078     SMemoryFileAttrs* attrs = new SMemoryFileAttrs();
04079     memset(attrs, 0, sizeof(SMemoryFileAttrs));
04080 
04081 #if defined(NCBI_OS_MSWIN)
04082 
04083     switch (protect_attr) {
04084         case CMemoryFile_Base::eMMP_Read:
04085             attrs->map_access  = FILE_MAP_READ;
04086             // Next two attributes can be redefined in the x_Open(),
04087             // which try to open file in the READWRITE mod first.
04088             // This allow do not lock a file/page and write into it from
04089             // somewhere else.
04090             attrs->map_protect = PAGE_READONLY;
04091             attrs->file_access = GENERIC_READ;
04092             break;
04093         case CMemoryFile_Base::eMMP_Write:
04094         case CMemoryFile_Base::eMMP_ReadWrite:
04095             // On MS Windows platform Write & ReadWrite access
04096             // to the mapped memory is equivalent
04097             if  (share_attr == CMemoryFile_Base::eMMS_Shared ) {
04098                 attrs->map_access = FILE_MAP_ALL_ACCESS;
04099             } else {
04100                 attrs->map_access = FILE_MAP_COPY;
04101             }
04102             attrs->map_protect = PAGE_READWRITE;
04103             // So the file also must be open for reading and writing
04104             attrs->file_access = GENERIC_READ | GENERIC_WRITE;
04105             break;
04106         default:
04107             _TROUBLE;
04108     }
04109     if ( share_attr == CMemoryFile_Base::eMMS_Shared ) {
04110         attrs->file_share = FILE_SHARE_READ | FILE_SHARE_WRITE;
04111     } else {
04112         attrs->file_share = FILE_SHARE_READ;
04113     }
04114 
04115 #elif defined(NCBI_OS_UNIX)
04116 
04117     switch (share_attr) {
04118         case CMemoryFile_Base::eMMS_Shared:
04119             attrs->map_access  = MAP_SHARED;
04120             // Read + write except, eMMP_Read mode
04121             attrs->file_access = O_RDWR;
04122             break;
04123         case CMemoryFile_Base::eMMS_Private:
04124             attrs->map_access  = MAP_PRIVATE;
04125             // In the private mode writing to the mapped region
04126             // do not affect the original file, so we can open it
04127             // in the read-only mode.
04128             attrs->file_access = O_RDONLY;
04129             break;
04130         default:
04131             _TROUBLE;
04132     }
04133     switch (protect_attr) {
04134         case CMemoryFile_Base::eMMP_Read:
04135             attrs->map_protect = PROT_READ;
04136             attrs->file_access = O_RDONLY;
04137             break;
04138         case CMemoryFile_Base::eMMP_Write:
04139             attrs->map_protect = PROT_WRITE;
04140             break;
04141         case CMemoryFile_Base::eMMP_ReadWrite:
04142             attrs->map_protect = PROT_READ | PROT_WRITE;
04143             break;
04144         default:
04145             _TROUBLE;
04146     }
04147 
04148 #endif
04149     return attrs;
04150 }
04151 
04152 
04153 CMemoryFile_Base::CMemoryFile_Base(void)
04154 {
04155     // Check if memory-mapping is supported on this platform
04156     if ( !IsSupported() ) {
04157         NCBI_THROW(CFileException, eMemoryMap,
04158                    "Memory-mapping is not supported by the C++ Toolkit"
04159                    " on this platform");
04160     }
04161     if ( !s_VirtualMemoryPageSize ) {
04162         s_VirtualMemoryPageSize = GetVirtualMemoryPageSize();
04163     }
04164 }
04165 
04166 
04167 bool CMemoryFile_Base::IsSupported(void)
04168 {
04169 #if defined(NCBI_OS_MSWIN)  ||  defined(NCBI_OS_UNIX)
04170     return true;
04171 #else
04172     return false;
04173 #endif
04174 }
04175 
04176 
04177 #if !defined(HAVE_MADVISE)
04178 bool CMemoryFile_Base::MemMapAdviseAddr(void*, size_t, EMemMapAdvise) {
04179     return true;
04180 }
04181 #else  /* HAVE_MADVISE */
04182 bool CMemoryFile_Base::MemMapAdviseAddr(void* addr, size_t len,
04183                                         EMemMapAdvise advise)
04184 {
04185     int adv;
04186     if ( !addr || !len ) {
04187         return false;
04188     }
04189     switch (advise) {
04190     case eMMA_Random:
04191         adv = MADV_RANDOM;     break;
04192     case eMMA_Sequential:
04193         adv = MADV_SEQUENTIAL; break;
04194     case eMMA_WillNeed:
04195         adv = MADV_WILLNEED;   break;
04196     case eMMA_DontNeed:
04197         adv = MADV_DONTNEED;   break;
04198     default:
04199         adv = MADV_NORMAL;
04200     }
04201     // Conversion type of "addr" to char* -- Sun Solaris fix
04202     if ( madvise((char*) addr, len, adv) != 0 ) {
04203         LOG_ERROR_AND_RETURN_ERRNO("CMemoryFile_Base::MemMapAdviseAddr():"
04204                                    " madvise() failed");
04205     }
04206     return true;
04207 }
04208 #endif  /* HAVE_MADVISE */
04209 
04210 
04211 #if defined(NCBI_OS_MSWIN)
04212 string s_LastErrorMessage(void)
04213 {
04214     char* ptr = NULL;
04215     FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 
04216                   FORMAT_MESSAGE_FROM_SYSTEM     |
04217                   FORMAT_MESSAGE_MAX_WIDTH_MASK  |
04218                   FORMAT_MESSAGE_IGNORE_INSERTS,
04219                   "%0", GetLastError(), 
04220                   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
04221                   (LPTSTR)&ptr, 0, NULL);
04222     string errmsg = ptr ? ptr : "Unknown reason";
04223     LocalFree(ptr);
04224     return errmsg;
04225 }
04226 #endif 
04227 
04228 CMemoryFileSegment::CMemoryFileSegment(SMemoryFileHandle& handle,
04229                                        SMemoryFileAttrs&  attrs,
04230                                        off_t              offset,
04231                                        size_t             length)
04232     : m_DataPtr(0), m_Offset(offset), m_Length(length),
04233       m_DataPtrReal(0), m_OffsetReal(offset), m_LengthReal(length)
04234 {
04235     if ( m_Offset < 0 ) {
04236         NCBI_THROW(CFileException, eMemoryMap,
04237                    "File offset may not be negative");
04238     }
04239     if ( !m_Length ) {
04240         NCBI_THROW(CFileException, eMemoryMap,
04241                    "File mapping region size must be greater than 0");
04242     }
04243     // Get system's memory allocation granularity.
04244     if ( !s_VirtualMemoryPageSize ) {
04245         NCBI_THROW(CFileException, eMemoryMap,
04246                    "Cannot determine virtual page size");
04247     }
04248     // Adjust mapped length and offset.
04249     if ( m_Offset % s_VirtualMemoryPageSize ) {
04250         m_OffsetReal -= (m_Offset % s_VirtualMemoryPageSize);
04251         m_LengthReal += (m_Offset % s_VirtualMemoryPageSize);
04252     }
04253     // Map file view to memory
04254     string errmsg;
04255 #if defined(NCBI_OS_MSWIN)
04256     DWORD offset_hi  = DWORD(Int8(m_OffsetReal) >> 32);
04257     DWORD offset_low = DWORD(Int8(m_OffsetReal) & 0xFFFFFFFF);
04258     m_DataPtrReal = MapViewOfFile(handle.hMap, attrs.map_access,
04259                                   offset_hi, offset_low, m_LengthReal);
04260     if ( !m_DataPtrReal ) {
04261         errmsg = s_LastErrorMessage();
04262     }
04263 
04264 #elif defined(NCBI_OS_UNIX)
04265     errno = 0;
04266     m_DataPtrReal = mmap(0, m_LengthReal, attrs.map_protect,
04267                          attrs.map_access, handle.hMap, m_OffsetReal);
04268     if ( m_DataPtrReal == MAP_FAILED ) {
04269         m_DataPtrReal = 0;
04270         errmsg = strerror(errno);
04271     }
04272 #endif
04273     if ( !m_DataPtrReal ) {
04274         NCBI_THROW(CFileException, eMemoryMap,
04275                    "Cannot map file '" + 
04276                    handle.sFileName + "' to memory (offset=" +
04277                    NStr::Int8ToString(m_Offset) + ", length=" +
04278                    NStr::Int8ToString(m_Length) + "): " + errmsg);
04279     }
04280     // Calculate user's pointer to data
04281     m_DataPtr = (char*)m_DataPtrReal + (m_Offset - m_OffsetReal);
04282 }
04283 
04284 
04285 CMemoryFileSegment::~CMemoryFileSegment(void)
04286 {
04287     Unmap();
04288 }
04289 
04290 
04291 bool CMemoryFileSegment::Flush(void) const
04292 {
04293     if ( !m_DataPtr ) {
04294         return false;
04295     }
04296     bool status;
04297 #if defined(NCBI_OS_MSWIN)
04298     status = (FlushViewOfFile(m_DataPtrReal, m_LengthReal) != 0);
04299 #elif defined(NCBI_OS_UNIX)
04300     status = (msync((char*)m_DataPtrReal, m_LengthReal, MS_SYNC) == 0);
04301 #endif
04302     if ( !status ) {
04303         LOG_ERROR_AND_RETURN_ERRNO("CMemoryFileSegment::Flush():"
04304                                    " Cannot flush memory segment");
04305     }
04306     return status;
04307 }
04308 
04309 
04310 bool CMemoryFileSegment::Unmap(void)
04311 {
04312     // If file view is not mapped do nothing
04313     if ( !m_DataPtr ) {
04314         return true;
04315     }
04316     bool status;
04317 #if defined(NCBI_OS_MSWIN)
04318     status = (UnmapViewOfFile(m_DataPtrReal) != 0);
04319 #elif defined(NCBI_OS_UNIX)
04320     status = (munmap((char*)m_DataPtrReal, (size_t) m_LengthReal) == 0);
04321 #endif
04322     if ( status ) {
04323         m_DataPtr = 0;
04324     } else {
04325         LOG_ERROR_AND_RETURN_ERRNO("CMemoryFileSegment::Unmap():"
04326                                    " Cannot unmap memory segment");
04327     }
04328     return status;
04329 }
04330 
04331 
04332 void CMemoryFileSegment::x_Verify(void) const
04333 {
04334     if ( m_DataPtr ) {
04335         return;
04336     }
04337     NCBI_THROW(CFileException, eMemoryMap, "File not mapped");
04338 }
04339 
04340 
04341 bool CMemoryFileSegment::MemMapAdvise(EMemMapAdvise advise) const
04342 {
04343     if ( !m_DataPtr ) {
04344         return false;
04345     }
04346     return MemMapAdviseAddr(m_DataPtrReal, m_LengthReal, advise);
04347 }
04348 
04349 
04350 CMemoryFileMap::CMemoryFileMap(const string&  file_name,
04351                                EMemMapProtect protect,
04352                                EMemMapShare   share,
04353                                EOpenMode      mode,
04354                                Uint8          max_file_len)
04355     : m_FileName(file_name), m_Handle(0), m_Attrs(0)
04356 {
04357 #if defined(NCBI_OS_MSWIN)
04358     // Name of a file-mapping object cannot contain '\'
04359     m_FileName = NStr::Replace(m_FileName, "\\", "/");
04360 #endif
04361 
04362     // Translate attributes 
04363     m_Attrs = s_TranslateAttrs(protect, share);
04364 
04365     // Create file if necessary
04366     if ( mode == eCreate ) {
04367         x_Create(max_file_len);
04368     }
04369     // Check file size
04370     Int8 file_size = GetFileSize();
04371     if ( file_size < 0 ) {
04372         if ( m_Attrs ) {
04373             delete m_Attrs;
04374             m_Attrs = 0;
04375         }
04376         NCBI_THROW(CFileException, eMemoryMap,
04377                    "To be memory mapped the file must exist: " + m_FileName);
04378     }
04379     // Extend file size if necessary
04380     if ( mode == eExtend  &&  max_file_len > (Uint8)file_size) {
04381         x_Extend(max_file_len - file_size);
04382         file_size = (Int8)max_file_len;
04383     }
04384 
04385     // Open file
04386     if ( file_size == 0 ) {
04387         // Special case -- file is empty
04388         m_Handle = new SMemoryFileHandle();
04389         m_Handle->hMap = kInvalidHandle;
04390         m_Handle->sFileName = m_FileName;
04391         return;
04392     }
04393     x_Open();
04394 }
04395 
04396 
04397 CMemoryFileMap::~CMemoryFileMap(void)
04398 {
04399     // Unmap used memory and close file
04400     x_Close();
04401     // Clean up allocated memory
04402     if ( m_Attrs ) {
04403         delete m_Attrs;
04404     }
04405 }
04406 
04407 
04408 void* CMemoryFileMap::Map(off_t offset, size_t length)
04409 {
04410     if ( !m_Handle  ||  (m_Handle->hMap == kInvalidHandle) ) {
04411         // Special case.
04412         // Always return 0 if a file is unmapped or have zero length.
04413         return 0;
04414     }
04415     // Map file wholly if the length of the mapped region is not specified
04416     if ( !length ) {
04417         Int8 file_size = GetFileSize() - offset;
04418         if ( (Uint8)file_size > get_limits(length).max() ) {
04419             NCBI_THROW(CFileException, eMemoryMap,
04420                        "File too big for memory mapping "   \
04421                        "(file \"" + m_FileName +"\", "
04422                        "offset=" + NStr::Int8ToString(offset) + ", "    \
04423                        "length=" + NStr::Int8ToString(length) + ")");
04424         } else if ( file_size > 0 ) {
04425             length = (size_t)file_size;
04426         } else {
04427             NCBI_THROW(CFileException, eMemoryMap,
04428                        "Mapping region offset specified beyond file size");
04429         }
04430     }
04431     // Map file segment
04432     CMemoryFileSegment* segment =  
04433         new CMemoryFileSegment(*m_Handle, *m_Attrs, offset, length);
04434     void* ptr = segment->GetPtr();
04435     if ( !ptr ) {
04436         delete segment;
04437         NCBI_THROW(CFileException, eMemoryMap,
04438                    "Cannot map (file \"" + m_FileName +"\", "
04439                    "offset=" + NStr::Int8ToString(offset) + ", "    \
04440                    "length=" + NStr::Int8ToString(length) + ")");
04441     }
04442     m_Segments[ptr] = segment;
04443     return ptr;
04444 }
04445 
04446 
04447 bool CMemoryFileMap::Unmap(void* ptr)
04448 {
04449     // Unmap mapped view of a file
04450     bool status = false;
04451     TSegments::iterator segment = m_Segments.find(ptr);
04452     if ( segment != m_Segments.end() ) {
04453         status = segment->second->Unmap();
04454         if ( status ) {
04455             delete segment->second;
04456             m_Segments.erase(segment);
04457         }
04458     }
04459     if ( !status ) {
04460         LOG_ERROR_AND_RETURN_ERRNO("CMemoryFileMap::Unmap():"
04461                                    " Memory segment not found");
04462     }
04463     return status;
04464 }
04465 
04466 
04467 bool CMemoryFileMap::UnmapAll(void)
04468 {
04469     bool status = true;
04470     void* key_to_delete = 0;
04471     ITERATE(TSegments, it, m_Segments) {
04472         if ( key_to_delete ) {
04473             m_Segments.erase(key_to_delete);
04474         }
04475         bool unmapped = it->second->Unmap();
04476         if ( status ) {
04477             status = unmapped;
04478         }
04479         if ( unmapped ) {
04480             key_to_delete = it->first;
04481             delete it->second;
04482         } else {
04483             key_to_delete = 0;
04484         }
04485     }
04486     if ( key_to_delete ) {
04487         m_Segments.erase(key_to_delete);
04488     }
04489     return status;
04490 }
04491 
04492 
04493 void CMemoryFileMap::x_Open(void)
04494 {
04495     m_Handle = new SMemoryFileHandle();
04496     m_Handle->hMap = kInvalidHandle;
04497     m_Handle->sFileName = m_FileName;
04498 
04499     string errmsg;
04500 
04501     for (;;) { // quasi-TRY block
04502 
04503 #if defined(NCBI_OS_MSWIN)
04504         errmsg = ": ";
04505 
04506         // If failed to attach to an existing file-mapping object then
04507         // create a new one (based on the specified file)
04508         HANDLE hMap = OpenFileMapping(m_Attrs->map_access, false,
04509                                       m_FileName.c_str());
04510         if ( !hMap ) { 
04511 
04512             // NOTE:
04513             //
04514             // First, try to open file/mapping in the READWRITE mode,
04515             // to prevent locking file by OS. If this fails, try to open
04516             // it in the predefined mode (usually READONLY).
04517 
04518             HANDLE hFile;
04519             DWORD x_file_access = GENERIC_READ | GENERIC_WRITE;
04520             DWORD x_map_protect = PAGE_READWRITE;
04521 
04522             hFile = CreateFile(m_FileName.c_str(), x_file_access, 
04523                                m_Attrs->file_share, NULL,
04524                                OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
04525             if ( (hFile == INVALID_HANDLE_VALUE)  &&
04526                  (m_Attrs->file_access != x_file_access) ) {
04527                 hFile = CreateFile(m_FileName.c_str(), m_Attrs->file_access, 
04528                                    m_Attrs->file_share, NULL,
04529                                    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,NULL);
04530                 x_map_protect = m_Attrs->map_protect;
04531             }
04532             if ( hFile == INVALID_HANDLE_VALUE ) {
04533                 errmsg += s_LastErrorMessage();
04534                 break;
04535             }
04536 
04537             // Create mapping
04538 
04539             hMap = CreateFileMapping(hFile, NULL,
04540                                      x_map_protect,
04541                                      0, 0, m_FileName.c_str());
04542             if ( !hMap  &&
04543                  (m_Attrs->map_protect != x_map_protect) ) {
04544                 hMap = CreateFileMapping(hFile, NULL,
04545                                          m_Attrs->map_protect,
04546                                          0, 0, m_FileName.c_str());
04547             }
04548             CloseHandle(hFile);
04549             if ( !hMap ) {
04550                 errmsg += s_LastErrorMessage();
04551                 break;
04552             }
04553         }
04554         m_Handle->hMap = hMap;
04555 
04556 #elif defined(NCBI_OS_UNIX)
04557         // Open file
04558         m_Handle->hMap = open(m_FileName.c_str(), m_Attrs->file_access);
04559         if ( m_Handle->hMap < 0 ) {
04560             break;
04561         }
04562 #endif
04563         // Success
04564         return;
04565     }
04566     // Error: close and cleanup
04567     x_Close();
04568     NCBI_THROW(CFileException, eMemoryMap,
04569                "CMemoryFile: Cannot memory map file \"" + m_FileName + '"');
04570 }
04571 
04572 
04573 void CMemoryFileMap::x_Close()
04574 {
04575     // Unmap all mapped segments with error ignoring
04576     ITERATE(TSegments, it, m_Segments) {
04577         delete it->second;
04578     }
04579     m_Segments.clear();
04580 
04581     // Close handle and cleanup
04582     if ( m_Handle ) {
04583         if ( m_Handle->hMap != kInvalidHandle ) { 
04584 #if defined(NCBI_OS_MSWIN)
04585             CloseHandle(m_Handle->hMap);
04586 #elif defined(NCBI_OS_UNIX)
04587             close(m_Handle->hMap);
04588 #endif
04589         }
04590         delete m_Handle;
04591         m_Handle  = 0;
04592     }
04593 }
04594 
04595 // Write 'length' zero bytes into the file and close file descriptor.
04596 void s_AppendZeros(int fd, Uint8 length)
04597 {
04598     char* buf  = new char[kDefaultBufferSize];
04599     memset(buf, '\0', kDefaultBufferSize);
04600     string errmsg;
04601     do {
04602         int x_written = (int)write(fd, (void*) buf, 
04603             length > kDefaultBufferSize ? kDefaultBufferSize :
04604                                           (unsigned int)length);
04605         if ( x_written < 0 ) {
04606             if (errno != EINTR) {
04607                 errmsg = strerror(errno);
04608                 break;
04609             }
04610             continue;
04611         }
04612         length -= x_written;
04613     }
04614     while (length);
04615 
04616     // Cleanup
04617     delete[] buf;
04618     close(fd);
04619     if ( length ) {
04620         NCBI_THROW(CFileException, eMemoryMap, "CMemoryFileMap:"
04621                    " Cannot extend file size: " + errmsg);
04622     }
04623 
04624 }
04625 
04626 
04627 void CMemoryFileMap::x_Create(Uint8 length)
04628 {
04629     int pmode = S_IREAD;
04630 #if defined(NCBI_OS_MSWIN)
04631     if (m_Attrs->file_access & (GENERIC_READ | GENERIC_WRITE)) 
04632 #elif defined(NCBI_OS_UNIX)
04633     if (m_Attrs->file_access & O_RDWR) 
04634 #endif
04635         pmode |= S_IWRITE;
04636 
04637     // Create new file
04638 #ifdef NCBI_OS_MSWIN
04639     int fd = _creat(m_FileName.c_str(), pmode);
04640 #else
04641     int fd = creat(m_FileName.c_str(), pmode);
04642 #endif
04643     if ( fd < 0 ) {
04644         NCBI_THROW(CFileException, eMemoryMap, "CMemoryFileMap:"
04645                    " Cannot create file \"" + m_FileName + '"');
04646     }
04647     // and fill it with zeros
04648     s_AppendZeros(fd, length);
04649 }
04650 
04651 
04652 void CMemoryFileMap::x_Extend(Uint8 length)
04653 {
04654     // Open file for append
04655 #if defined(NCBI_OS_MSWIN)
04656     int fd = _open(m_FileName.c_str(), O_BINARY | O_APPEND | O_WRONLY, 0);
04657 #else
04658     int fd = open(m_FileName.c_str(), O_APPEND | O_WRONLY, 0);
04659 #endif
04660     if ( fd < 0 ) {
04661         NCBI_THROW(CFileException, eMemoryMap, "CMemoryFileMap:"
04662                    " Cannot open file \"" + m_FileName +
04663                    "\" to change its size");
04664     }
04665     // and extend it with zeros
04666     s_AppendZeros(fd, length);
04667 }
04668 
04669 
04670 CMemoryFileSegment* 
04671 CMemoryFileMap::x_GetMemoryFileSegment(void* ptr) const
04672 {
04673     if ( !m_Handle  &&  (m_Handle->hMap == kInvalidHandle) ) {
04674         NCBI_THROW(CFileException, eMemoryMap, "CMemoryFileMap:"
04675                    " File is not mapped");
04676     }
04677     TSegments::const_iterator segment = m_Segments.find(ptr);
04678     if ( segment == m_Segments.end() ) {
04679         NCBI_THROW(CFileException, eMemoryMap, "CMemoryFileMap:"
04680                    " Cannot find mapped file segment"
04681                    " with specified address");
04682     }
04683     return segment->second;
04684 }
04685 
04686    
04687 CMemoryFile::CMemoryFile(const string&  file_name,
04688                          EMemMapProtect protect,
04689                          EMemMapShare   share,
04690                          off_t          offset,
04691                          size_t         length,
04692                          EOpenMode      mode,
04693                          Uint8          max_file_len)
04694 
04695     : CMemoryFileMap(file_name, protect, share, mode, max_file_len), m_Ptr(0)
04696 {
04697     // Check that file is ready for mapping to memory
04698     if ( !m_Handle  ||  (m_Handle->hMap == kInvalidHandle) ) {
04699         return;
04700     }
04701     Map(offset, length);
04702 }
04703 
04704 
04705 void* CMemoryFile::Map(off_t offset, size_t length)
04706 {
04707     // Unmap if already mapped
04708     if ( m_Ptr ) {
04709         Unmap();
04710     }
04711     m_Ptr = CMemoryFileMap::Map(offset, length);
04712     return m_Ptr;
04713 }
04714 
04715 
04716 bool CMemoryFile::Unmap()
04717 {
04718     if ( !m_Ptr ) {
04719         return true;
04720     }
04721     bool status = CMemoryFileMap::Unmap(m_Ptr);
04722     m_Ptr = 0;
04723     return status;
04724 }
04725 
04726 
04727 void* CMemoryFile::Extend(size_t length)
04728 {
04729     x_Verify();
04730 
04731     // Get current mapped segment
04732     CMemoryFileSegment* segment = x_GetMemoryFileSegment(m_Ptr);
04733     off_t offset = segment->GetOffset();
04734 
04735     // Get file size
04736     Int8 file_size = GetFileSize();
04737 
04738     // Map file wholly if the length of the mapped region is not specified
04739     if ( !length ) {
04740         Int8 fs = file_size - offset;
04741         if ( (Uint8)fs > get_limits(length).max() ) {
04742             NCBI_THROW(CFileException, eMemoryMap,
04743                        "Specified length of the mapping region"
04744                        " is too big"
04745                        " (length=" + NStr::Int8ToString(length) + ')');
04746         } else if ( fs > 0 ) {
04747             length = (size_t)fs;
04748         } else {
04749             NCBI_THROW(CFileException, eMemoryMap,
04750                        "Specified offset of the mapping region"
04751                        " exceeds the file size");
04752         }
04753     }
04754 
04755     // Changing file size is necessary
04756     if (Int8(offset + length) > file_size) {
04757         x_Close();
04758         m_Ptr = 0;
04759         x_Extend(offset + length - file_size);
04760         x_Open();
04761     }
04762     // Remap current region
04763     Map(offset, length);
04764     return GetPtr();
04765 }
04766 
04767 
04768 void CMemoryFile::x_Verify(void) const
04769 {
04770     if ( m_Ptr ) {
04771         return;
04772     }
04773     NCBI_THROW(CFileException, eMemoryMap, "CMemoryFile: File is not mapped");
04774 }
04775 
04776 
04777 
04778 //////////////////////////////////////////////////////////////////////////////
04779 //
04780 // CFileException
04781 //
04782 
04783 const char* CFileException::GetErrCodeString(void) const
04784 {
04785     switch (GetErrCode()) {
04786     case eMemoryMap:    return "eMemoryMap";
04787     case eRelativePath: return "eRelativePath";
04788     case eNotExists:    return "eNotExists";
04789     case eFileIO:       return "eFileIO";
04790     case eTmpFile:      return "eTmpFile";
04791     default:            return CException::GetErrCodeString();
04792     }
04793 }
04794 
04795 const char* CFileErrnoException::GetErrCodeString(void) const
04796 {
04797     switch (GetErrCode()) {
04798     case eFileSystemInfo:  return "eFileSystemInfo";
04799     case eFileLock:        return "eFileLock";
04800     case eFileIO:          return "eFileIO";
04801     default:               return CException::GetErrCodeString();
04802     }
04803 }
04804 
04805 
04806 
04807 //////////////////////////////////////////////////////////////////////////////
04808 //
04809 // Find files
04810 //
04811 
04812 void x_Glob(const string& path,
04813             const list<string>& parts,
04814             list<string>::const_iterator next,
04815             list<string>& result,
04816             TFindFiles flags)
04817 {
04818     vector<string> paths;
04819     paths.push_back(path);
04820     vector<string> masks;
04821     masks.push_back(*next);
04822     bool last = ++next == parts.end();
04823     TFindFiles ff = flags;
04824     if ( !last ) {
04825         ff &= ~(fFF_File | fFF_Recursive);
04826         ff |= fFF_Dir;
04827     }
04828     list<string> found;
04829     FindFiles(found, paths.begin(), paths.end(), masks, ff);
04830     if ( last ) {
04831         result.insert(result.end(), found.begin(), found.end());
04832     }
04833     else {
04834         if ( !found.empty() ) {
04835             ITERATE(list<string>, it, found) {
04836                 x_Glob(CDirEntry::AddTrailingPathSeparator(*it),
04837                     parts, next, result, flags);
04838             }
04839         }
04840         else {
04841             x_Glob(CDirEntry::AddTrailingPathSeparator(path + masks.front()),
04842                 parts, next, result, flags);
04843         }
04844     }
04845 }
04846 
04847 
04848 void FindFiles(const string& pattern,
04849                list<string>& result,
04850                TFindFiles flags)
04851 {
04852     string kDirSep(1, CDirEntry::GetPathSeparator());
04853     string abs_path = CDirEntry::CreateAbsolutePath(pattern);
04854     string search_path = kDirSep;
04855 
04856     list<string> parts;
04857     NStr::Split(abs_path, kDirSep, parts);
04858     if ( parts.empty() ) {
04859         return;
04860     }
04861 
04862 #if defined(DISK_SEPARATOR)
04863     // Network paths on Windows start with double back-slash and
04864     // need special processing.
04865     string kNetSep(2, CDirEntry::GetPathSeparator());
04866     bool is_network = pattern.find(kNetSep) == 0;
04867     if ( is_network ) {
04868         search_path = kNetSep + parts.front() + kDirSep;
04869         parts.erase(parts.begin());
04870     }
04871     else {
04872         string disk;
04873         CDirEntry::SplitPathEx(abs_path, &disk);
04874         if ( disk.empty() ) {
04875             // Disk is missing in the absolute path, add it.
04876             CDirEntry::SplitPathEx(CDir::GetCwd(), &disk);
04877             if ( !disk.empty() ) {
04878                 search_path = disk + kDirSep;
04879             }
04880         }
04881         else {
04882             search_path = disk;
04883             // Disk is present but may be missing dir separator
04884             if (abs_path[disk.size()] == DIR_SEPARATOR) {
04885                 parts.erase(parts.begin()); // Remove disk from parts
04886                 search_path += kDirSep;
04887             }
04888             else {
04889                 // Disk is included in the first part, remove it.
04890                 string temp = parts.front().substr(disk.size());
04891                 parts.erase(parts.begin());
04892                 parts.insert(parts.begin(), temp);
04893             }
04894         }
04895     }
04896 #endif
04897 
04898     x_Glob(search_path, parts, parts.begin(), result, flags);
04899 }
04900 
04901 
04902 
04903 //////////////////////////////////////////////////////////////////////////////
04904 //
04905 // CFileIO
04906 //
04907 
04908 CFileIO::CFileIO(void)
04909     : m_Handle(kInvalidHandle), m_CloseHandle(false)
04910 {
04911     return;
04912 }
04913 
04914 
04915 CFileIO::~CFileIO()
04916 {
04917     if (m_Handle == kInvalidHandle) {
04918         return;
04919     }
04920     Close();
04921 }
04922 
04923 
04924 void CFileIO::Open(const string& filename,
04925                    EOpenMode     open_mode,
04926                    EAccessMode   access_mode,
04927                    EShareMode    share_mode)
04928 {
04929 #if defined(NCBI_OS_MSWIN)
04930 
04931     // Translate parameters
04932     DWORD dwAccessMode, dwShareMode, dwOpenMode;
04933 
04934     switch (open_mode) {
04935         case eCreate:
04936             dwOpenMode = CREATE_ALWAYS;
04937             break;
04938         case eCreateNew:
04939             dwOpenMode = CREATE_NEW;
04940             break;
04941         case eOpen:
04942             dwOpenMode = OPEN_EXISTING;
04943             break;
04944         case eOpenAlways:
04945             dwOpenMode = OPEN_ALWAYS;
04946             break;
04947         case eTruncate:
04948             dwOpenMode = TRUNCATE_EXISTING;
04949             break;
04950         default:
04951             _TROUBLE;
04952     }
04953     switch (access_mode) {
04954         case eRead:
04955             dwAccessMode = GENERIC_READ;
04956             break;
04957         case eWrite:
04958             dwAccessMode = GENERIC_WRITE;
04959             break;
04960         case eReadWrite:
04961             dwAccessMode = GENERIC_READ | GENERIC_WRITE;
04962             break;
04963         default:
04964             _TROUBLE;
04965     };
04966     switch (share_mode) {
04967         case eShareRead:
04968             dwShareMode = FILE_SHARE_READ;
04969             break;
04970         case eShareWrite:
04971             dwShareMode = FILE_SHARE_WRITE;
04972             break;
04973         case eShare:
04974             dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
04975             break;
04976         case eExclusive:
04977             dwShareMode = 0;
04978             break;
04979         default:
04980             _TROUBLE;
04981     }
04982 
04983     m_Handle = CreateFile(filename.c_str(), dwAccessMode,
04984                           dwShareMode, NULL, dwOpenMode,
04985                           FILE_ATTRIBUTE_NORMAL, NULL);
04986 
04987 #elif defined(NCBI_OS_UNIX)
04988 
04989     // Translate parameters
04990 # if defined(O_BINARY)
04991     int flags = O_BINARY;
04992 # else
04993     int flags = 0; 
04994 # endif
04995     mode_t mode = 0;
04996 
04997     switch (open_mode) {
04998         case eCreate:
04999             flags |= (O_CREAT | O_TRUNC);
05000             break;
05001         case eCreateNew:
05002             if ( CFile(filename).Exists() ) {
05003                 NCBI_THROW(CFileException, eFileIO,
05004                            "Open mode is eCreateNew but file already exists: "
05005                            + filename );
05006             }
05007             flags |= O_CREAT;
05008             break;
05009         case eOpen:
05010             // by default
05011             break;
05012         case eOpenAlways:
05013             if ( !CFile(filename).Exists() ) {
05014                 flags |= O_CREAT;
05015             }
05016             break;
05017         case eTruncate:
05018             flags |= O_TRUNC;
05019             break;
05020         default:
05021             _TROUBLE;
05022     }
05023     switch (access_mode) {
05024         case eRead:
05025             flags |= O_RDONLY;
05026             mode  |= S_IREAD;
05027             break;
05028         case eWrite:
05029             flags |= O_WRONLY;
05030             mode  |= S_IWRITE;
05031             break;
05032         case eReadWrite:
05033             flags |= O_RDWR;
05034             mode  |= (S_IREAD | S_IWRITE);
05035             break;
05036         default:
05037             _TROUBLE;
05038     };
05039     // -- Ignore 'share_mode' on UNIX.
05040     share_mode = eShare;
05041 
05042     // Try to open/create file
05043     m_Handle = open(filename.c_str(), flags, mode);
05044 
05045 #endif
05046 
05047     if (m_Handle == kInvalidHandle) {
05048         NCBI_THROW(CFileErrnoException, eFileIO,
05049                    "Cannot open file " + filename);
05050     }
05051     m_CloseHandle = true;
05052 }
05053 
05054 
05055 void CFileIO::Close(void)
05056 {
05057     if (m_CloseHandle) {
05058 #if defined(NCBI_OS_MSWIN)
05059         CloseHandle(m_Handle);
05060 #elif defined(NCBI_OS_UNIX)
05061         close(m_Handle);
05062 #endif
05063     }
05064     m_Handle = kInvalidHandle;
05065     m_CloseHandle = false;
05066 }
05067 
05068 
05069 ssize_t CFileIO::Read(void* buf, size_t count) const
05070 {
05071 #if defined(NCBI_OS_MSWIN)
05072     DWORD n = 0;
05073     if (count > ULONG_MAX) {
05074         count = ULONG_MAX;
05075     }
05076     if ( ::ReadFile(m_Handle, buf, (DWORD)count, &n, NULL) == 0 ) {
05077         return GetLastError() == ERROR_HANDLE_EOF? 0 : -1;
05078     }
05079 #elif defined(NCBI_OS_UNIX)
05080     ssize_t n = 0;
05081     while ((n = read(int(m_Handle), buf, count)) < 0) {
05082         if (errno != EINTR) {
05083             return -1;
05084         }
05085     }
05086 #endif
05087     return n;
05088 }
05089 
05090 
05091 ssize_t CFileIO::Write(const void* buf, size_t count) const
05092 {
05093 #if defined(NCBI_OS_MSWIN)
05094     DWORD n = 0;
05095     if (count > ULONG_MAX) {
05096         count = ULONG_MAX;
05097     }
05098     if ( WriteFile(m_Handle, buf, (DWORD)count, &n, NULL) == 0 ) {
05099         return -1;
05100     }
05101 #elif defined(NCBI_OS_UNIX)
05102     ssize_t n = write(int(m_Handle), buf, count);
05103 #endif
05104     return n;
05105 }
05106 
05107 
05108 void CFileIO::Flush(void) const
05109 {
05110     bool res;
05111 #if defined(NCBI_OS_MSWIN)
05112     res = (FlushFileBuffers(m_Handle) == TRUE);
05113 #elif defined(NCBI_OS_UNIX)
05114     res = (fsync(m_Handle) == 0);
05115 #endif
05116     if ( !res ) {
05117         NCBI_THROW(CFileErrnoException, eFileIO, "Cannot flush");
05118     }
05119 }
05120 
05121 
05122 void CFileIO::SetFileHandle(TFileHandle handle)
05123 {
05124     // Close previous handle if needed
05125     Close();
05126     // Use given handle for all I/O
05127     m_Handle = handle;
05128 }
05129 
05130 
05131 ssize_t CFileIO::GetFilePos(void) const
05132 {
05133 #if defined(NCBI_OS_MSWIN)
05134     LARGE_INTEGER ofs;
05135     LARGE_INTEGER pos;
05136     ofs.QuadPart = 0;
05137     pos.QuadPart = 0;
05138     BOOL res = SetFilePointerEx(m_Handle, ofs, &pos, FILE_CURRENT);
05139     if (res) {
05140         return (ssize_t)pos.QuadPart;
05141     }
05142 #elif defined(NCBI_OS_UNIX)
05143     off_t pos = lseek(m_Handle, 0, SEEK_CUR);
05144     if (pos != -1) {
05145         return (ssize_t)pos;
05146     }
05147 #endif
05148     return -1;
05149 }
05150 
05151 
05152 void CFileIO::SetFilePos(off_t offset, EPositionMoveMethod move_method) const
05153 {
05154 #if defined(NCBI_OS_MSWIN)
05155     DWORD from = 0;
05156     switch (move_method) {
05157         case eBegin:
05158             from = FILE_BEGIN;
05159             break;
05160         case eCurrent:
05161             from = FILE_CURRENT;
05162             break;
05163         case eEnd:
05164             from = FILE_END;
05165             break;
05166         default:
05167             _TROUBLE;
05168     }
05169     LARGE_INTEGER ofs;
05170     ofs.QuadPart = offset;
05171     bool res = (SetFilePointerEx(m_Handle, ofs, NULL, from) == TRUE);
05172 #elif defined(NCBI_OS_UNIX)
05173     int from = 0;
05174     switch (move_method) {
05175         case eBegin:
05176             from = SEEK_SET;
05177             break;
05178         case eCurrent:
05179             from = SEEK_CUR;
05180             break;
05181         case eEnd:
05182             from = SEEK_END;
05183             break;
05184         default:
05185             _TROUBLE;
05186     }
05187     bool res = (lseek(m_Handle, offset, from) != -1);
05188 #endif
05189     if ( !res ) {
05190         NCBI_THROW(CFileErrnoException, eFileIO,
05191                    "SetFilePos() failed"
05192                    " (offset=" + NStr::Int8ToString(offset) +
05193                    ", method=" + NStr::IntToString(move_method) + ')');
05194     }
05195 }
05196 
05197 
05198 void CFileIO::SetFileSize(size_t length, EPositionMoveMethod pos) const
05199 {
05200 #if defined(NCBI_OS_MSWIN)
05201     BOOL res = true;
05202     // Get current position if needed
05203     LARGE_INTEGER ofs;
05204     LARGE_INTEGER saved;
05205     ofs.QuadPart = 0;
05206     saved.QuadPart = 0;
05207     // Save current file position if needed
05208     if (pos == eCurrent) {
05209         res = SetFilePointerEx(m_Handle, ofs, &saved, FILE_CURRENT);
05210     }
05211     if (res) {
05212         // Set file position to specified length (new EOF)
05213         ofs.QuadPart = length;
05214         res = SetFilePointerEx(m_Handle, ofs, NULL, FILE_BEGIN);
05215         // And change file size
05216         if (res) {
05217             res = SetEndOfFile(m_Handle);
05218         }
05219         // Set file pointer if other than eEnd
05220         if (res) {
05221             if (pos == eBegin) {
05222                 // eBegin
05223                 ofs.QuadPart = 0;
05224                 res = SetFilePointerEx(m_Handle, ofs, NULL, FILE_BEGIN);
05225             }
05226             else if (pos == eCurrent) {
05227                 res = SetFilePointerEx(m_Handle, saved, NULL, FILE_BEGIN);
05228             }
05229             // Nothing todo if eEnd, because we already at the EOF position
05230         }
05231     }
05232 #elif defined(NCBI_OS_UNIX)
05233     bool res = (ftruncate(m_Handle, (off_t)length) != -1);
05234     // POSIX ftruncate() doesn't move file pointer
05235     if (res  &&  (pos != eCurrent)) {
05236         SetFilePos(0, pos);
05237     }
05238 #endif
05239     if ( !res ) {
05240         NCBI_THROW(CFileErrnoException, eFileIO,
05241                    "SetFileSize() failed"
05242                    " (length=" + NStr::UInt8ToString(length) + ')');
05243     }
05244 }
05245 
05246 
05247 
05248 //////////////////////////////////////////////////////////////////////////////
05249 //
05250 // CFileReader
05251 //
05252 
05253 CFileReader::CFileReader(const string& filename, EShareMode share_mode)
05254 {
05255     m_File.Open(filename, eOpen, eRead, share_mode);
05256 }
05257 
05258 
05259 CFileReader::CFileReader(TFileHandle handle)
05260 {
05261     m_File.SetFileHandle(handle);
05262     return;
05263 }
05264 
05265 
05266 IReader* CFileReader::New(const string& filename, EShareMode share_mode)
05267 {
05268     if ( filename == "-" ) {
05269 #if defined(NCBI_OS_MSWIN)
05270         TFileHandle handle = GetStdHandle(STD_INPUT_HANDLE);
05271 #elif defined(NCBI_OS_UNIX)
05272         TFileHandle handle = 0;
05273 #endif
05274         return new CFileReader(handle);
05275     }
05276     else {
05277         return new CFileReader(filename, share_mode);
05278     }
05279 }
05280 
05281 
05282 ERW_Result CFileReader::Read(void* buf, size_t count, size_t* bytes_read)
05283 {
05284     if ( bytes_read ) {
05285         *bytes_read = 0;
05286     }
05287     if ( !count ) {
05288         return eRW_Success;
05289     }
05290     ssize_t n = m_File.Read(buf, count);
05291     if ( n == -1 ) {
05292         return eRW_Error;
05293     }
05294     if ( bytes_read ) {
05295         *bytes_read = n;
05296     }
05297     return n? eRW_Success : eRW_Eof;
05298 }
05299 
05300 
05301 ERW_Result CFileReader::PendingCount(size_t* /*count*/)
05302 {
05303     return eRW_NotImplemented;
05304 }
05305 
05306 
05307 //////////////////////////////////////////////////////////////////////////////
05308 //
05309 // CFileWriter
05310 //
05311 
05312 CFileWriter::CFileWriter(const string& filename,
05313                          EOpenMode  open_mode,
05314                          EShareMode share_mode)
05315 {
05316     m_File.Open(filename, open_mode, eWrite, share_mode);
05317 }
05318 
05319 
05320 CFileWriter::CFileWriter(TFileHandle handle)
05321 {
05322     m_File.SetFileHandle(handle);
05323     return;
05324 }
05325 
05326 
05327 IWriter* CFileWriter::New(const string& filename,
05328                           EOpenMode  open_mode,
05329                           EShareMode share_mode)
05330 {
05331     return new CFileWriter(filename, open_mode, share_mode);
05332 }
05333 
05334 
05335 ERW_Result CFileWriter::Write(const void* buf,
05336                               size_t count, size_t* bytes_written)
05337 {
05338     if ( bytes_written ) {
05339         *bytes_written = 0;
05340     }
05341     if ( !count ) {
05342         return eRW_Success;
05343     }
05344     ssize_t n = m_File.Write(buf, count);
05345     if ( n == -1 ) {
05346         return eRW_Error;
05347     }
05348     if ( bytes_written ) {
05349         *bytes_written = n;
05350     }
05351     return n? eRW_Success : eRW_Error;
05352 }
05353 
05354 
05355 ERW_Result CFileWriter::Flush(void)
05356 {
05357     return eRW_Success;
05358 }
05359 
05360 
05361 //////////////////////////////////////////////////////////////////////////////
05362 //
05363 // CFileReaderWriter
05364 //
05365 
05366 CFileReaderWriter::CFileReaderWriter(const string& filename,
05367                                      EOpenMode  open_mode,
05368                                      EShareMode share_mode)
05369 {
05370     m_File.Open(filename, open_mode, eReadWrite, share_mode);
05371 }
05372 
05373 
05374 CFileReaderWriter::CFileReaderWriter(TFileHandle handle)
05375 {
05376     m_File.SetFileHandle(handle);
05377     return;
05378 }
05379 
05380 
05381 IReaderWriter* CFileReaderWriter::New(const string& filename,
05382                                       EOpenMode  open_mode,
05383                                       EShareMode share_mode)
05384 {
05385     return new CFileReaderWriter(filename, open_mode, share_mode);
05386 }
05387 
05388 
05389 ERW_Result CFileReaderWriter::Read(void* buf,
05390                                    size_t count, size_t* bytes_read)
05391 {
05392     if ( bytes_read ) {
05393         *bytes_read = 0;
05394     }
05395     if ( !count ) {
05396         return eRW_Success;
05397     }
05398     ssize_t n = m_File.Read(buf, count);
05399     if ( n == -1 ) {
05400         return eRW_Error;
05401     }
05402     if ( bytes_read ) {
05403         *bytes_read = n;
05404     }
05405     return n? eRW_Success : eRW_Eof;
05406 }
05407 
05408 
05409 ERW_Result CFileReaderWriter::PendingCount(size_t* /*count*/)
05410 {
05411     return eRW_NotImplemented;
05412 }
05413 
05414 
05415 ERW_Result CFileReaderWriter::Write(const void* buf,
05416                                     size_t count, size_t* bytes_written)
05417 {
05418     if ( bytes_written ) {
05419         *bytes_written = 0;
05420     }
05421     if ( !count ) {
05422         return eRW_Success;
05423     }
05424     ssize_t n = m_File.Write(buf, count);
05425     if ( n == -1 ) {
05426         return eRW_Error;
05427     }
05428     if ( bytes_written ) {
05429         *bytes_written = n;
05430     }
05431     return n? eRW_Success : eRW_Error;
05432 }
05433 
05434 
05435 ERW_Result CFileReaderWriter::Flush(void)
05436 {
05437     try {
05438         m_File.Flush();
05439     } catch (CFileException&) { 
05440        return eRW_Error;
05441     }
05442     return eRW_Success;
05443 }
05444 
05445 
05446 
05447 //////////////////////////////////////////////////////////////////////////////
05448 //
05449 // CFileLock
05450 //
05451 
05452 // Clean up an all non-default bits in group if all bits are set
05453 #define F_CLEAN_REDUNDANT(group) \
05454     if (F_ISSET(m_Flags, (group))) \
05455         m_Flags &= ~unsigned((group) & ~unsigned(fDefault))
05456 
05457 // Platform-dependent structure to store file locking information
05458 struct SLock {
05459     SLock(void) {};
05460     SLock(off_t off, size_t len) {
05461         Reset(off, len);
05462     }
05463 #if defined(NCBI_OS_MSWIN)
05464     void Reset(off_t off, size_t len) 
05465     {
05466         // Locking a region that goes beyond the current EOF position
05467         // is not an error.
05468         if (len) {
05469             length_lo = (DWORD)(len & 0xFFFFFFFF);
05470             length_hi = (DWORD)((Int8(len) >> 32) & 0xFFFFFFFF);
05471         } else {
05472             length_lo = 0;
05473             length_hi = 0xFFFFFFFF;
05474         }
05475     };
05476     DWORD offset_lo;
05477     DWORD offset_hi;
05478     DWORD length_lo;
05479     DWORD length_hi;
05480 #elif defined(NCBI_OS_UNIX)
05481     void Reset(off_t off, size_t len) {
05482         offset = off;
05483         length = len;
05484     }
05485     off_t  offset;
05486     size_t length;
05487 #endif
05488 };
05489 
05490 
05491 CFileLock::CFileLock(const string& filename, TFlags flags, EType type,
05492                      off_t offset, size_t length)
05493     : m_Handle(kInvalidHandle), m_CloseHandle(false), m_Flags(flags),
05494       m_IsLocked(false), m_Lock(0)
05495 {
05496     x_Init(filename.c_str(), type, offset, length);
05497 }
05498 
05499 
05500 CFileLock::CFileLock(const char* filename, TFlags flags, EType type,
05501                      off_t offset, size_t length)
05502     : m_Handle(kInvalidHandle), m_CloseHandle(false), m_Flags(flags),
05503       m_IsLocked(false), m_Lock(0)
05504 {
05505     x_Init(filename, type, offset, length);
05506 }
05507 
05508 
05509 CFileLock::CFileLock(TFileHandle handle, TFlags flags, EType type,
05510                      off_t offset, size_t length)
05511     : m_Handle(handle), m_CloseHandle(false), m_Flags(flags),
05512       m_IsLocked(false), m_Lock(0)
05513 {
05514     x_Init(0, type, offset, length);
05515 }
05516 
05517 
05518 void CFileLock::x_Init(const char* filename, EType type, off_t offset, size_t length)
05519 {
05520     // Reset redundant flags
05521     F_CLEAN_REDUNDANT(fLockNow | fLockLater);
05522     F_CLEAN_REDUNDANT(fAutoUnlock | fNoAutoUnlock);
05523 
05524     // Open file
05525     if (filename) {
05526 #if defined(NCBI_OS_MSWIN)
05527         m_Handle = CreateFile(filename, GENERIC_READ,
05528                               FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
05529 #elif defined(NCBI_OS_UNIX)
05530         m_Handle = open(filename, O_RDWR);
05531 #endif
05532     }
05533     if (m_Handle == kInvalidHandle) {
05534         NCBI_THROW(CFileErrnoException, eFileLock,
05535                    "Cannot open file " + string(filename));
05536     }
05537     if (filename) {
05538         m_CloseHandle = true;
05539     }
05540     m_Lock = new SLock;
05541 
05542     // Lock file if necessary
05543     if (F_ISSET(m_Flags, fLockNow)) {
05544          Lock(type, offset, length);
05545     }
05546 }
05547 
05548 
05549 CFileLock::~CFileLock()
05550 {
05551     if (m_Handle == kInvalidHandle) {
05552         return;
05553     }
05554     try {
05555         // Remove lock automaticaly
05556         if (F_ISSET(m_Flags, fAutoUnlock)) {
05557             Unlock();
05558         }
05559     } catch(CException& e) {
05560         NCBI_REPORT_EXCEPTION_X(4,
05561                                 "CFileLock::~CFileLock():"
05562                                 " Cannot unlock", e);
05563     }
05564 
05565     if (m_CloseHandle) {
05566 #if defined(NCBI_OS_MSWIN)
05567         CloseHandle(m_Handle);
05568 #elif defined(NCBI_OS_UNIX)
05569         close(m_Handle);
05570 #endif
05571     }
05572     return;
05573 }
05574 
05575 
05576 void CFileLock::Lock(EType type, off_t offset, size_t length)
05577 {
05578     // Remove previous lock
05579     if (m_IsLocked) {
05580         Unlock();
05581     }
05582     // Set new one
05583     m_Lock->Reset(offset, length);
05584     
05585 #if defined(NCBI_OS_MSWIN)
05586     DWORD flags = LOCKFILE_FAIL_IMMEDIATELY;
05587     if (type == eExclusive) {
05588         flags |= LOCKFILE_EXCLUSIVE_LOCK;
05589     }
05590     OVERLAPPED overlapped;
05591     overlapped.hEvent     = 0;
05592     overlapped.Offset     = m_Lock->offset_lo;
05593     overlapped.OffsetHigh = m_Lock->offset_hi;
05594     bool res = LockFileEx(m_Handle, flags, 0, 
05595                             m_Lock->length_lo, m_Lock->length_hi,
05596                             &overlapped) == TRUE;
05597 #elif defined(NCBI_OS_UNIX)
05598     struct flock fl;
05599     fl.l_type   = (type == eShared) ? F_RDLCK : F_WRLCK;
05600     fl.l_whence = SEEK_SET;
05601     fl.l_start  = m_Lock->offset;
05602     fl.l_len    = m_Lock->length;   // 0 - lock to EOF 
05603     fl.l_pid    = getpid();
05604     
05605     int err;
05606     do {
05607         err = fcntl(m_Handle, F_SETLK, &fl);
05608     } while (err && (errno == EINTR));
05609     bool res = (err == 0);
05610 
05611 #endif
05612     if (!res) {
05613         NCBI_THROW(CFileErrnoException, eFileLock, "CFileLock::Lock():"
05614                    " Cannot lock");
05615     }
05616     m_IsLocked = true;
05617     return;
05618 }
05619 
05620 
05621 void CFileLock::Unlock(void)
05622 {
05623     if (!m_IsLocked) {
05624         return;
05625     }
05626 #if defined(NCBI_OS_MSWIN)
05627     OVERLAPPED overlapped;
05628     overlapped.hEvent     = 0;
05629     overlapped.Offset     = m_Lock->offset_lo;
05630     overlapped.OffsetHigh = m_Lock->offset_hi;
05631     bool res = UnlockFileEx(m_Handle, 0,
05632                             m_Lock->length_lo, m_Lock->length_hi,
05633                             &overlapped) == TRUE;
05634 
05635 #elif defined(NCBI_OS_UNIX)
05636     struct flock fl;
05637     fl.l_type   = F_UNLCK;
05638     fl.l_whence = SEEK_SET;
05639     fl.l_start  = m_Lock->offset;
05640     fl.l_len    = m_Lock->length;
05641     fl.l_pid    = getpid();
05642     
05643     int err;
05644     do {
05645         err = fcntl(m_Handle, F_SETLK, &fl);
05646     } while (err && (errno == EINTR));
05647     bool res = (err == 0);
05648 
05649 #endif
05650     if (!res) {
05651         NCBI_THROW(CFileErrnoException, eFileLock, "CFileLock::Unlock():"
05652                    " Cannot unlock");
05653     }
05654     m_IsLocked = false;
05655     return;
05656 }
05657 
05658 
05659 //////////////////////////////////////////////////////////////////////////////
05660 //
05661 // Misc
05662 //
05663 
05664 void CFileAPI::SetLogging(ESwitch on_off_default)
05665 {
05666     NCBI_PARAM_TYPE(NCBI, FileAPILogging)::SetDefault(
05667         on_off_default != eDefault ?
05668             on_off_default != eOff : DEFAULT_LOGGING_VALUE);
05669 }
05670 
05671 void CFileAPI::SetDeleteReadOnlyFiles(ESwitch on_off_default)
05672 {
05673     NCBI_PARAM_TYPE(NCBI, DeleteReadOnlyFiles)::SetDefault(
05674         on_off_default == eOn);
05675 }
05676 
05677 END_NCBI_SCOPE
05678 
05679 

Generated on Wed Dec 9 04:14:36 2009 for NCBI C++ ToolKit by  doxygen 1.4.6
Modified on Wed Dec 09 08:17:56 2009 by modify_doxy.py rev. 173732