diff --git a/cwFileSys.cpp b/cwFileSys.cpp index 43b50ec..9df9e19 100644 --- a/cwFileSys.cpp +++ b/cwFileSys.cpp @@ -8,6 +8,7 @@ #ifdef cwLINUX #include #include +#include // 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( 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( 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; } diff --git a/cwFileSys.h b/cwFileSys.h index 85bca35..3ce3548 100644 --- a/cwFileSys.h +++ b/cwFileSys.h @@ -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 ); + + } } diff --git a/cwWebSockSvr.h b/cwWebSockSvr.h index 9515bcc..96cbd24 100644 --- a/cwWebSockSvr.h +++ b/cwWebSockSvr.h @@ -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. diff --git a/main.cpp b/main.cpp index 2e89a28..a1f6ad4 100644 --- a/main.cpp +++ b/main.cpp @@ -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