//
// 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 ;
}