Implemented filesys::dirEntries()

This commit is contained in:
kpl 2019-12-27 23:35:39 -05:00
parent 5a62d884dd
commit 68b8a15714
4 changed files with 303 additions and 4 deletions

View File

@ -8,6 +8,7 @@
#ifdef cwLINUX
#include <libgen.h>
#include <sys/stat.h>
#include <dirent.h> // opendir()/readdir()
#endif
namespace cw
@ -140,7 +141,7 @@ char* cw::filesys::vMakeFn( const char* dir, const char* fn, const char* ext, va
while( (dp = va_arg(vl,const char*)) != nullptr )
n += strlen(dp) + 1; // add 1 for ending sep
// add 1 for terminating zero and allocate memory
// add 1 for terminating zero and allocate memory
if((rp = mem::allocZ<char>( n+1 )) == nullptr )
{
@ -340,6 +341,257 @@ cw::filesys::pathPart_t* cw::filesys::pathParts( const char* pathStr )
rp->extStr = nullptr;
return rp;
}
namespace cw
{
namespace filesys
{
typedef struct
{
unsigned filterFlags;
dirEntry_t* rp;
char* dataPtr;
char* endPtr;
unsigned entryCnt;
unsigned entryIdx;
unsigned dataByteCnt;
unsigned passIdx;
} deRecd_t;
cw::rc_t _dirGetEntries( deRecd_t* drp, const char* dirStr )
{
rc_t rc = kOkRC;
DIR* dirp = NULL;
struct dirent* dp = NULL;
char curDirPtr[] = "./";
unsigned dn = 0;
if( dirStr == NULL || strlen(dirStr) == 0 )
dirStr = curDirPtr;
if( isDir(dirStr) == false )
return rc;
unsigned fnCharCnt = strlen(dirStr) + PATH_MAX;
char fn[ fnCharCnt + 1 ];
// copy the directory into fn[] ...
fn[0] = 0;
fn[fnCharCnt] = 0;
strcpy(fn,dirStr);
cwAssert( strlen(fn)+2 < fnCharCnt );
// ... and be sure that it is terminated with a path sep char
if( fn[ strlen(fn)-1 ] != cwPathSeparatorChar )
{
char sep[] = { cwPathSeparatorChar, '\0' };
strcat(fn,sep);
}
// file names will be appended to the path at this location
unsigned fni = strlen(fn);
// open the directory
if((dirp = opendir(dirStr)) == NULL)
{
rc = cwLogSysError(kOpFailRC,errno,"Unable to open the directory:'%s'.",dirStr);
goto errLabel;
}
// get the next directory entry
while((dp = readdir(dirp)) != NULL )
{
// validate d_name
if( (dn = strlen(dp->d_name)) > 0 )
{
unsigned flags = 0;
// handle cases where d_name begins with '.'
if( dp->d_name[0] == '.' )
{
if( strcmp(dp->d_name,".") == 0 )
{
if( cwIsFlag(drp->filterFlags,kCurDirFsFl) == false )
continue;
flags |= kCurDirFsFl;
}
if( strcmp(dp->d_name,"..") == 0 )
{
if( cwIsFlag(drp->filterFlags,kParentDirFsFl) == false )
continue;
flags |= kParentDirFsFl;
}
if( flags == 0 )
{
if( cwIsFlag(drp->filterFlags,kInvisibleFsFl) == false )
continue;
flags |= kInvisibleFsFl;
}
}
fn[fni] = 0;
strncat( fn, dp->d_name, fnCharCnt-fni );
unsigned fnN = strlen(fn);
// if the filename is too long for the buffer
if( fnN > fnCharCnt )
{
rc = cwLogSysError(kBufTooSmallRC, errno, "The directory entry:'%s' was too long to be processed.",dp->d_name);
goto errLabel;
}
// is a link
if( isLink(fn) )
{
if( cwIsFlag(drp->filterFlags,kLinkFsFl) == false )
continue;
flags |= kLinkFsFl;
if( cwIsFlag(drp->filterFlags,kRecurseLinksFsFl) )
if((rc = _dirGetEntries(drp,fn)) != kOkRC )
goto errLabel;
}
else
{
// is the entry a file
if( isFile(fn) )
{
if( cwIsFlag(drp->filterFlags,kFileFsFl)==false )
continue;
flags |= kFileFsFl;
}
else
{
// is the entry a dir
if( isDir(fn) )
{
if( cwIsFlag(drp->filterFlags,kDirFsFl) == false)
continue;
flags |= kDirFsFl;
if( cwIsFlag(drp->filterFlags,kRecurseFsFl) )
if((rc = _dirGetEntries(drp,fn)) != kOkRC )
goto errLabel;
}
else
{
continue;
}
}
}
//cwAssert(flags != 0);
if( drp->passIdx == 0 )
{
++drp->entryCnt;
// add 1 for the name terminating zero
drp->dataByteCnt += sizeof(dirEntry_t) + 1;
if( cwIsFlag(drp->filterFlags,kFullPathFsFl) )
drp->dataByteCnt += fnN;
else
drp->dataByteCnt += dn;
}
else
{
cwAssert( drp->passIdx == 1 );
cwAssert( drp->entryIdx < drp->entryCnt );
unsigned n = 0;
if( cwIsFlag(drp->filterFlags,kFullPathFsFl) )
{
n = fnN+1;
cwAssert( drp->dataPtr + n <= drp->endPtr );
strcpy(drp->dataPtr,fn);
}
else
{
n = dn+1;
cwAssert( drp->dataPtr + n <= drp->endPtr );
strcpy(drp->dataPtr,dp->d_name);
}
drp->rp[ drp->entryIdx ].flags = flags;
drp->rp[ drp->entryIdx ].name = drp->dataPtr;
drp->dataPtr += n;
cwAssert( drp->dataPtr <= drp->endPtr);
++drp->entryIdx;
}
}
}
errLabel:
if( dirp != NULL )
closedir(dirp);
return rc;
}
}
}
cw::filesys::dirEntry_t* cw::filesys::dirEntries( const char* dirStr, unsigned filterFlags, unsigned* dirEntryCntPtr )
{
rc_t rc = kOkRC;
deRecd_t r;
memset(&r,0,sizeof(r));
//r.p = _cmFileSysHandleToPtr(h);
r.filterFlags = filterFlags;
cwAssert( dirEntryCntPtr != NULL );
*dirEntryCntPtr = 0;
for(r.passIdx=0; r.passIdx<2; ++r.passIdx)
{
if((rc = _dirGetEntries( &r, dirStr )) != kOkRC )
goto errLabel;
if( r.passIdx == 0 && r.dataByteCnt>0 )
{
// allocate memory to hold the return values
if(( r.rp = mem::allocZ<dirEntry_t>( r.dataByteCnt )) == NULL )
{
rc = cwLogError(kObjAllocFailRC, "Unable to allocate %i bytes of dir entry memory.",r.dataByteCnt);
goto errLabel;
}
r.dataPtr = (char*)(r.rp + r.entryCnt);
r.endPtr = ((char*)r.rp) + r.dataByteCnt;
}
}
errLabel:
if( rc == kOkRC )
{
cwAssert( r.entryIdx == r.entryCnt );
*dirEntryCntPtr = r.entryCnt;
}
else
{
if( r.rp != NULL )
mem::release(r.rp);
r.rp = NULL;
}
return r.rp;
}

View File

@ -39,6 +39,39 @@ namespace cw
// The returned record and the strings it points to are contained in a single block of
// memory which must be released by a call to memRelease() or memFree()
pathPart_t* pathParts( const char* pathNameStr );
// Flags used by dirEntries 'includeFlags' parameter.
enum
{
kFileFsFl = 0x001, //< include all visible files
kDirFsFl = 0x002, //< include all visible directory
kLinkFsFl = 0x004, //< include all symbolic links
kInvisibleFsFl = 0x008, //< include file/dir name beginning with a '.'
kCurDirFsFl = 0x010, //< include '.' directory
kParentDirFsFl = 0x020, //< include '..' directory
kAllFsFl = 0x02f, //< all type flags
kFullPathFsFl = 0x040, //< return the full path in the 'name' field of dirEntry_t;
kRecurseFsFl = 0x080, //< recurse into directories
kRecurseLinksFsFl = 0x100 //< recurse into symbol link directories
};
// The return type for dirEntries().
typedef struct
{
unsigned flags; //< Entry type flags from kXXXFsFl.
const char* name; //< Entry name or full path depending on kFullPathFsFl.
} dirEntry_t;
// Return the file and directory names contained in a given subdirectory.
//
// Set 'includeFlags' with the kXXXFsFl flags of the files to include in the returned
// directory entry array. The value pointed to by dirEntryCntPtr will be set to the
// number of records in the returned array.
dirEntry_t* dirEntries( const char* dirStr, unsigned includeFlags, unsigned* dirEntryCntRef );
}
}

View File

@ -20,8 +20,7 @@ namespace cw {
rc_t destroy( handle_t& h );
thread::handle_t threadHandle( handle_t h );
thread::handle_t threadHandle( handle_t h );
websock::handle_t websockHandle( handle_t h );
// Start or unpause the server.

View File

@ -160,6 +160,20 @@ void socketSrvTest( cw::object_t* cfg, int argc, const char* argv[] )
}
}
void dirEntryTest( cw::object_t* cfg, int argc, const char* argv[] )
{
if( argc >= 2 )
{
const char* path = argv[1];
unsigned dirEntryN = 0;
unsigned includeFlags = cw::filesys::kFileFsFl | cw::filesys::kDirFsFl | cw::filesys::kFullPathFsFl | cw::filesys::kRecurseFsFl;
cw::filesys::dirEntry_t* de = cw::filesys::dirEntries( path,includeFlags, &dirEntryN );
for(unsigned i=0; i<dirEntryN; ++i)
cwLogInfo("%s",de[i].name);
cw::mem::release(de);
}
}
void stubTest( cw::object_t* cfg, int argc, const char* argv[] )
{
@ -202,6 +216,7 @@ int main( int argc, const char* argv[] )
{ "audioDevAlsa", audioDevAlsaTest },
{ "socket", socketTest },
{ "socketSrv", socketSrvTest },
{ "dirEntry", dirEntryTest },
{ "stub", stubTest },
{ nullptr, nullptr }
};