// // File: hzDirectory.cpp // // Legal Notice: This file is part of the HadronZoo C++ Class Library. Copyright 2025 HadronZoo Project (http://www.hadronzoo.com) // // The HadronZoo C++ Class Library is free software: You can redistribute it, and/or modify it under the terms of the GNU Lesser General Public License, as published by the Free // Software Foundation, either version 3 of the License, or any later version. // // The HadronZoo C++ Class Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR // A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License along with the HadronZoo C++ Class Library. If not, see http://www.gnu.org/licenses. //
// // HadronZoo Directory and file management package //
#include <fstream>
#include <dirent.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h>
#include "hzChars.h" #include "hzTextproc.h" #include "hzErrcode.h" #include "hzUnixacc.h" #include "hzDirectory.h" #include "hzTmplList.h" #include "hzProcess.h"
using namespace std ;
/* ** Definitions */
#define PATHSIZE 256
/* ** Variables */
static std::ofstream s_Output ; // Archive file being created static std::ifstream s_Input ; // Archive file being unfolded
static hzList<char*> s_Excludes ; // File endings to exclude
/* ** SECTION 1: hzDirent functions */
void hzDirent::InitStat (const hzString& pardir, const hzString& name, FSTAT& fs) { // Initialize a directory entry from a struct stat instance. This method is best when reading the filesystem directly. Please use InitNorm if populating a // hzDirent instance from a file listing or config file. // // Arguments: 1) dir The host directory // 2) name Directory entry name // 3) fs The supplied struct stat // // Returns: None
m_parent = pardir ; m_Name = name ;
m_Inode = fs.st_ino ; m_Size = fs.st_size ; m_Ctime = fs.st_ctime ; m_Mtime = fs.st_mtime ; m_Mode = fs.st_mode ; m_Owner = fs.st_uid ; m_Group = fs.st_gid ; m_Links = fs.st_nlink ; m_Status = 0 ; }
void hzDirent::InitNorm ( const hzString& pardir, const hzString& name, uint64_t size, uint32_t ino, uint32_t ctime, uint32_t mtime, uint32_t mode, uint32_t uid, uint32_t gid, uint32_t nlink ) { // Initialize a directory entry explicitly. This method is best when populating from a file listing of config file. Please use InitStat when reading from // the file system. // // Arguments: 1) dir The host directory // 2) name Directory entry name // 3) size File size (only applies to files) // 4) ino I-node number // 5) ctime Time created // 6) mtime Time last modified // 7) mode Access permissions // 8) uid Owner's UNIX user id // 9) gid Owner's UNIX group id // 10) nlink Number of links // // Returns: None
//parent = dir ; m_parent = pardir ; //dir->Path() ; m_Name = name ;
m_Inode = ino ; m_Size = size ; m_Ctime = ctime ; m_Mtime = mtime ; m_Mode = mode ; m_Owner = uid ; m_Group = gid ; m_Links = nlink ; m_Status = 0 ; }
const hzString hzDirent::Path (void) const { // If this is a dir return name (the path) or if this is a file return the path of the parent dir
if (ISDIR(m_Mode)) return m_Name ;
return m_parent ; }
hzDirent& hzDirent::operator= (const hzDirent& op) { m_parent = op.m_parent ; m_Name = op.m_Name ; m_Ctime = op.m_Ctime ; m_Mtime = op.m_Mtime ; m_Inode = op.m_Inode ; m_Size = op.m_Size ; m_Mode = op.m_Mode ; m_Owner = op.m_Owner ; m_Group = op.m_Group ; m_Links = op.m_Links ; m_Status = op.m_Status ; return *this ; }
bool hzDirent::operator< (const hzDirent& op) const { // Both are dirs if (ISDIR(m_Mode) && ISDIR(op.m_Mode)) return m_Name < op.m_Name ? true : false ;
// This is dir, op is file. Return true only if this dir is lower than that of the operand. Even if equal // the file has a higher value because it equates to dirname/filename (i.e. is longer) if (ISDIR(m_Mode)) return m_Name < op.Path() ? true : false ;
// This is file, op is dir and can only be less than the op if the parent is less than the operand if (ISDIR(op.m_Mode)) return Path() < op.m_Name ? true : false ;
// Both are files if (Path() < op.Path()) return true ; //if (parent == op.parent && m_Name < op.m_Name) if (m_parent == op.m_parent && m_Name < op.m_Name) return true ; return false ; }
bool hzDirent::operator> (const hzDirent& op) const { // Both are dirs if (ISDIR(m_Mode) && ISDIR(op.m_Mode)) return m_Name > op.m_Name ? true : false ;
// This is dir, op is file. Return true only if this dir is higher than that of the operand. Even if equal // the file has a higher value because it equates to dirname/filename (i.e. is longer) if (ISDIR(m_Mode)) return m_Name > op.Path() ? true : false ;
// This is file, op is dir and can only be less than the op if the parent is less than the operand if (ISDIR(op.m_Mode)) return Path() < op.m_Name ? false : true ;
// Both are files if (Path() > op.Path()) return true ; //if (parent == op.parent && m_Name > op.m_Name) if (m_parent == op.m_parent && m_Name > op.m_Name) return true ; return false ; }
bool hzDirent::operator== (const hzDirent& op) const { // If both are directories they are deemed equal simply if the names match.
if (ISDIR(m_Mode) && ISDIR(op.m_Mode)) return m_Name == op.m_Name ? true : false ;
// Cannot be equal if one is a dir and the other is a file
if (ISDIR(m_Mode)) return false ; if (ISDIR(op.m_Mode)) return false ;
// Case where both are file
if (Path() != op.Path()) return false ; return m_Name == op.m_Name && m_Mtime == op.m_Mtime && m_Size == op.m_Size ? true : false ; }
/* ** SECTION 1: Non member functions */
hzEcode GetCurrDir (hzString& Dir) { // Category: Directory // // Populate a hzString with the name of the current working directory. This function calls the GNU system function get_current_dir_name(). // // Arguments: 1) Dir The hzString reference to populate // // Returns: E_NOTFOUND If the current directory has access issues or has been unlinked // E_OK If the operation was successful
_hzfunc("GetCurrDir") ;
char* cpDir ; // Buffer for current working directory
Dir.Clear() ;
cpDir = get_current_dir_name() ;
if (!cpDir) { if (errno == EACCES) return hzwarn(E_NOTFOUND, "The current working directory has access issues") ; if (errno == ENOENT) return hzwarn(E_NOTFOUND, "The current working directory has been unlinked") ;
return hzwarn(E_NOTFOUND, "Unspecified error") ; }
Dir = cpDir ; free(cpDir) ; return E_OK ; }
hzEcode GetAbsPath (hzString& abs, const char* rel) { // Category: Directory // // Translate the supplied null terminate string, assumed to be an path relative to the current directory, into an absolute path. Places the result into the // supplied string recepticle. // // Note that if the relative path is not supplied the output value will be the name of the current working directory. // // Note also that this function DOES NOT TEST if the resulting path exists or that it can be accessed. // // Arguments: 1) Dir The hzString reference to populate // // Returns: E_NOTFOUND If the current directory has access issues or has been unlinked // E_OK If the operation was successful
_hzfunc("GetCurrDir") ;
char* cpDir ; // Buffer for current working directory
// If the supplied relative path is actually absolute, just set absolte = relative if (rel[0] == CHAR_FWSLASH) { abs = rel ; return E_OK ; }
// Look for the home directory sequence if (rel[0] == CHAR_TILDA && rel[1] == CHAR_FWSLASH) { abs = getenv("HOME") ; abs += "/" ; abs += (rel + 2) ; return E_OK ; }
// Clear string abs.Clear() ;
// Get current working dir cpDir = get_current_dir_name() ; if (!cpDir) { if (errno == EACCES) return hzwarn(E_NOTFOUND, "The current working directory has access issues") ; if (errno == ENOENT) return hzwarn(E_NOTFOUND, "The current working directory has been unlinked") ;
return hzwarn(E_NOTFOUND, "Unspecified error") ; }
// If no relative path supplied, use the current directory if (!rel || !rel[0]) { abs = cpDir ; free(cpDir) ; return E_OK ; }
char* i ; // Path iterator char* j ; // Placeholder uint32_t lev ; // Directory level uint32_t oset ; // Offset into path uint32_t step ; // Number of backward steps (../)
if (rel[0] == CHAR_PERIOD && rel[1] == CHAR_PERIOD && rel[2] == CHAR_FWSLASH) { for (lev = 0, i = cpDir ; *i ; i++) { if (*i == CHAR_FWSLASH) { j = i ; lev++ ; } }
// Count the sequences of ../ for (step = 1, oset = 3 ; rel[oset] == CHAR_PERIOD && rel[oset+1] == CHAR_PERIOD && rel[oset+2] == CHAR_FWSLASH ; step++, oset += 3) ; if (step > lev) { free(cpDir) ; return E_BADVALUE ; }
for (; step ; step--) { for (j-- ; i != cpDir && *j != CHAR_FWSLASH ; j--) ; } *j = 0 ; }
abs = cpDir ; abs += "/" ; abs += rel ;
free(cpDir) ; return E_OK ; }
hzEcode ReadDir (hzVect<hzDirent>& Dirs, hzVect<hzDirent>& Files, const char* cpPath, const char* cpCriteria) { // Category: Directory // // Populate vectors of directory entries for the sub-directories and files in a given directory where these meet the supplied // selection criteria. This function will not recurse into the sub-directories. // // Arguments: 1) Dirs The vector for the sub-directories // 2) Files The vector for the files // 3) cpPath The directory to be examined (null is taken as the current working directory) // 4) cpCriteria The file selection criteria (null equates to *) // // Returns: E_OPENFAIL If the directory cannot be opened // E_CORRUPT If a directory entry could not be found by the stat function // E_OK If operation successful (even if nothing is found)
_hzfunc("ReadDir(1)") ;
FSTAT fs ; // File status dirent* pDE ; // Diectory entry DIR* pDir ; // Directory hzDirent meta ; // Directory entry meta data hzString thePath ; // The path to here hzString teststr ; // Fullpath test value hzString de_name ; // Directory entry name hzEcode rc ; // Return code
/* ** Move thru current directory and read files and sub directories */
Dirs.Clear() ; Files.Clear() ;
rc = GetAbsPath(thePath, cpPath) ; if (rc != E_OK) return hzwarn(rc, "Could not obtain absolute path for (%s)", cpPath) ;
if (lstat(*thePath, &fs) < 0) return E_NOTFOUND ;
if (!S_ISDIR(fs.st_mode)) return hzwarn(E_TYPE, "Given path (%s) is not a directory", cpPath) ;
pDir = opendir(*thePath) ; if (!pDir) return hzwarn(E_OPENFAIL, "Directory (%s) could not be opened", cpPath) ;
for (; pDE = readdir(pDir) ;) { if (pDE->d_name[0] == '.' && (pDE->d_name[1] == 0 || (pDE->d_name[1] == '.' && pDE->d_name[2] == 0))) continue ;
if (cpCriteria) { if (!FormCheckCstr(pDE->d_name, cpCriteria)) continue ; }
if (cpPath && cpPath[0]) { teststr = cpPath ; teststr += "/" ; teststr += pDE->d_name ; } else teststr = pDE->d_name ;
if (stat(*teststr, &fs) == -1) { closedir(pDir) ; hzerr(E_CORRUPT, "Could not stat directory entry %s", *teststr) ; return E_CORRUPT ; }
if (S_ISDIR(fs.st_mode)) { // The entry is a directory de_name = pDE->d_name ; meta.InitStat(thePath, de_name, fs) ; Dirs.Add(meta) ; continue ; }
if (S_ISREG(fs.st_mode)) { de_name = pDE->d_name ; meta.InitStat(thePath, de_name, fs) ; Files.Add(meta) ; continue ; } }
closedir(pDir) ; return E_OK ; }
hzEcode ReadDir (hzVect<hzDirent>& entries, const char* cpPath, const char* cpCriteria) { // Category: Directory // // Read the directory (cpPath) and place the directory entries for the sub-directories and files, where they meet the supplied // filename criteria (cpCriteria), in a single supplied vector. This function does not recurse into sub-directories. // // Arguments: 1) entries The vector for the sub-directories and files // 2) cpPath The directory to be examined (null is taken as the current working directory) // 3) cpCriteria The file selection criteria (null equates to *) // // Returns: E_NOTFOUND If the requested directory does not exist. // E_OPENFAIL If the requested directory could not be opened (eg insufficient permissions) // E_CORRUPT If an entry found in the directory could not subsequently be stat-ed. // E_OK The operation was successful.
_hzfunc("ReadDir(2)") ;
FSTAT fs ; // File status dirent* pDE ; // Directory entry DIR* pDir ; // Directory pointer hzDirent meta ; // Directory entry metadata hzString thePath ; // Path of directory being read hzString teststr ; // Test string (filenames) hzString filename ; // Filename hzEcode rc ; // Return code
entries.Clear() ;
// Establish applicable directory if (cpPath && cpPath[0]) { // Directory supplied rc = GetAbsPath(thePath, cpPath) ; if (rc != E_OK) return hzwarn(rc, "Could not obtain absolute path for (%s)", cpPath) ;
if (lstat(*thePath, &fs) < 0) return hzwarn(E_NOTFOUND, "No such directory or file exists (%s)", *thePath) ;
if (!S_ISDIR(fs.st_mode)) return hzwarn(E_TYPE, "Given path (%s) is not a directory", *thePath) ;
pDir = opendir(*thePath) ; } else { // No directory supplied, use current GetCurrDir(thePath) ; pDir = opendir(".") ; }
if (!pDir) return hzwarn(E_OPENFAIL, "Directory (%s) could not be opened", *thePath) ; // Perform the directory read for (; pDE = readdir(pDir) ;) { if (pDE->d_name[0] == '.' && (pDE->d_name[1] == 0 || (pDE->d_name[1] == '.' && pDE->d_name[2] == 0))) continue ;
if (!FormCheckCstr(pDE->d_name, cpCriteria)) continue ;
if (thePath) { teststr = thePath ; teststr += "/" ; teststr += pDE->d_name ; } else teststr = pDE->d_name ;
if (stat(*teststr, &fs) == -1) { closedir(pDir) ; hzerr(E_CORRUPT, "Could not stat directory entry %s", *teststr) ; return E_CORRUPT ; }
filename = pDE->d_name ; meta.InitStat(thePath, filename, fs) ; entries.Add(meta) ; }
closedir(pDir) ; return E_OK ; }
hzEcode ListDir (hzVect<hzString>& Dirs, hzVect<hzString>& Files, const char* cpPath, const char* cpCriteria) { // Category: Directory // // Populate vectors of directory entries for the sub-directories and files in a given directory where these meet the supplied // selection criteria. This function will not recurse into the sub-directories. Note that this function is essentially the same as // ReadDir (hzVect<hzDirent>& Dirs, hzVect<hzDirent>& Files, const char* cpPath, const char* cpCriteria) - except that the two vectors // are of strings rather than hzDirent instances. // // Arguments: 1) Dirs The vector for the sub-directory names // 2) Files The vector for the file names // 3) cpPath The directory to be examined (null is taken as the current working directory) // 4) cpCriteria The file selection criteria (null equates to *) // // Returns: E_OPENFAIL If the directory cannot be opened // E_CORRUPT If a directory entry could not be found by the stat function // E_OK If operation successful (even if nothing is found)
_hzfunc("ListDir") ;
FSTAT fs ; // File status dirent* pDE ; // Directory entry DIR* pDir ; // Directory pointer hzDirent meta ; // Directory entry metadata hzString teststr ; // Test string (filenames) hzString name ; // Directory entry name
Dirs.Clear() ; Files.Clear() ;
/* ** Move thru current directory and read files and sub directories */
if (cpPath && cpPath[0]) pDir = opendir(cpPath) ; else pDir = opendir(".") ;
if (!pDir) return E_OPENFAIL ; for (; pDE = readdir(pDir) ;) { if (pDE->d_name[0] == '.' && (pDE->d_name[1] == 0 || (pDE->d_name[1] == '.' && pDE->d_name[2] == 0))) continue ;
if (!FormCheckCstr(pDE->d_name, cpCriteria)) continue ;
if (cpPath && cpPath[0]) { teststr = cpPath ; teststr += "/" ; teststr += pDE->d_name ; } else teststr = pDE->d_name ;
if (stat(*teststr, &fs) == -1) { hzerr(E_CORRUPT, "Could not stat directory entry %s", *teststr) ; return E_CORRUPT ; }
if (S_ISDIR(fs.st_mode)) { name = pDE->d_name ; Dirs.Add(name) ; continue ; }
if (S_ISREG(fs.st_mode)) { name = pDE->d_name ; Files.Add(name) ; continue ; } }
return E_OK ; }
/* ** Directory and File operations */
hzEcode BlattDir (const char* dirname) { // Category: Directory // // Blatts the named directory (if correct permissions). Uses recursion to delete any sub-directories // // Arguments: 1) dirname The directory name (full path) // // Returns: E_ARGUMENT If the directory is not supplied // E_OPENFAIL If the supplied directory could not be opened // E_CORRUPT If any directory entry cannot be stated // E_WRITEFAIL If the directory and any entry within it could not be unlinked // E_OK If the directory was successfully removed
_hzfunc("BlattDir") ;
FSTAT fs ; // To obtain directory entry info dirent* pDE ; // Directory entry DIR* pDir ; // An open directory hzString teststr ; // Full path of directory entry int32_t sys_rc = 0 ; // Return code from std calls hzEcode rc = E_OK ;
/* ** Move thru current directory and read files and sub directories */
if (!dirname || !dirname[0]) return E_ARGUMENT ;
pDir = opendir(dirname) ; if (!pDir) return E_OPENFAIL ; for (; rc == E_OK && (pDE = readdir(pDir)) ;) { if (pDE->d_name[0] == '.' && (pDE->d_name[1] == 0 || (pDE->d_name[1] == '.' && pDE->d_name[2] == 0))) continue ;
teststr = dirname ; teststr += "/" ; teststr += pDE->d_name ;
// Stat failure? if (stat(*teststr, &fs) == -1) { hzerr(E_CORRUPT, "Could not stat directory entry %s", *teststr) ; return E_CORRUPT ; }
if (S_ISDIR(fs.st_mode)) { rc = BlattDir(*teststr) ; continue ; }
if (S_ISREG(fs.st_mode)) { sys_rc = unlink(*teststr) ; if (sys_rc) rc = E_WRITEFAIL ; continue ; } }
closedir(pDir) ;
if (rc == E_OK) { sys_rc = rmdir(dirname) ; if (sys_rc) rc = E_WRITEFAIL ; }
return rc ; }
hzEcode Filecopy (const hzString& tgt, const hzString& src) { // Category: Directory // // Copies a single file. The source must exist, contain data and be readable. The target file will be overwritten it is already exists OR created as new if // the target directory exists and the program the correct access permissions. // // Both the source and target must be fully specified and not contain wildcard characters. // // Arguments: 1) tgt The target file path. // 2) src The source file path. // // Returns: E_ARGUMENT If either the source or the target filepath is NULL // E_NOTFOUND If any filepath specified does not exist as a directory entry of any kind // E_TYPE If any filepath names a directory entry that is not a file // E_NODATA If the source file is empty // E_OPENFAIL If the source file could not be opened for reading // E_OK If the operation was successful
_hzfunc("Filecopy") ;
ifstream is ; // Input stream ofstream os ; // Output stream char buf [1028] ; // Working buffer hzEcode rc = E_OK ; // Return code
if (!tgt) return hzerr(E_ARGUMENT, "No target filepath supplied") ;
// Open source for reading rc = OpenInputStrm(is, src) ; if (rc != E_OK) return rc ;
// Open target for writing os.open(tgt) ; if (os.fail()) { is.close() ; return hzerr(E_WRITEFAIL, "Could not open/create target file %s\n", *tgt) ; }
for (; rc == E_OK ;) { is.read(buf, 1024) ; if (!is.gcount()) break ;
os.write(buf, is.gcount()) ; if (os.fail()) return hzerr(E_WRITEFAIL, "Could not write %d bytes to target file %s\n", is.gcount(), *tgt) ; }
is.close() ; os.close() ;
threadLog("File %s copied to %s\n", *src, *tgt) ; return rc ; }
hzEcode Dircopy (const hzString& tgt, const hzString& src, bool bRecurse) { // Category: Directory // // Copy a directory // // Arguments: 1) tgt This must specify a directory that either exists or can be created // 2) src This must specify a directory that exists. // 3) bRecurse If set, the directory copy will be recursive. // // Returns: E_NOTFOUND If the specified source directory cannot be found. // E_TYPE If the either the specified source or target is not a directory. // E_WRITEFAIL If the target directory cannot be asserted or a file could not be created or written. // E_EXCLUDE If file system permissions were the cause of (3) // E_OK If the operation was fully successful
FSTAT fs ; // Directory entry status hzEcode rc = E_OK ; // Return code
if (!tgt || !src) return E_ARGUMENT ;
if (lstat(src, &fs) == -1) return E_NOTFOUND ;
if (!S_ISDIR(fs.st_mode)) return E_CORRUPT ;
rc = AssertDir(tgt, (uint32_t) 0777) ; if (rc != E_OK) return rc ;
return rc ; }
hzEcode Filemove (const hzString& src, const hzString& tgt) { // Category: Directory // // Move a single file from the source filepath to the target filepath. This is functionally similar to the UNIX mv command or the C library function rename // except that overwriting a file is not permitted. // // Arguments: 1) source The source filepath // 2) target The target filepath // // Returns: E_ARGUMENT If either the source or the target is not supplied // E_NOTFOUND If the source file does not exist // E_DUPLICATE If the target file does exist // E_TYPE If either the source or target is a directory // E_WRITEFAIL If the rename operation failed // E_OK If the file was moved
_hzfunc(__func__) ;
FSTAT fs ; // File status
if (!tgt || !src) return E_ARGUMENT ;
if (lstat(src, &fs) < 0) return E_NOTFOUND ;
if (S_ISDIR(fs.st_mode)) return E_TYPE ;
// Check if target exists if (lstat(tgt, &fs) == 0) { if (S_ISDIR(fs.st_mode)) return E_TYPE ;
return E_DUPLICATE ; }
if (rename(*src, *tgt) < 0) return E_WRITEFAIL ; return E_OK ; }
/* ** Section 2: File listings ** ** Provides the application functions of:- ** 1) hzEcode FindfilesStd (hzArray<hzString>& files, const char* criteria) ; ** 2) hzEcode FindfilesTar (hzArray<hzString>& files, const char* criteria) ; */
static hzEcode _scanfiles_r (hzArray<hzString>& files, hzArray<hzString>& parts, const hzString& pathsofar, uint32_t nLevel, bool bLimit) { // Category: Directory // // Recursive directory scan. // // Method: Scan a directory for files. // // Arguments: 1) files Repository for files found. This is only populated here if we are on the last part of the criteria given in the 2nd argument // 2) parts The parts of the path (eg /home/username has parts of home and username) // 3) Pathsofar the directory to be read in this invokation. // 4) Level this determines if we are in the last part of the directory to be scanned. // 5) Limit If set we do not go into sub-directories of the directory to be scanned for files. // // Returns: E_ARGUMENT If the pathsofar is empty or no parts are specified // E_OPENFAIL If the directory cannot be opened // E_CORRUPT If a directory entry cannot be stated // E_OK If the directory is successfully scanned
_hzfunc("_scanfiles_r") ;
hzVect<hzString> levels ; // Partial paths hzVect<hzString> dirs ; // Needed only for ListDir function
FSTAT fs ; // Directory entry status dirent* pDE ; // Directory entry DIR* pDir ; // Directory pointer hzString teststr ; // File/dir to be tested with stat hzString part ; // This part (of the criteria)
// Check arguments if (!pathsofar || !parts.Count()) return E_ARGUMENT ;
if (bLimit) { // Search for files is limited to specified directories. The last part of the criteria will be matched only on files if (nLevel < 0 || nLevel >= parts.Count()) return E_ARGUMENT ;
part = parts[nLevel] ;
// Are we on the last part of the path in which the filename criteria is specified? if (nLevel == (parts.Count() - 1)) { // We are so we are just looking for file that match. No directories are taken into account
pDir = opendir(*pathsofar) ; if (!pDir) return E_OPENFAIL ; for (; pDE = readdir(pDir) ;) { if (pDE->d_name[0] == '.' && (pDE->d_name[1] == 0 || (pDE->d_name[1] == '.' && pDE->d_name[2] == 0))) continue ;
if (!FormCheckCstr(pDE->d_name, *part)) continue ;
// Build the complete path to the file before calling stat. This is nessesary since we are not actually in the directory // as we have never called chdir()
// Test for path of / if (pathsofar.Length() == 1) teststr = "/" ; else teststr = pathsofar + "/" ; teststr += pDE->d_name ;
if (stat(*teststr, &fs) == -1) { closedir(pDir) ; hzerr(E_CORRUPT, "Could not stat directory entry %s", *teststr) ; return E_CORRUPT ; }
// Only add the entry if it is a file if (S_ISREG(fs.st_mode)) files.Add(teststr) ; }
closedir(pDir) ; return E_OK ; }
// We are not on the last part so olny look for directories matching the current part criteria. Then for each directory call the // next level. pDir = opendir(*pathsofar) ; if (!pDir) return E_OPENFAIL ; for (; pDE = readdir(pDir) ;) { if (pDE->d_name[0] == '.' && (pDE->d_name[1] == 0 || (pDE->d_name[1] == '.' && pDE->d_name[2] == 0))) continue ;
if (!FormCheckCstr(pDE->d_name, *part)) continue ;
if (pathsofar.Length() == 1) teststr = "/" ; else teststr = pathsofar + "/" ; teststr += pDE->d_name ;
if (stat(*teststr, &fs) == -1) { closedir(pDir) ; hzerr(E_CORRUPT, "Could not stat directory entry %s", *teststr) ; return E_CORRUPT ; }
// Only call next level if the entry is a directory if (S_ISDIR(fs.st_mode)) _scanfiles_r(files, parts, teststr, nLevel + 1, bLimit) ; } } else { // Search for files is not limited to specified directories. The last part of the criteria will be matched on both on files // and directories. Where files match this last part they are included as they are in the limited search. Where directories // match this last part all thier files and all the files of all thier subdirectories are included. The purpose of this // mode of operation is to facilitate a tar-like interpretation of a directory. Eg the criteria dvlp/* means the directory // of dvlp (in the current dir) and every file in it and below it.
// On top level? if (nLevel > (parts.Count() - 1)) part = parts[parts.Count() - 1] ; else part = parts[nLevel] ;
// We are not on the last part so olny look for directories matching the current part criteria. Then for each directory call the // next level. pDir = opendir(*pathsofar) ; if (!pDir) return E_OPENFAIL ; for (; pDE = readdir(pDir) ;) { if (pDE->d_name[0] == '.' && (pDE->d_name[1] == 0 || (pDE->d_name[1] == '.' && pDE->d_name[2] == 0))) continue ;
if (!FormCheckCstr(pDE->d_name, *part)) continue ;
if (pathsofar.Length() == 1) teststr = "/" ; else teststr = pathsofar + "/" ; teststr += pDE->d_name ;
if (stat(*teststr, &fs) == -1) { closedir(pDir) ; hzerr(E_CORRUPT, "Could not stat directory entry %s", *teststr) ; return E_CORRUPT ; }
// Current part is a dir? if (S_ISDIR(fs.st_mode)) _scanfiles_r(files, parts, teststr, nLevel + 1, bLimit) ;
// Current part is a file if (S_ISREG(fs.st_mode)) { // If on last part or above add the files if (nLevel >= (parts.Count() - 1)) files.Add(teststr) ; } } }
closedir(pDir) ; return E_OK ; }
static hzEcode _scanfiles (hzArray<hzString>& files, const char* criteria, bool bLimit) { // Category: Directory // // This finds all files matching the supplied criteria and starting from the supplied root directory. The relative // paths [to the root] of the files found will populate the supplied vector (arg 1) // // Arguments: 1) files The vector of files found // 2) criteria The scaning criteria // 3) bLimit Boolean limit // // Returns: E_ARGUMENT If the pathsofar is empty or no parts are specified // E_OPENFAIL If the directory cannot be opened // E_CORRUPT If a directory entry cannot be stated // E_OK If the directory is successfully scanned // // This function uses recursion to read all the directories implied by the criteria. The criteria is split by the forward slash // and each part is examined in turn. Those parts occuring before the last forward slash are called directory parts and the part // occuring after the last forward slash is the file part. // // If the criteria begins with a forward slash the root directory (from which the process starts) is set to the filesystem root // (/). The criteria is then advanced one place before being split. // // If the criteria begins with a ../ sequence we take note of the current directory and step back one place. If there are multiple // ../ sequences we step back multiple times. The ./ sequence is ignored. // // If the criteria begins with any other sequence this is assumed to be a part that will be applied to the contents of the current // directory. // // If bLimit is true then the search for files will be limited to files matching the last part in the directories starting from // the root and matching all the previous parts. But if false the files found will include the above but if the last part also // matches to directories these will be examined as well. False is the default so beware!
hzArray<hzString> parts ; // Parts of full path
hzChain rootVal ; // For building the path so far const char* i ; // Pathname iterator hzString rootDir ; // The path so far hzString cwd ; // Current working dir hzString root ; // Root (rebuilt directory path) hzString curr ; // Current working directory excluding leading slash hzString crit ; // Basename criteria uint32_t nLevel ; // Directory step backs (../) uint32_t nCount ; // Step back iterator
i = criteria ; if (!i || !i[0]) return E_OK ;
if (i[0] == CHAR_FWSLASH) { // Start at the root directory rootVal.AddByte(CHAR_FWSLASH) ; i++ ; } else { // If the criteria starts with a ../ sequence we have to go back from the current directory but if not then we will start at // the current directory GetCurrDir(cwd) ; rootVal = cwd ;
if (i[0] == CHAR_PERIOD && i[1] == CHAR_PERIOD && i[2] == CHAR_FWSLASH) { curr = *cwd + 1 ; SplitStrOnChar(parts, curr, CHAR_FWSLASH) ;
for (nLevel = parts.Count() - 1 ; i[0] == CHAR_PERIOD && i[1] == CHAR_PERIOD && i[2] == CHAR_FWSLASH ; nLevel--, i += 3) ; if (nLevel < 0) return E_NOTFOUND ;
root.Clear() ; for (nCount = 0 ; nCount < nLevel ; nCount++) { rootVal += "/" ; rootVal += parts[nCount] ; } }
if (i[0] == CHAR_PERIOD && i[1] == CHAR_FWSLASH) i += 2 ; }
crit = i ; SplitStrOnChar(parts, crit, CHAR_FWSLASH) ;
rootDir = rootVal ; return _scanfiles_r(files, parts, rootDir, 0, bLimit) ; }
hzEcode FindfilesStd (hzArray<hzString>& files, const char* criteria) { // Category: Directory // // Find files strictly conforming to the supplied criteria. Current Working directory is treated as the root. // // Arguments: 1) files The vector of files found // 2) criteria The scaning criteria // // Returns: E_ARGUMENT If the pathsofar is empty or no parts are specified // E_OPENFAIL If the directory cannot be opened // E_CORRUPT If a directory entry cannot be stated // E_OK If the directory is successfully scanned
return _scanfiles(files, criteria, true) ; }
hzEcode FindfilesTar (hzArray<hzString>& files, const char* criteria) { // Category: Directory // // Find files according to the tar interpretation of the supplied criteria. Current Working directory is treated as the root. // // Arguments: 1) files The vector of files found // 2) criteria The scaning criteria // // Returns: E_ARGUMENT If the pathsofar is empty or no parts are specified // E_OPENFAIL If the directory cannot be opened // E_CORRUPT If a directory entry cannot be stated // E_OK If the directory is successfully scanned
return _scanfiles(files, criteria, false) ; }
/* ** TEST Files and Directories */
#if 0 hzEcode TestFile (hzDirent& de, const char* cpPathname) { // Category: Directory // // Determine if a file exists at the supplied pathname and populate the supplied hzDirent instance with the file info if it does // // Arguments: 1) de The hzDirent instance to be populated // 2) cpPathname The full pathname of the anticipated file // // Returns: E_ARGUMENT If the pathname was not supplied // E_NOTFOUND If the file does not exist // E_TYPE If the supplied pathname is that of a directory or non-file // E_OK If the files exists
FSTAT fs ; // File status hzString name ; // Directory entry name
if (!cpPathname || !cpPathname[0]) return E_ARGUMENT ;
if (lstat(cpPathname, &fs) == -1) return E_NOTFOUND ;
if (ISDIR(fs.st_mode)) return E_TYPE ;
de.InitStat(0, name, fs) ;
return E_OK ; } #endif
hzEcode TestFile (const char* fullpath) { // Category: Directory // // Determine if a file exists at the supplied pathname. // // Arguments: 1) fullpath The full pathname of the anticipated file // // Returns: E_ARGUMENT If the pathname was not supplied // E_NODATA If permissions were denied // E_CORRUPT If the supplied pathname was nonsensical (stat operation produced an errno of EIO, ELOOP or EOVERFLOW) // E_SYNTAX If the supplied pathname was too long // E_NOTFOUND If the anticipated file does not exist // E_TYPE If the supplied pathname is that of a directory or non-file // E_OK If the files exists
FSTAT fs ; // File status hzEcode rc = E_OK ; // Return code
if (lstat(fullpath, &fs) == -1) { switch (errno) { case EACCES: rc = E_NODATA ; break ; case EIO: rc = E_CORRUPT ; break ; case ELOOP: rc = E_CORRUPT ; break ; case ENAMETOOLONG: rc = E_SYNTAX ; break ; case ENOENT: rc = E_NOTFOUND ; break ; case ENOTDIR: rc = E_SYNTAX ; break ; case EOVERFLOW: rc = E_CORRUPT ; break ; } } else { if (ISDIR(fs.st_mode)) rc = E_TYPE ; }
return rc ; }
#if 0 hzEcode TestDir (hzDirent& de, const char* cpPathname) { // Category: Directory // // Determine if a directory exists at the supplied pathname and populate the supplied hzDirent instance with the file info if it does // // Arguments: 1) de The hzDirent instance to be populated // 2) cpPathname The full pathname of the anticipated directory // // Returns: E_ARGUMENT If the pathname was not supplied // E_NOTFOUND If the directory does not exist // E_TYPE If the supplied pathname is not that of a directory // E_OK If the directory exists
FSTAT fs ; // Directory status hzString name ; // Directory name
if (!cpPathname || !cpPathname[0]) return E_ARGUMENT ;
if (lstat(cpPathname, &fs) == -1) return E_NOTFOUND ;
if (!ISDIR(fs.st_mode)) return E_TYPE ;
de.InitStat(0, name, fs) ;
return E_OK ; } #endif
hzEcode DirExists (const char* dirname) { // Category: Directory // // Determine if a directory exists at the supplied pathname. // // Arguments: 1) dirname The full pathname of the anticipated file // // Returns: E_ARGUMENT If the pathname was not supplied // E_NOTFOUND If the directory does not exist // E_TYPE If the supplied pathname is not that of a directory // E_OK If the directory exists
FSTAT fs ; // Directory status
if (!dirname || !dirname[0]) return E_ARGUMENT ;
if (stat(dirname, &fs) == -1) return E_NOTFOUND ;
if (!S_ISDIR(fs.st_mode)) return E_TYPE ;
return E_OK ; }
hzEcode OpenInputStrm (std::ifstream& input, const char* filepath) { // Category: Directory // // Uses the supplied input stream to open a file at the specified filepath. The main reason for this function is to standarize error condition reporting as // all file open operations have essentially the same set of issues namely:- // // 1) The file either is not there // 2) The pathname names a directory or other file system object, instead of a file // 3) The file is empty (an error when intending to read) // 4) The file cannot be read (program does not have access permissions // // Arguments: 1) input Reference to an input stream which this function will open // 2) filepath Full pathname of file to open // 3) callfn This function's caller. If this is zero, this function refrains from logging error and just returns them. // // Returns: E_ARGUMENT If the filepath is NULL // E_NOTFOUND If the filepath specified does not exist as a directory entry of any kind // E_TYPE If the filepath names a directory entry that is not a file // E_NODATA If the filepath is a file but empty // E_OPENFAIL If the file specified could not be opened for reading // E_OK If the input stream was opened OK
_hzfunc(__func__) ;
FSTAT fs ; // File status
// Check filepath is supplied if (!filepath || !filepath[0]) return hzerr(E_ARGUMENT, "No input filename specified") ;
// Check if filepath names a file if (lstat(filepath, &fs) < 0) return hzerr(E_NOTFOUND, "Filename %s does not exist", filepath) ;
// Check filepath actually is a file if (!S_ISREG(fs.st_mode)) return hzerr(E_TYPE, "Filename %s is not a file", filepath) ;
// Check file has content if (!fs.st_size) return hzerr(E_NODATA, "Filename %s is empty", filepath) ;
// Check supplied stream is not already open if (input.is_open()) hzwarn(E_DUPLICATE, "Filename %s is already open", filepath) ; else { input.open(filepath) ; if (input.fail()) return hzerr(E_OPENFAIL, "Filename %s could not be opened", filepath) ; }
return E_OK ; }
/* ** SECTION 2: hzFileset functions */
void hzFileset::_clear (void) { // Clear the file set. This function returns void and has no arguments. There are no reportable errors. // // Arguments: None // Returns: None
uint32_t nIndex ; // Directory iterator
m_nBlocs = 0 ; m_nChars = 0 ; m_nLinks = 0 ; m_nSocks = 0 ; m_nBytes = 0 ;
for (nIndex = 0 ; nIndex < m_dirs.Count() ; nIndex++) { delete m_dirs.GetObj(nIndex) ; } m_dirs.Clear() ;
for (nIndex = 0 ; nIndex < m_file.Count() ; nIndex++) { delete m_file.GetObj(nIndex) ; } m_file.Clear() ; }
hzEcode hzFileset::_scan_r (hzDirent* parent, hzString& curdir) { // Move thru current directory and read files and sub directories. As this is a private function of the hzFileset // class and cannot be called directly by an application, certain assumptions have been permitted. At the point // where it is called by hzFileset::Scan() and the parent is null the directory we are actually in is one of the // roots of the hzFileset instance. // // Arguments: 1) parent Pointer to parent directory entry // 2) curdir Current directory name // // Returns: E_ARGUMENT If the current directory is not supplied // E_OPENFAIL If the directory cannot be opened // E_CORRUPT If the supplied parent is not a directory or if an listed entity cannot be stated // E_OK If the directory was scanned successfully
_hzfunc("hzFileset::_scan_r") ;
FSTAT fs ; // File status dirent* pDE ; // Directory entry DIR* pDir ; // Directory hzDirent* pThisdir ; // Directory being processed hzDirent* pFX ; // New directory entry hzString nextent ; // Next directory entry hzString filename ; // Filename hzEcode rc = E_OK ; // Return code
/* ** Open, check and init the directory */
if (!curdir) return hzerr(E_ARGUMENT, "Error: No current directory\n") ;
if (parent) { // If a parent has been supplied, this must be a directory
if (!parent->IsDir()) return hzerr(E_CORRUPT, "Error: Supplied parent (%s) is not a directory\n", parent->txtName()) ; }
// Ignore certain system/kernel directories if (curdir == "/proc") return E_OK ; if (curdir == "/dev") return E_OK ;
if (stat(*curdir, &fs) < 0) return hzerr(E_CORRUPT, "Could not stat supplied 'current directory' (%s)\n", *curdir) ;
// Create the dir-ent for this current directory and insert it into the FS-Image directory maps pThisdir = new hzDirent() ; if (!pThisdir) Fatal("Could not create hzDirent instance\n") ; pThisdir->InitStat(parent->strName(), curdir, fs) ;
if (!pThisdir->IsDir()) return hzerr(E_CORRUPT, "Error: Supplied directory (%s) is not a directory\n", pThisdir->txtName()) ;
m_dirs.Insert(pThisdir->Path(), pThisdir) ;
/* ** Read the entries for this directory */
if (!(pDir = opendir(*curdir))) return hzerr(E_OPENFAIL, "Could not open directory %s", *curdir) ;
for (; (pDE = readdir(pDir)) ;) { if (!strcmp(pDE->d_name, ".")) continue ; if (!strcmp(pDE->d_name, "..")) continue ;
nextent = curdir ; nextent += "/" ; nextent += pDE->d_name ;
if (lstat(*nextent, &fs) < 0) { m_Error.Printf("Error: lstat returned (errno) %s\n", ShowErrno()) ; m_Error.Printf("Listed entry (%s) could not be stat-ed\n", *nextent) ; return E_CORRUPT ; }
if (S_ISDIR(fs.st_mode)) { // The entry is a directory
if (curdir == "/") { if (!strcmp(pDE->d_name, "proc")) continue ; if (!strcmp(pDE->d_name, "dev")) continue ; }
rc = _scan_r(pThisdir, nextent) ; if (rc != E_OK) return rc ;
continue ; }
if (S_ISREG(fs.st_mode)) { // The etry is a file so add/update
if (!(pFX = new hzDirent())) Fatal("Could not create hzDirent instance\n") ;
filename = pDE->d_name ; pFX->InitStat(pThisdir->strName(), filename, fs) ; m_file.Insert(pFX->Path(), pFX) ; m_nBytes += pFX->Size() ;
//pThisdir->AddChild(pFX) ;
continue ; }
if (S_ISBLK(fs.st_mode)) m_nBlocs++ ; if (S_ISCHR(fs.st_mode)) m_nChars++ ; if (S_ISLNK(fs.st_mode)) m_nLinks++ ; if (S_ISSOCK(fs.st_mode)) m_nSocks++ ; }
closedir(pDir) ; return E_OK ; }
hzEcode hzFileset::Scan (void) { // Run a fileset scan. Firstly mark all files for delete before a run. Then during the run the new files will be marked as CREATE and existing files either // as MODIFY or NOACTION // // Arguments: None // // Returns: E_ARGUMENT If the directory name is missing from the fileset root // E_OPENFAIL If the directory cannot be opened // E_CORRUPT If the supplied parent is not a directory or if an listed entity cannot be stated // E_OK If the directory was scanned successfully
_hzfunc("hzFileset::Scan") ;
hzList<hzString>::Iter ri ; // Roots iterator
FSTAT fs ; // To check if dir exists hzString opdir ; // Operations (root) directory. hzEcode rc = E_OK ; // Return code
m_Error.Clear() ;
_clear() ;
/* ** Save the current directory then iterate through the root directories, building the dependancy tree of each. ** When done return to the original current directory. */
for (ri = m_Roots ; ri.Valid() ; ri++) { opdir = ri.Element() ;
if (lstat(*opdir, &fs) < 0) { m_Error.Printf("Warning: root of %s does not exist\n", *opdir) ; continue ; }
rc = _scan_r(0, opdir) ; if (rc != E_OK) return rc ; }
return E_OK ; }
hzEcode hzFileset::Import (const char* filepath) { // Populate the hzFileset instance from a import file // // Arguments: 1) filepath The import file // // Returns: E_ARGUMENT If the import filename is not supplied // E_OPENFAIL If the import file cannot be opened // E_READFAIL If the import file cannot be read // E_OK If the import was successful
_hzfunc("hzFileset::Import()") ;
std::ifstream is ; // File input stream
hzDirent* pDX ; // New directory hzDirent* pFX ; // New file char* i ; // Buffer iterator hzString newname ; // New dir/file name uint32_t nLine ; // Line number uint32_t nInode ; // Inode (used in copy protection etc) uint32_t nSize ; // File size uint32_t nCtime ; // File create date uint32_t nMtime ; // File modified date uint32_t nMode ; // File mode uint32_t nOwner ; // UNIX owner id uint32_t nGroup ; // UNIX group id bool bOk ; // Format OK hzEcode rc ; // Return code char cvLine[512] ; // Line buffer
// Check filepath if (!filepath || !filepath[0]) return E_ARGUMENT ;
// Open for reading is.open(filepath) ; if (is.fail()) return E_OPENFAIL ;
// Read in line by line for (nLine = 1 ;; nLine++) { is.getline(cvLine, 512) ; if (!is.gcount()) break ;
i = cvLine ;
if (i[0] == '#') continue ;
// These apply to both dirs and files
bOk = IsPosint(nInode, i + 2) ; if (bOk) bOk = IsHexnum(nMode, i + 13) ; if (bOk) bOk = IsPosint(nSize, i + 22) ; if (bOk) bOk = IsPosint(nCtime, i + 33) ; if (bOk) bOk = IsPosint(nMtime, i + 44) ; if (bOk) bOk = IsPosint(nOwner, i + 55) ; if (bOk) bOk = IsPosint(nGroup, i + 61) ;
if (!bOk) { is.close() ; return hzerr(E_CORRUPT, "Line %d of %s is malformed", nLine, filepath) ; }
newname = i + 67 ;
if (i[0] == 'D') { // Directory entry pDX = new hzDirent() ; pDX->InitNorm(0, newname, nInode, nSize, nCtime, nMtime, nMode, nOwner, nGroup, 0) ; m_dirs.Insert(pDX->Path(), pDX) ; continue ; }
if (i[0] == 'F') { // File entry
pFX = new hzDirent() ; pFX->InitNorm(pDX->strName(), newname, nInode, nSize, nCtime, nMtime, nMode, nOwner, nGroup, 0) ; m_nBytes += pFX->Size() ; m_file.Insert(pFX->Path(), pFX) ; } }
is.close() ;
return E_OK ; }
hzEcode hzFileset::Export (const char* filepath) { // Export the hzDirSync to a file // // Arguments: 1) filepath The 'horizon' file // // Returns: E_ARGUMENT If the export filename is not supplied // E_OPENFAIL If the export file cannot be opened // E_WRITEFAIL If the export file cannot be written to or there was a write failure // E_OK If the export was successful
_hzfunc("hzFileset::Export()") ;
hzList<hzDirent*>::Iter I ; // Directory listing
std::ofstream os ; // For writing file
hzChain Z ; // For formulating output hzDirent* pDX ; // Directory hzDirent* pFX ; // File uint32_t totSize = 0 ; // Total size of all files uint32_t nD ; // Fileset directory iterator uint32_t nF ; // Fileset file iterator uint32_t fileLo ; // Object position of first file in the given directory uint32_t fileHi ; // Object position of last file in the given directory char cvLine [512] ; // Working buffer
if (!filepath || !filepath[0]) return E_ARGUMENT ;
os.open(filepath) ; if (os.fail()) return E_OPENFAIL ;
Z.Printf("# Export: %s Directories\n", FormalNumber(m_dirs.Count(),0)) ; Z.Printf("# Export: %s Files\n", FormalNumber(m_file.Count(),0)) ; os << Z ;
if (os.fail()) return E_WRITEFAIL ;
for (nD = 0 ; nD < m_dirs.Count() ; nD++) { pDX = m_dirs.GetObj(nD) ;
sprintf(cvLine, "D %010d %08x %010u %010u %010u %05d %05d %s\n", pDX->Inode(), pDX->Mode(), pDX->Size(), pDX->Ctime(), pDX->Mtime(), pDX->Owner(), pDX->Group(), pDX->txtName()) ; os << cvLine ;
fileLo = m_file.First(pDX->strName()) ; if (fileLo < 0) continue ;
fileHi = m_file.Last(pDX->strName()) ;
for (nF = fileLo ; nF <= fileHi ; nF++) //for (I = pDX->m_Kinder ; I.Valid() ; I++) { //pFX = I.Element() ; pFX = m_file.GetObj(nF) ;
if (pFX->IsDir()) continue ;
totSize += pFX->Size() ;
sprintf(cvLine, "F %010d %08x %010u %010u %010u %05d %05d %s\n", pFX->Inode(), pFX->Mode(), pFX->Size(), pFX->Ctime(), pFX->Mtime(), pFX->Owner(), pFX->Group(), pFX->txtName()) ; os << cvLine ; } }
if (os.fail()) return E_WRITEFAIL ;
os << "# Export Complete - total " << FormalNumber(totSize,0) << " bytes\n" ; os.close() ; return E_OK ; }
/* ** Section 1: Directory assertion. Create directory if not already in existance */
hzEcode AssertDir (const char* dirname, uint32_t nPerms) { // Category: Directory // // Test if a directory at the supplied path exists and if not, attempts to create it. // // Arguments: 1) dirname The directory pathname // 2) nPerms The permissions that will be given to the directory if created by this function. // // Returns: E_ARGUMENT If the directory pathname is not supplied. // E_NOCREATE If the directory could not be created // E_OK Operation successful
_hzfunc("AssertDir") ;
FSTAT fs ; // File status const char* i ; // Directory path iterator char* j ; // Path part terminator char* cpPath ; // Working buffer (for path parts)
// Check arguments if (!dirname || !dirname[0]) return E_ARGUMENT ;
// Check if path exists if (stat(dirname, &fs) != -1) { // Is path a directory? if (fs.st_mode & S_IFDIR) return E_OK ;
return hzerr(E_NOCREATE, "Target dir [%s] exists as non-directory", dirname) ; }
// The path does not exist so create. Begin with asserting the first part of the path and move through each '/' in turn. cpPath = new char[strlen(dirname) + 1] ;
j = cpPath ; i = dirname ;
// Loop thru subdirectories for (; *i ;) { for (*j++ = *i++ ; *i && *i != CHAR_FWSLASH ; *j++ = *i++) ; *j = 0 ;
if (stat(cpPath, &fs) == -1) { // Check permision to create subdir if (mkdir(cpPath, (nPerms & 0x01ff)) == -1) { hzerr(E_OPENFAIL, "Cannot make dir [%s] %s", cpPath, strerror(errno)) ; delete cpPath ; return E_OPENFAIL ; }
continue ; }
// Is sub-path a directory? if (fs.st_mode & S_IFDIR) continue ;
hzerr(E_OPENFAIL, "Directory entity [%s] exists as non-directory", cpPath) ; delete cpPath ; return E_OPENFAIL ; }
delete cpPath ; return E_OK ; }