commit 3954d794f1d06324b28757fd5b11dc862028b6cc Author: kpl Date: Wed Dec 18 22:24:12 2019 -0500 Initial commit diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8541a99 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ + +HDR = cwCommon.h cwCommonImpl.h cwMem.h cwLog.h +SRC = main.cpp cwCommonImpl.cpp cwMem.cpp cwLog.cpp + +HDR += cwFileSys.h cwText.h cwFile.h cwLex.h cwNumericConvert.h +SRC += cwFileSys.cpp cwText.cpp cwFile.cpp cwLex.cpp + +HDR += cwObject.h cwTextBuf.h cwThread.h +SRC += cwObject.cpp cwTextBuf.cpp cwThread.cpp + + +LIBS = -lpthread + + +cw_rt : $(SRC) $(HDR) + g++ -g --std=c++17 $(LIBS) -Wall -DcwLINUX -o $@ $(SRC) + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..a959260 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ + + + + +- logDefaultFormatter() in cwLog.cpp uses stack allocated memory in a way that could easily be exploited. + +- lexIntMatcher() in cwLex.cpp doesn't handle 'e' notation correctly. See note in code. + +- numeric_convert() in cwNumericConvert.h could be made more efficient using type_traits. \ No newline at end of file diff --git a/cwCommon.h b/cwCommon.h new file mode 100644 index 0000000..c610668 --- /dev/null +++ b/cwCommon.h @@ -0,0 +1,66 @@ +#ifndef cwCOMMON_H +#define cwCOMMON_H + +#include // declares 'NULL' +#include + + + +#define kInvalidIdx ((unsigned)-1) +#define kInvalidId ((unsigned)-1) +#define kInvalidCnt ((unsigned)-1) + + +namespace cw +{ + + typedef enum + { + kOkRC = 0, + kObjAllocFailRC, // an object allocation failed + kObjFreeFailRC, // an object free failed + kInvalidOpRC, // the current state does not support the operation + kInvalidArgRC, + kInvalidIdRC, // an identifer was found to be invalid + kOpenFailRC, + kCloseFailRC, + kWriteFailRC, + kReadFailRC, + kFlushFailRC, + kSeekFailRC, + kEofRC, + kResourceNotAvailableRC, + kMemAllocFailRC, + kGetAttrFailRC, + kSetAttrFailRC, + kTimeOutRC, + kProtocolErrorRC, + kOpFailRC, + kSyntaxErrorRC, + kBufTooSmallRC, + kAssertFailRC, // used with cwLogFatal + kBaseAppRC + } cwRC_t; + + typedef unsigned rc_t; + + typedef struct handle_str + { + void* p; + } handle_t; + + template< typename T > + struct handle + { + typedef T p_type; + T* p = nullptr; + + void set(T* ptr) { this->p=ptr; } + bool isValid() const { return this->p != nullptr; } + void release() { memRelease(p); p=nullptr; } + }; + +} + + +#endif diff --git a/cwCommonImpl.cpp b/cwCommonImpl.cpp new file mode 100644 index 0000000..2662132 --- /dev/null +++ b/cwCommonImpl.cpp @@ -0,0 +1,76 @@ +#include "cwCommon.h" +#include "cwLog.h" + +#include "cwCommonImpl.h" + +#include + +namespace cw +{ + void _sleep( struct timespec* ts ) + { + // TODO: consider handling errors from nanosleep + nanosleep(ts,NULL); + } + +} + +const char* cw::idToLabel( const idLabelPair_t* array, unsigned id, unsigned eolId ) +{ + const idLabelPair_t* p = array; + for(; p->id != eolId; ++p) + if( p->id == id ) + return p->label; + + return nullptr; +} + +unsigned cw::labelToId( const idLabelPair_t* array, const char* label, unsigned eolId ) +{ + const idLabelPair_t* p = array; + for(; p->id != eolId; ++p) + if( std::strcmp(label,p->label) == 0 ) + return p->id; + + return eolId; +} + + + + + +void cw::sleepSec( unsigned secs ) +{ + struct timespec ts; + ts.tv_sec = secs; + ts.tv_nsec = 0; + + cw::_sleep(&ts); +} + +void cw::sleepMs( unsigned ms ) +{ + struct timespec ts; + ts.tv_sec = ms / 1000; + ts.tv_nsec = (ms % 1000) * 1000000; + + cw::_sleep(&ts); +} + +void cw::sleepUs( unsigned us ) +{ + struct timespec ts; + ts.tv_sec = us / 1000000; + ts.tv_nsec = (us % 1000000) * 1000; + + cw::_sleep(&ts); +} + +void cw::sleepNs( unsigned ns ) +{ + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = ns; + + cw::_sleep(&ts); +} diff --git a/cwCommonImpl.h b/cwCommonImpl.h new file mode 100644 index 0000000..1cb6af6 --- /dev/null +++ b/cwCommonImpl.h @@ -0,0 +1,72 @@ +#ifndef cwCommonImpl_H +#define cwCommonImpl_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include // std::min,std::max +#include // std::forward +#include // std::numeric_limits< + +#if defined(cwLINUX) || defined(cwOSX) +#define cwPOSIX_FILE_SYS +#include // linux time.h +#define cwPathSeparatorChar '/' +#endif + +#define cwStringNullGuard(s) ((s)==nullptr ? "" : (s)) + + +#define cwAllFlags(f,m) (((f) & (m)) == (m)) // Test if all of a group 'm' of binary flags in 'f' are set. +#define cwIsFlag(f,m) (((f) & (m)) ? true : false) // Test if any one of a the bits in 'm' is also set in 'f'. +#define cwIsNotFlag(f,m) (cwIsFlag(f,m)==false) // Test if none of the bits in 'm' are set in 'f'. +#define cwSetFlag(f,m) ((f) | (m)) // Return 'f' with the bits in 'm' set. +#define cwClrFlag(f,m) ((f) & (~(m))) // Return 'f' with the bits in 'm' cleared. +#define cwTogFlag(f,m) ((f)^(m)) // Return 'f' with the bits in 'm' toggled. +#define cwEnaFlag(f,m,b) ((b) ? cwSetFlag(f,m) : cwClrFlag(f,m)) // Set or clear bits in 'f' based on bits in 'm' and the state of 'b'. + +// In-place assignment version of the above bit operations +#define cwSetBits(f,m) ((f) |= (m)) // Set 'f' with the bits in 'm' set. +#define cwClrBits(f,m) ((f) &= (~(m))) // Set 'f' with the bits in 'm' cleared. +#define cwTogBits(f,m) ((f)^=(m)) // Return 'f' with the bits in 'm' toggled. +#define cwEnaBits(f,m,b) ((b) ? cwSetBits(f,m) : cwClrBits(f,m)) // Set or clear bits in 'f' based on bits in 'm' and the state of 'b'. + + + +namespace cw +{ + + +#define cwAssert(cond) while(1){ if(!(cond)){ cwLogFatal(kAssertFailRC,"Assert failed on condition:%s",#cond ); } break; } + + + template< typename H, typename T > T* handleToPtr( H h ) + { + cwAssert( h.p != nullptr ); + return h.p; + } + + typedef struct idLabelPair_str + { + unsigned id; + const char* label; + } idLabelPair_t; + + const char* idToLabel( const idLabelPair_t* array, unsigned id, unsigned eolId ); + unsigned labelToId( const idLabelPair_t* array, const char* label, unsigned eolId ); + + void sleepSec( unsigned secs ); // sleep seconds + void sleepMs( unsigned ms ); // sleep milliseconds + void sleepUs( unsigned us ); // sleep seconds + void sleepNs( unsigned ns ); // sleep nanoseconds + + +} + +#endif diff --git a/cwFile.cpp b/cwFile.cpp new file mode 100644 index 0000000..7da5500 --- /dev/null +++ b/cwFile.cpp @@ -0,0 +1,799 @@ +#include "cwCommon.h" +#include "cwLog.h" +#include "cwCommonImpl.h" +#include "cwMem.h" +#include "cwFileSys.h" +#include "cwText.h" +#include "cwFile.h" + +#ifdef cwLINUX +#include +#endif + + +namespace cw +{ + + typedef struct file_str + { + FILE* fp; + char* fnStr; + } file_t; + + +#define _fileHandleToPtr(h) handleToPtr(h) + +char* _fileToBuf( fileH_t h, unsigned nn, unsigned* bufByteCntPtr ) +{ + errno = 0; + + unsigned n = fileByteCount(h); + char* buf = nullptr; + + // if the file size calculation is ok + if( errno != 0 ) + { + cwLogSysError(kOpFailRC,errno,"Invalid file buffer length on '%s'.", cwStringNullGuard(fileName(h))); + goto errLabel; + } + + // allocate the read target buffer + if((buf = memAlloc(n+nn)) == nullptr) + { + cwLogError(kMemAllocFailRC,"Read buffer allocation failed."); + goto errLabel; + } + + // read the file + if( fileRead(h,buf,n) != kOkRC ) + goto errLabel; + + // zero memory after the file data + memset(buf+n,0,nn); + + if( bufByteCntPtr != nullptr ) + *bufByteCntPtr = n; + + return buf; + + errLabel: + if( bufByteCntPtr != nullptr ) + *bufByteCntPtr = 0; + + memRelease(buf); + + return nullptr; + +} + +char* _fileFnToBuf( const char* fn, unsigned nn, unsigned* bufByteCntPtr ) +{ + fileH_t h; + char* buf = nullptr; + + if( fileOpen(h,fn,kReadFileFl | kBinaryFileFl) != kOkRC ) + goto errLabel; + + buf = _fileToBuf(h,nn,bufByteCntPtr); + + errLabel: + fileClose(h); + + return buf; +} + + rc_t _fileGetLine( file_t* p, char* buf, unsigned* bufByteCntPtr ) +{ + // fgets() reads up to n-1 bytes into buf[] + if( fgets(buf,*bufByteCntPtr,p->fp) == nullptr ) + { + // an read error or EOF condition occurred + *bufByteCntPtr = 0; + + if( !feof(p->fp ) ) + return cwLogSysError(kReadFailRC,errno,"File read line failed"); + + return kReadFailRC; + } + + return kOkRC; +} + +} + + +cw::rc_t cw::fileOpen( fileH_t& hRef, const char* fn, unsigned flags ) +{ + char mode[] = "/0/0/0"; + file_t* p = nullptr; + rc_t rc; + + if((rc = fileClose(hRef)) != kOkRC ) + return rc; + + if( cwIsFlag(flags,kReadFileFl) ) + mode[0] = 'r'; + else + if( cwIsFlag(flags,kWriteFileFl) ) + mode[0] = 'w'; + else + if( cwIsFlag(flags,kAppendFileFl) ) + mode[0] = 'a'; + else + cwLogError(kInvalidArgRC,"File open flags must contain 'kReadFileFl','kWriteFileFl', or 'kAppendFileFl'."); + + if( cwIsFlag(flags,kUpdateFileFl) ) + mode[1] = '+'; + + // handle requests to use stdin,stdout,stderr + FILE* sfp = nullptr; + if( cwIsFlag(flags,kStdoutFileFl) ) + { + sfp = stdout; + fn = "stdout"; + } + else + if( cwIsFlag(flags,kStderrFileFl) ) + { + sfp = stderr; + fn = "stderr"; + } + else + if( cwIsFlag(flags,kStdinFileFl) ) + { + sfp = stdin; + fn = "stdin"; + } + + + if( fn == nullptr ) + return cwLogError(kInvalidArgRC,"File object allocation failed due to empty file name."); + + unsigned byteCnt = sizeof(file_t) + strlen(fn) + 1; + + if((p = memAllocZ(byteCnt)) == nullptr ) + return cwLogError(kOpFailRC,"File object allocation failed for file '%s'.",cwStringNullGuard(fn)); + + p->fnStr = (char*)(p+1); + strcpy(p->fnStr,fn); + + if( sfp != nullptr ) + p->fp = sfp; + else + { + errno = 0; + if((p->fp = fopen(fn,mode)) == nullptr ) + { + rc_t rc = cwLogSysError(kOpenFailRC,errno,"File open failed on file:'%s'.",cwStringNullGuard(fn)); + memRelease(p); + return rc; + } + } + + hRef.set(p); + + return kOkRC; +} + +cw::rc_t cw::fileClose( fileH_t& hRef ) +{ + if( fileIsValid(hRef) == false ) + return kOkRC; + + file_t* p = _fileHandleToPtr(hRef); + + errno = 0; + if( p->fp != nullptr ) + if( fclose(p->fp) != 0 ) + return cwLogSysError(kCloseFailRC,errno,"File close failed on '%s'.", cwStringNullGuard(p->fnStr)); + + memRelease(p); + hRef.set(nullptr); + + return kOkRC; +} + +bool cw::fileIsValid( fileH_t h ) +{ return h.isValid(); } + +cw::rc_t cw::fileRead( fileH_t h, void* buf, unsigned bufByteCnt ) +{ + file_t* p = _fileHandleToPtr(h); + + errno = 0; + if( fread(buf,bufByteCnt,1,p->fp) != 1 ) + return cwLogSysError(kReadFailRC,errno,"File read failed on '%s'.", cwStringNullGuard(p->fnStr)); + + return kOkRC; +} + +cw::rc_t cw::fileWrite( fileH_t h, const void* buf, unsigned bufByteCnt ) +{ + file_t* p = _fileHandleToPtr(h); + + errno = 0; + if( fwrite(buf,bufByteCnt,1,p->fp) != 1 ) + return cwLogSysError(kWriteFailRC,errno,"File write failed on '%s'.", cwStringNullGuard(p->fnStr)); + + return kOkRC; +} + +cw::rc_t cw::fileSeek( fileH_t h, enum fileSeekFlags_t flags, int offsByteCnt ) +{ + file_t* p = _fileHandleToPtr(h); + unsigned fileflags = 0; + + if( cwIsFlag(flags,kBeginFileFl) ) + fileflags = SEEK_SET; + else + if( cwIsFlag(flags,kCurFileFl) ) + fileflags = SEEK_CUR; + else + if( cwIsFlag(flags,kEndFileFl) ) + fileflags = SEEK_END; + else + return cwLogError(kInvalidArgRC,"Invalid file seek flag on '%s'.",cwStringNullGuard(p->fnStr)); + + errno = 0; + if( fseek(p->fp,offsByteCnt,fileflags) != 0 ) + return cwLogSysError(kSeekFailRC,errno,"File seek failed on '%s'",cwStringNullGuard(p->fnStr)); + + return kOkRC; +} + +cw::rc_t cw::fileTell( fileH_t h, long* offsPtr ) +{ + cwAssert( offsPtr != nullptr ); + *offsPtr = -1; + file_t* p = _fileHandleToPtr(h); + errno = 0; + + if((*offsPtr = ftell(p->fp)) == -1) + return cwLogSysError(kOpFailRC,errno,"File tell failed on '%s'.", cwStringNullGuard(p->fnStr)); + return kOkRC; +} + + +bool cw::fileEof( fileH_t h ) +{ return feof( _fileHandleToPtr(h)->fp ) != 0; } + + +unsigned cw::fileByteCount( fileH_t h ) +{ + struct stat sr; + int f; + file_t* p = _fileHandleToPtr(h); + const char errMsg[] = "File byte count request failed."; + + errno = 0; + + if((f = fileno(p->fp)) == -1) + { + cwLogSysError(kInvalidOpRC,errno,"%s because fileno() failed on '%s'.",errMsg,cwStringNullGuard(p->fnStr)); + return 0; + } + + if(fstat(f,&sr) == -1) + { + cwLogSysError(kInvalidOpRC,errno,"%s because fstat() failed on '%s'.",errMsg,cwStringNullGuard(p->fnStr)); + return 0; + } + + return sr.st_size; +} + +cw::rc_t cw::fileByteCountFn( const char* fn, unsigned* fileByteCntPtr ) +{ + cwAssert( fileByteCntPtr != nullptr ); + rc_t rc; + fileH_t h; + + if((rc = fileOpen(h,fn,kReadFileFl)) != kOkRC ) + return rc; + + if( fileByteCntPtr != nullptr) + *fileByteCntPtr = fileByteCount(h); + + fileClose(h); + + return rc; +} + +cw::rc_t cw::fileCompare( const char* fn0, const char* fn1, bool& isEqualRef ) +{ + rc_t rc = kOkRC; + unsigned bufByteCnt = 2048; + fileH_t h0; + fileH_t h1; + file_t* p0 = nullptr; + file_t* p1 = nullptr; + char b0[ bufByteCnt ]; + char b1[ bufByteCnt ]; + + isEqualRef = true; + + if((rc = fileOpen(h0,fn0,kReadFileFl)) != kOkRC ) + goto errLabel; + + if((rc = fileOpen(h1,fn1,kReadFileFl)) != kOkRC ) + goto errLabel; + + p0 = _fileHandleToPtr(h0); + p1 = _fileHandleToPtr(h1); + + while(1) + { + size_t n0 = fread(b0,1,bufByteCnt,p0->fp); + size_t n1 = fread(b1,1,bufByteCnt,p1->fp); + if( n0 != n1 || memcmp(b0,b1,n0) != 0 ) + { + isEqualRef = false; + break; + } + + if( n0 != bufByteCnt || n1 != bufByteCnt ) + break; + } + + errLabel: + fileClose(h0); + fileClose(h1); + return rc; +} + + +const char* cw::fileName( fileH_t h ) +{ + file_t* p = _fileHandleToPtr(h); + return p->fnStr; +} + +cw::rc_t cw::fileFnWrite( const char* fn, const void* buf, unsigned bufByteCnt ) +{ + fileH_t h; + rc_t rc; + + if((rc = fileOpen(h,fn,kWriteFileFl)) != kOkRC ) + goto errLabel; + + rc = fileWrite(h,buf,bufByteCnt); + + errLabel: + fileClose(h); + + return rc; +} + + +cw::rc_t cw::fileCopy( + const char* srcDir, + const char* srcFn, + const char* srcExt, + const char* dstDir, + const char* dstFn, + const char* dstExt) +{ + rc_t rc = kOkRC; + unsigned byteCnt = 0; + char* buf = nullptr; + char* srcPathFn = nullptr; + char* dstPathFn = nullptr; + + // form the source path fn + if((srcPathFn = fileSysMakeFn(srcDir,srcFn,srcExt,nullptr)) == nullptr ) + { + rc = cwLogError(kOpFailRC,"The soure file name for dir:%s name:%s ext:%s could not be formed.",cwStringNullGuard(srcDir),cwStringNullGuard(srcFn),cwStringNullGuard(srcExt)); + goto errLabel; + } + + // form the dest path fn + if((dstPathFn = fileSysMakeFn(dstDir,dstFn,dstExt,nullptr)) == nullptr ) + { + rc = cwLogError(kOpFailRC,"The destination file name for dir:%s name:%s ext:%s could not be formed.",cwStringNullGuard(dstDir),cwStringNullGuard(dstFn),cwStringNullGuard(dstExt)); + goto errLabel; + } + + // verify that the source exists + if( fileSysIsFile(srcPathFn) == false ) + { + rc = cwLogError(kOpenFailRC,"The source file '%s' does not exist.",cwStringNullGuard(srcPathFn)); + goto errLabel; + } + + // read the source file into a buffer + if((buf = fileFnToBuf(srcPathFn,&byteCnt)) == nullptr ) + rc = cwLogError(kReadFailRC,"Attempt to fill a buffer from '%s' failed.",cwStringNullGuard(srcPathFn)); + else + { + // write the file to the output file + if( fileFnWrite(dstPathFn,buf,byteCnt) != kOkRC ) + rc = cwLogError(kWriteFailRC,"An attempt to write a buffer to '%s' failed.",cwStringNullGuard(dstPathFn)); + } + + errLabel: + // free the buffer + memRelease(buf); + memRelease(srcPathFn); + memRelease(dstPathFn); + return rc; + +} + +cw::rc_t cw::fileBackup( const char* dir, const char* name, const char* ext ) +{ + rc_t rc = kOkRC; + char* newName = nullptr; + char* newFn = nullptr; + unsigned n = 0; + char* srcFn = nullptr; + fileSysPathPart_t* pp = nullptr; + + // form the name of the backup file + if((srcFn = fileSysMakeFn(dir,name,ext,nullptr)) == nullptr ) + { + rc = cwLogError(kOpFailRC,"Backup source file name formation failed."); + goto errLabel; + } + + // if the src file does not exist then there is nothing to do + if( fileSysIsFile(srcFn) == false ) + return rc; + + // break the source file name up into dir/fn/ext. + if((pp = fileSysPathParts(srcFn)) == nullptr || pp->fnStr==nullptr) + { + rc = cwLogError(kOpFailRC,"The file name '%s' could not be parsed into its parts.",cwStringNullGuard(srcFn)); + goto errLabel; + } + + // iterate until a unique file name is found + for(n=0; 1; ++n) + { + memRelease(newFn); + + // generate a new file name + newName = memPrintf(newName,"%s_%i",pp->fnStr,n); + + // form the new file name into a complete path + if((newFn = fileSysMakeFn(pp->dirStr,newName,pp->extStr,nullptr)) == nullptr ) + { + rc = cwLogError(kOpFailRC,"A backup file name could not be formed for the file '%s'.",cwStringNullGuard(newName)); + goto errLabel; + } + + // if the new file name is not already in use ... + if( fileSysIsFile(newFn) == false ) + { + // .. then duplicate the file + if((rc = fileCopy(srcFn,nullptr,nullptr,newFn,nullptr,nullptr)) != kOkRC ) + rc = cwLogError(rc,"The file '%s' could not be duplicated as '%s'.",cwStringNullGuard(srcFn),cwStringNullGuard(newFn)); + + break; + } + + + } + + errLabel: + + memRelease(srcFn); + memRelease(newFn); + memRelease(newName); + memRelease(pp); + + return rc; + +} + + +char* cw::fileToBuf( fileH_t h, unsigned* bufByteCntPtr ) +{ return _fileToBuf(h,0,bufByteCntPtr); } + +char* cw::fileFnToBuf( const char* fn, unsigned* bufByteCntPtr ) +{ return _fileFnToBuf(fn,0,bufByteCntPtr); } + +char* cw::fileToStr( fileH_t h, unsigned* bufByteCntPtr ) +{ return _fileToBuf(h,1,bufByteCntPtr); } + +char* cw::fileFnToStr( const char* fn, unsigned* bufByteCntPtr ) +{ return _fileFnToBuf(fn,1,bufByteCntPtr); } + +cw::rc_t cw::fileLineCount( fileH_t h, unsigned* lineCntPtr ) +{ + rc_t rc = kOkRC; + file_t* p = _fileHandleToPtr(h); + unsigned lineCnt = 0; + long offs; + int c; + + + cwAssert( lineCntPtr != nullptr ); + *lineCntPtr = 0; + + if((rc = fileTell(h,&offs)) != kOkRC ) + return rc; + + errno = 0; + + while(1) + { + c = fgetc(p->fp); + + if( c == EOF ) + { + if( errno ) + rc = cwLogSysError(kReadFailRC,errno,"File read char failed on 's'.", cwStringNullGuard(fileName(h))); + else + ++lineCnt; // add one in case the last line isn't terminated with a '\n'. + + break; + } + + // if an end-of-line was encountered + if( c == '\n' ) + ++lineCnt; + + } + + if((rc = fileSeek(h,kBeginFileFl,offs)) != kOkRC ) + return rc; + + *lineCntPtr = lineCnt; + + return rc; +} + + +cw::rc_t cw::fileGetLine( fileH_t h, char* buf, unsigned* bufByteCntPtr ) +{ + cwAssert( bufByteCntPtr != nullptr ); + file_t* p = _fileHandleToPtr(h); + unsigned tn = 128; + char t[ tn ]; + unsigned on = *bufByteCntPtr; + long offs; + rc_t rc; + + // store the current file offset + if((rc = fileTell(h,&offs)) != kOkRC ) + return rc; + + // if no buffer was given then use t[] + if( buf == nullptr || *bufByteCntPtr == 0 ) + { + *bufByteCntPtr = tn; + buf = t; + } + + // fill the buffer from the current line + if((rc = _fileGetLine(p,buf,bufByteCntPtr)) != kOkRC ) + return rc; + + // get length of the string in the buffer + // (this is one less than the count of bytes written to the buffer) + unsigned n = strlen(buf); + + // if the provided buffer was large enough to read the entire string + if( on > n+1 ) + { + //*bufByteCntPtr = n+1; + return kOkRC; + } + + // + // the provided buffer was not large enough + // + + // m tracks the length of the string + unsigned m = n; + + while( n+1 == *bufByteCntPtr ) + { + // fill the buffer from the current line + if((rc = _fileGetLine(p,buf,bufByteCntPtr)) != kOkRC ) + return rc; + + n = strlen(buf); + m += n; + } + + // restore the original file offset + if((rc = fileSeek(h,kBeginFileFl,offs)) != kOkRC ) + return rc; + + // add 1 for /0, 1 for /n and 1 to detect buf-too-short + *bufByteCntPtr = m+3; + + return kBufTooSmallRC; + +} + +cw::rc_t cw::fileGetLineAuto( fileH_t h, char** bufPtrPtr, unsigned* bufByteCntPtr ) +{ + rc_t rc = kOkRC; + bool fl = true; + char* buf = *bufPtrPtr; + + *bufPtrPtr = nullptr; + + while(fl) + { + fl = false; + + switch( rc = fileGetLine(h,buf,bufByteCntPtr) ) + { + case kOkRC: + { + *bufPtrPtr = buf; + } + break; + + case kBufTooSmallRC: + buf = memResizeZ(buf,*bufByteCntPtr); + fl = true; + break; + + default: + memRelease(buf); + break; + } + } + + + + return rc; +} + +cw::rc_t cw::fileReadChar( fileH_t h, char* buf, unsigned cnt ) +{ return fileRead(h,buf,sizeof(buf[0])*cnt); } + +cw::rc_t cw::fileReadUChar( fileH_t h, unsigned char* buf, unsigned cnt ) +{ return fileRead(h,buf,sizeof(buf[0])*cnt); } + +cw::rc_t cw::fileReadShort( fileH_t h, short* buf, unsigned cnt ) +{ return fileRead(h,buf,sizeof(buf[0])*cnt); } + +cw::rc_t cw::fileReadUShort( fileH_t h, unsigned short* buf, unsigned cnt ) +{ return fileRead(h,buf,sizeof(buf[0])*cnt); } + +cw::rc_t cw::fileReadLong( fileH_t h, long* buf, unsigned cnt ) +{ return fileRead(h,buf,sizeof(buf[0])*cnt); } + +cw::rc_t cw::fileReadULong( fileH_t h, unsigned long* buf, unsigned cnt ) +{ return fileRead(h,buf,sizeof(buf[0])*cnt); } + +cw::rc_t cw::fileReadInt( fileH_t h, int* buf, unsigned cnt ) +{ return fileRead(h,buf,sizeof(buf[0])*cnt); } + +cw::rc_t cw::fileReadUInt( fileH_t h, unsigned int* buf, unsigned cnt ) +{ return fileRead(h,buf,sizeof(buf[0])*cnt); } + +cw::rc_t cw::fileReadFloat( fileH_t h, float* buf, unsigned cnt ) +{ return fileRead(h,buf,sizeof(buf[0])*cnt); } + +cw::rc_t cw::fileReadDouble( fileH_t h, double* buf, unsigned cnt ) +{ return fileRead(h,buf,sizeof(buf[0])*cnt); } + +cw::rc_t cw::fileReadBool( fileH_t h, bool* buf, unsigned cnt ) +{ return fileRead(h,buf,sizeof(buf[0])*cnt); } + + + +cw::rc_t cw::fileWriteChar( fileH_t h, const char* buf, unsigned cnt ) +{ return fileWrite(h,buf,sizeof(buf[0])*cnt); } + +cw::rc_t cw::fileWriteUChar( fileH_t h, const unsigned char* buf, unsigned cnt ) +{ return fileWrite(h,buf,sizeof(buf[0])*cnt); } + +cw::rc_t cw::fileWriteShort( fileH_t h, const short* buf, unsigned cnt ) +{ return fileWrite(h,buf,sizeof(buf[0])*cnt); } + +cw::rc_t cw::fileWriteUShort( fileH_t h, const unsigned short* buf, unsigned cnt ) +{ return fileWrite(h,buf,sizeof(buf[0])*cnt); } + +cw::rc_t cw::fileWriteLong( fileH_t h, const long* buf, unsigned cnt ) +{ return fileWrite(h,buf,sizeof(buf[0])*cnt); } + +cw::rc_t cw::fileWriteULong( fileH_t h, const unsigned long* buf, unsigned cnt ) +{ return fileWrite(h,buf,sizeof(buf[0])*cnt); } + +cw::rc_t cw::fileWriteInt( fileH_t h, const int* buf, unsigned cnt ) +{ return fileWrite(h,buf,sizeof(buf[0])*cnt); } + +cw::rc_t cw::fileWriteUInt( fileH_t h, const unsigned int* buf, unsigned cnt ) +{ return fileWrite(h,buf,sizeof(buf[0])*cnt); } + +cw::rc_t cw::fileWriteFloat( fileH_t h, const float* buf, unsigned cnt ) +{ return fileWrite(h,buf,sizeof(buf[0])*cnt); } + +cw::rc_t cw::fileWriteDouble( fileH_t h, const double* buf, unsigned cnt ) +{ return fileWrite(h,buf,sizeof(buf[0])*cnt); } + +cw::rc_t cw::fileWriteBool( fileH_t h, const bool* buf, unsigned cnt ) +{ return fileWrite(h,buf,sizeof(buf[0])*cnt); } + + +cw::rc_t cw::fileWriteStr( fileH_t h, const char* s ) +{ + rc_t rc; + + unsigned n = textLength(s); + + if((rc = fileWriteUInt(h,&n,1)) != kOkRC ) + return rc; + + if( n > 0 ) + rc = fileWriteChar(h,s,n); + return rc; +} + + +cw::rc_t cw::fileReadStr( fileH_t h, char** sRef, unsigned maxCharN ) +{ + unsigned n; + rc_t rc; + + cwAssert(sRef != nullptr ); + + *sRef = nullptr; + + if( maxCharN == 0 ) + maxCharN = 16384; + + // read the string length + if((rc = fileReadUInt(h,&n,1)) != kOkRC ) + return rc; + + // verify that string isn't too long + if( n > maxCharN ) + { + return cwLogError(kInvalidArgRC,"The stored string is larger than the maximum allowable size of %i.",maxCharN); + } + + // allocate a read buffer + char* s = memAllocZ(n+1); + + // fill the buffer from the file + if((rc = fileReadChar(h,s,n)) != kOkRC ) + return rc; + + s[n] = 0; // terminate the string + + *sRef = s; + + return rc; +} + + +cw::rc_t cw::filePrint( fileH_t h, const char* text ) +{ + file_t* p = _fileHandleToPtr(h); + + errno = 0; + if( fputs(text,p->fp) < 0 ) + return cwLogSysError(kOpFailRC,errno,"File print failed on '%s'.", cwStringNullGuard(fileName(h))); + + return kOkRC; +} + + +cw::rc_t cw::fileVPrintf( fileH_t h, const char* fmt, va_list vl ) +{ + file_t* p = _fileHandleToPtr(h); + + if( vfprintf(p->fp,fmt,vl) < 0 ) + return cwLogSysError(kOpFailRC,errno,"File print failed on '%s'.", cwStringNullGuard(fileName(h))); + + return kOkRC; +} + +cw::rc_t cw::filePrintf( fileH_t h, const char* fmt, ... ) +{ + va_list vl; + va_start(vl,fmt); + rc_t rc = fileVPrintf(h,fmt,vl); + va_end(vl); + return rc; +} + + diff --git a/cwFile.h b/cwFile.h new file mode 100644 index 0000000..cd3734d --- /dev/null +++ b/cwFile.h @@ -0,0 +1,212 @@ +#ifndef cwFile_H +#define cwFile_H + +namespace cw +{ + typedef handle fileH_t; + + // Flags for use with fileOpen(). + enum fileOpenFlags_t + { + kReadFileFl = 0x01, //< Open a file for reading + kWriteFileFl = 0x02, //< Create an empty file for writing + kAppendFileFl = 0x04, //< Open a file for writing at the end of the file. + kUpdateFileFl = 0x08, //< Open a file for reading and writing. + kBinaryFileFl = 0x10, //< Open a file for binary (not text) input/output. + kStdoutFileFl = 0x20, //< Ignore fn use 'stdout' + kStderrFileFl = 0x40, //< Ignore fn use 'stderr' + kStdinFileFl = 0x80, //< Ignore fn use 'stdin' + }; + + // Open or create a file. + // Equivalent to fopen(). + // If *hp was not initalized by an earlier call to fileOpen() then it should + // be set to fileNullHandle prior to calling this function. If *hp is a valid handle + // then it is automatically finalized by an internal call to fileClose() prior to + // being re-iniitalized. + // + // If kStdoutFileFl, kStderrFileFl or kStdinFileFl are set then + // file name argument 'fn' is ignored. + rc_t fileOpen( + fileH_t& hRef, // Pointer to a client supplied fileHandle_t to recieve the handle for the new object. + const char* fn, // The name of the file to open or create. + unsigned flags ); // See fileOpenFlags_t + + // Close a file opened with Equivalent to fclose(). + rc_t fileClose( fileH_t& hRef ); + + // Return true if the file handle is associated with an open file. + bool fileIsValid( fileH_t h ); + + // Read a block bytes from a file. Equivalent to fread(). + rc_t fileRead( fileH_t h, void* buf, unsigned bufByteCnt ); + + // Write a block of bytes to a file. Equivalent to fwrite(). + rc_t fileWrite( fileH_t h, const void* buf, unsigned bufByteCnt ); + + enum fileSeekFlags_t + { + kBeginFileFl = 0x01, + kCurFileFl = 0x02, + kEndFileFl = 0x04 + }; + + // Set the file position indicator. Equivalent to fseek(). + rc_t fileSeek( fileH_t h, enum fileSeekFlags_t flags, int offsByteCnt ); + + // Return the file position indicator. Equivalent to ftell(). + rc_t fileTell( fileH_t h, long* offsPtr ); + + // Return true if the file position indicator is at the end of the file. + // Equivalent to feof(). + bool fileEof( fileH_t h ); + + // Return the length of the file in bytes + unsigned fileByteCount( fileH_t h ); + rc_t fileByteCountFn( const char* fn, unsigned* fileByteCntPtr ); + + // Set *isEqualPtr=true if the two files are identical. + rc_t fileCompare( const char* fn0, const char* fn1, bool& isEqualFlRef ); + + // Return the file name associated with a file handle. + const char* fileName( fileH_t h ); + + // Write a buffer to a file. + rc_t fileFnWrite( const char* fn, const void* buf, unsigned bufByteCnt ); + + // Allocate and fill a buffer from the file. + // Set *bufByteCntPtr to count of bytes read into the buffer. + // 'bufByteCntPtr' is optional - set it to nullptr if it is not required by the caller. + // It is the callers responsibility to delete the returned buffer with a + // call to cmMemFree() + char* fileToBuf( fileH_t h, unsigned* bufByteCntPtr ); + + // Same as fileToBuf() but accepts a file name argument. + char* fileFnToBuf( const char* fn, unsigned* bufByteCntPtr ); + + + // Copy the file named in srcDir/srcFn/srcExt to a file named dstDir/dstFn/dstExt. + // Note that srcExt/dstExt may be set to nullptr if the file extension is included + // in srcFn/dstFn. Likewise srcFn/dstFn may be set to nullptr if the file name + // is included in srcDir/dstDir. + rc_t fileCopy( + const char* srcDir, + const char* srcFn, + const char* srcExt, + const char* dstDir, + const char* dstFn, + const char* dstExt); + + + // This function creates a backup copy of the file 'fn' by duplicating it into + // a file named fn_#.ext where # is an integer which makes the file name unique. + // The integers chosen with zero and are incremented until an + // unused file name is found in the same directory as 'fn'. + // If the file identified by 'fn' is not found then the function returns quietly. + rc_t fileBackup( const char* dir, const char* name, const char* ext ); + + // Allocate and fill a zero terminated string from a file. + // Set *bufByteCntPtr to count of bytes read into the buffer.= + // (the buffer memory size is one byte larger to account for the terminating zero) + // 'bufByteCntPtr' is optional - set it to nullptr if it is not required by the caller. + // It is the callers responsibility to delete the returned buffer with a + // call to cmMemFree() + char* fileToStr( fileH_t h, unsigned* bufByteCntPtr ); + + // Same as fileToBuf() but accepts a file name argument. + char* fileFnToStr( const char* fn, unsigned* bufByteCntPtr ); + + // Return the count of lines in a file. + rc_t fileLineCount( fileH_t h, unsigned* lineCntPtr ); + + // Read the next line into buf[bufByteCnt]. + // Consider using fileGetLineAuto() as an alternative to this function + // to avoid having to use a buffer with an explicit size. + // + // If buf is not long enough to hold the entire string then + // + // 1. The function returns kFileBufTooSmallRC + // 2. *bufByteCntPtr is set to the size of the required buffer. + // 3. The internal file position is left unchanged. + // + // If the buffer is long enough to hold the entire line then + // *bufByteCntPtr is left unchanged. + // See fileGetLineTest() in cmProcTest.c or fileGetLineAuto() + // in file.c for examples of how to use this function to a + // achieve proper buffer sizing. + rc_t fileGetLine( fileH_t h, char* buf, unsigned* bufByteCntPtr ); + + // A version of fileGetLine() which eliminates the need to handle buffer + // sizing. + // + // Example usage: + // + // char* buf = nullptr; + // unsigned bufByteCnt = 0; + // while(fileGetLineAuto(h,&buf,&bufByteCnt)==kOkFileRC) + // proc(buf); + // cmMemPtrFree(buf); + // + // On the first call to this function *bufPtrPtr must be set to nullptr and + // *bufByteCntPtr must be set to 0. + // Following the last call to this function call cmMemPtrFree(bufPtrptr) + // to be sure the line buffer is fully released. Note this step is not + // neccessary if the last call does not return kOkFileRC. + rc_t fileGetLineAuto( fileH_t h, char** bufPtrPtr, unsigned* bufByteCntPtr ); + + // Binary Array Reading Functions + // Each of these functions reads a block of binary data from a file. + // The advantage to using these functions over fileRead() is only that they are type specific. + rc_t fileReadChar( fileH_t h, char* buf, unsigned cnt ); + rc_t fileReadUChar( fileH_t h, unsigned char* buf, unsigned cnt ); + rc_t fileReadShort( fileH_t h, short* buf, unsigned cnt ); + rc_t fileReadUShort( fileH_t h, unsigned short* buf, unsigned cnt ); + rc_t fileReadLong( fileH_t h, long* buf, unsigned cnt ); + rc_t fileReadULong( fileH_t h, unsigned long* buf, unsigned cnt ); + rc_t fileReadInt( fileH_t h, int* buf, unsigned cnt ); + rc_t fileReadUInt( fileH_t h, unsigned int* buf, unsigned cnt ); + rc_t fileReadFloat( fileH_t h, float* buf, unsigned cnt ); + rc_t fileReadDouble( fileH_t h, double* buf, unsigned cnt ); + rc_t fileReadBool( fileH_t h, bool* buf, unsigned cnt ); + + // Binary Array Writing Functions + // Each of these functions writes an array to a binary file. + // The advantage to using functions rather than fileWrite() is only that they are type specific. + rc_t fileWriteChar( fileH_t h, const char* buf, unsigned cnt ); + rc_t fileWriteUChar( fileH_t h, const unsigned char* buf, unsigned cnt ); + rc_t fileWriteShort( fileH_t h, const short* buf, unsigned cnt ); + rc_t fileWriteUShort( fileH_t h, const unsigned short* buf, unsigned cnt ); + rc_t fileWriteLong( fileH_t h, const long* buf, unsigned cnt ); + rc_t fileWriteULong( fileH_t h, const unsigned long* buf, unsigned cnt ); + rc_t fileWriteInt( fileH_t h, const int* buf, unsigned cnt ); + rc_t fileWriteUInt( fileH_t h, const unsigned int* buf, unsigned cnt ); + rc_t fileWriteFloat( fileH_t h, const float* buf, unsigned cnt ); + rc_t fileWriteDouble( fileH_t h, const double* buf, unsigned cnt ); + rc_t fileWriteBool( fileH_t h, const bool* buf, unsigned cnt ); + + // Write a string to a file as ... + // where N is the count of characters in the string. + rc_t fileWriteStr( fileH_t h, const char* s ); + + // Read a string back from a file as written by fileWriteStr(). + // Note that the string will by string will be dynamically allocated + // and threfore must eventually be released via cmMemFree(). + // If maxCharN is set to zero then the default maximum string + // length is 16384. Note that this limit is used to prevent + // corrupt files from generating excessively long strings. + rc_t fileReadStr( fileH_t h, char** sRef, unsigned maxCharN ); + + // Formatted Text Output Functions: + // Print formatted text to a file. + rc_t filePrint( fileH_t h, const char* text ); + rc_t filePrintf( fileH_t h, const char* fmt, ... ); + rc_t fileVPrintf( fileH_t h, const char* fmt, va_list vl ); + + + +} + + + + +#endif diff --git a/cwFileSys.cpp b/cwFileSys.cpp new file mode 100644 index 0000000..6cf3d43 --- /dev/null +++ b/cwFileSys.cpp @@ -0,0 +1,345 @@ +#include "cwCommon.h" +#include "cwLog.h" + +#include "cwFileSys.h" +#include "cwCommonImpl.h" +#include "cwMem.h" + +#ifdef cwLINUX +#include +#include +#endif + +namespace cw +{ + + bool _fileSysConcat( char* rp, unsigned rn, char sepChar, const char* suffixStr ) + { + unsigned m = strlen(rp); + + // m==0 if no sep needs to be inserted or removed + + //if( m == 0 ) + // return false; + + if( m != 0 ) + { + + // if a sep char needs to be inserted + if( rp[m-1] != sepChar && suffixStr[0] != sepChar ) + { + cwAssert((m+1)=rn) + return false; + + rp[m] = sepChar; + rp[m+1]= 0; + ++m; + } + else + // if a sep char needs to be removed + if( rp[m-1] == sepChar && suffixStr[0] == sepChar ) + { + rp[m-1] = 0; + --m; + } + + } + + cwAssert( rn>=m && strlen(rp)+strlen(suffixStr) <= rn ); + strncat(rp,suffixStr,rn-m); + + return true; + } +} + +bool cw::fileSysIsDir( const char* dirStr ) +{ + struct stat s; + + errno = 0; + + if( stat(dirStr,&s) != 0 ) + { + // if the dir does not exist + if( errno == ENOENT ) + return false; + + cwLogSysError( kOpFailRC, errno, "'stat' failed on '%s'",dirStr); + return false; + } + + return S_ISDIR(s.st_mode); +} + +bool cw::fileSysIsFile( const char* fnStr ) +{ + struct stat s; + errno = 0; + + if( stat(fnStr,&s) != 0 ) + { + + // if the file does not exist + if( errno == ENOENT ) + return false; + + cwLogSysError( kOpFailRC, errno, "'stat' failed on '%s'.",fnStr); + return false; + } + + return S_ISREG(s.st_mode); +} + + +bool cw::fileSysIsLink( const char* fnStr ) +{ + struct stat s; + errno = 0; + + if( lstat(fnStr,&s) != 0 ) + { + // if the file does not exist + if( errno == ENOENT ) + return false; + + cwLogSysError( kOpFailRC, errno, "'stat' failed on '%s'.",fnStr); + return false; + } + + return S_ISLNK(s.st_mode); +} + + + +char* cw::fileSysVMakeFn( const char* dir, const char* fn, const char* ext, va_list vl ) +{ + rc_t rc = kOkRC; + char* rp = nullptr; + const char* dp = nullptr; + unsigned n = 0; + char pathSep = cwPathSeparatorChar; + char extSep = '.'; + va_list vl_t; + va_copy(vl_t,vl); + + // get prefix directory length + if( dir != nullptr ) + n += strlen(dir) + 1; // add 1 for ending sep + + // get file name length + if( fn != nullptr ) + n += strlen(fn); + + // get extension length + if( ext != nullptr ) + n += strlen(ext) + 1; // add 1 for period + + // get length of all var args dir's + 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 + + if((rp = memAllocZ( n+1 )) == nullptr ) + { + rc = cwLogError(kMemAllocFailRC,"Unable to allocate file name memory."); + goto errLabel; + } + + va_copy(vl,vl_t); + + rp[n] = 0; + rp[0] = 0; + + // copy out the prefix dir + if( dir != nullptr ) + strncat(rp,dir,n-strlen(rp)); + + // copy out each of the var arg's directories + while((dp = va_arg(vl,const char*)) != nullptr ) + if(!_fileSysConcat(rp,n,pathSep,dp) ) + { + cwAssert(0); + goto errLabel; + } + + + // copy out the file name + if( fn != nullptr ) + if(!_fileSysConcat(rp,n,pathSep,fn)) + { + cwAssert(0); + goto errLabel; + } + + // copy out the extension + if( ext != nullptr ) + if(!_fileSysConcat(rp,n,extSep,ext)) + { + cwAssert(0); + goto errLabel; + } + + cwAssert(strlen(rp)<=n); + + errLabel: + + if( rc != kOkRC && rp != nullptr ) + memRelease( rp ); + + return rp; +} + + +char* cw::fileSysMakeFn( const char* dir, const char* fn, const char* ext, ... ) +{ + va_list vl; + va_start(vl,ext); + char* fnOut = fileSysVMakeFn(dir,fn,ext,vl); + va_end(vl); + return fnOut; +} + + + +cw::fileSysPathPart_t* cw::fileSysPathParts( const char* pathStr ) +{ + unsigned n = 0; // char's in pathStr + unsigned dn = 0; // char's in the dir part + unsigned fn = 0; // char's in the name part + unsigned en = 0; // char's in the ext part + char* cp = nullptr; + fileSysPathPart_t* rp = nullptr; + + + if( pathStr==nullptr ) + return nullptr; + + // skip leading white space + for(; *pathStr; ++pathStr ) + if( !isspace(*pathStr ) ) + break; + + + // get the length of pathStr + n = strlen(pathStr); + + // remove trailing spaces + for(; n > 0; --n ) + if( !isspace(pathStr[n-1]) ) + break; + + // if pathStr is empty + if( n == 0 ) + return nullptr; + + + char buf[n+1]; + buf[n] = 0; + + // Get the last word (which may be a file name) from pathStr. + // (pathStr must be copied into a buf because basename() + // is allowed to change the values in its arg.) + strncpy(buf,pathStr,n); + cp = basename(buf); + + if( cp != nullptr ) + { + char* ep; + // does the last word have a period in it + if( (ep = strchr(cp,'.')) != nullptr ) + { + *ep = 0; // end the file name at the period + ++ep; // set the ext marker + en = strlen(ep); // get the length of the ext + } + + fn = strlen(cp); //get the length of the file name + } + + // Get the directory part. + // ( pathStr must be copied into a buf because dirname() + // is allowed to change the values in its arg.) + strncpy(buf,pathStr,n); + + // if the last char in pathStr[] is '/' then the whole string is a dir. + // (this is not the answer that dirname() and basename() would give). + if( pathStr[n-1] == cwPathSeparatorChar ) + { + fn = 0; + en = 0; + dn = n; + cp = buf; + } + else + { + cp = dirname(buf); + } + + + if( cp != nullptr ) + dn = strlen(cp); + + // get the total size of the returned memory. (add 3 for ecmh possible terminating zero) + n = sizeof(fileSysPathPart_t) + dn + fn + en + 3; + + // alloc memory + if((rp = memAllocZ( n )) == nullptr ) + { + cwLogError( kMemAllocFailRC, "Unable to allocate the file system path part record for '%s'.",pathStr); + return nullptr; + } + + // set the return pointers for ecmh of the parts + rp->dirStr = (const char* )(rp + 1); + rp->fnStr = rp->dirStr + dn + 1; + rp->extStr = rp->fnStr + fn + 1; + + + // if there is a directory part + if( dn>0 ) + strcpy((char*)rp->dirStr,cp); + else + rp->dirStr = nullptr; + + if( fn || en ) + { + // Get the trailing word again. + // pathStr must be copied into a buf because basename() may + // is allowed to change the values in its arg. + strncpy(buf,pathStr,n); + cp = basename(buf); + + + if( cp != nullptr ) + { + + char* ep; + if( (ep = strchr(cp,'.')) != nullptr ) + { + *ep = 0; + ++ep; + + cwAssert( strlen(ep) == en ); + strcpy((char*)rp->extStr,ep); + } + + + cwAssert( strlen(cp) == fn ); + if(fn) + strcpy((char*)rp->fnStr,cp); + } + } + + if( fn == 0 ) + rp->fnStr = nullptr; + + if( en == 0 ) + rp->extStr = nullptr; + + return rp; + +} + diff --git a/cwFileSys.h b/cwFileSys.h new file mode 100644 index 0000000..39a4c42 --- /dev/null +++ b/cwFileSys.h @@ -0,0 +1,45 @@ +#ifndef cwFileSys_H +#define cwFileSys_H + +namespace cw +{ + + // Test the type of a file system object: + // + bool fileSysIsDir( const char* dirStr ); //< Return true if 'dirStr' refers to an existing directory. + bool fileSysIsFile( const char* fnStr ); //< Return true if 'fnStr' refers to an existing file. + bool fileSysIsLink( const char* fnStr ); //< Return true if 'fnStr' refers to a symbolic link. + + // Create File Names: + // + // Create a file name by concatenating sub-strings. + // + // Variable arg's. entries are directories inserted between + // 'dirPrefixStr' and the file name. + // Terminate var arg's directory list with a nullptr. + // + // The returned string must be released by a call to memRelease() or memFree(). + char* fileSysVMakeFn( const char* dir, const char* fn, const char* ext, va_list vl ); + char* fileSysMakeFn( const char* dir, const char* fn, const char* ext, ... ); + + + // Parse a path into its parts: + // + // Return record used by fileSysParts() + typedef struct + { + const char* dirStr; + const char* fnStr; + const char* extStr; + } fileSysPathPart_t; + + // Given a file name decompose it into a directory string, file name string and file extension string. + // 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() + fileSysPathPart_t* fileSysPathParts( const char* pathNameStr ); + + +} + + +#endif diff --git a/cwLex.cpp b/cwLex.cpp new file mode 100644 index 0000000..0c81005 --- /dev/null +++ b/cwLex.cpp @@ -0,0 +1,922 @@ +#include "cwCommon.h" +#include "cwLog.h" +#include "cwCommonImpl.h" +#include "cwMem.h" +#include "cwFile.h" + +#include "cwLex.h" + + +namespace cw +{ + enum + { + kRealFloatLexFl = 0x01, + kIntUnsignedLexFl = 0x02 + }; + + struct lex_str; + + typedef unsigned (*lexMatcherFuncPtr_t)( struct lex_str* p, const char* cp, unsigned cn, const char* keyStr ); + + // token match function record + typedef struct + { + unsigned typeId; // token type this matcher recognizes + lexMatcherFuncPtr_t funcPtr; // recognizer function (only used if userPtr==nullptr) + char* tokenStr; // fixed string data used by the recognizer (only used if userPtr==nullptr) + lexUserMatcherPtr_t userPtr; // user defined recognizer function (only used if funcPtr==nullptr) + bool enableFl; // true if this matcher is enabled + } lexMatcher; + + + + typedef struct lex_str + { + const char* cp; // character buffer + unsigned cn; // count of characters in buffer + unsigned ci; // current buffer index position + unsigned flags; // lexer control flags + unsigned curTokenId; // type id of the current token + unsigned curTokenCharIdx; // index into cp[] of the current token + unsigned curTokenCharCnt; // count of characters in the current token + unsigned curLine; // line number of the current token + unsigned curCol; // column number of the current token + + unsigned nextLine; + unsigned nextCol; + char* blockBegCmtStr; + char* blockEndCmtStr; + char* lineCmtStr; + + lexMatcher* mfp; // base of matcher array + unsigned mfi; // next available matcher array slot + unsigned mfn; // count of elementes in mfp[] + char* textBuf; // text buf used by lexSetFile() + + unsigned attrFlags; // used to store the int and real suffix type flags + unsigned lastRC; + } lex_t; + + lexH_t lexNullHandle; + + +#define _lexHandleToPtr(h) handleToPtr(h) + + bool _lexIsNewline( char c ) + { return c == '\n'; } + + bool _lexIsCommentTypeId( unsigned typeId ) + { return typeId == kBlockCmtLexTId || typeId == kLineCmtLexTId; } + + + // Locate 'keyStr' in cp[cn] and return the index into cp[cn] of the character + // following the last char in 'keyStr'. If keyStr is not found return kInvalidIdx. + unsigned _lexScanTo( const char* cp, unsigned cn, const char* keyStr ) + { + unsigned i = 0; + unsigned n = strlen(keyStr); + + if( n <= cn ) + for(; i<=cn-n; ++i) + if( strncmp(cp + i, keyStr, n ) == 0 ) + return i+n; + + return kInvalidIdx; + + } + + + unsigned _lexExactStringMatcher( lex_t* p, const char* cp, unsigned cn, const char* keyStr ) + { + unsigned n = strlen(keyStr); + return strncmp(keyStr,cp,n) == 0 ? n : 0; + } + + + unsigned _lexSpaceMatcher( lex_t* p, const char* cp, unsigned cn, const char* keyStr ) + { + unsigned i=0; + for(; i0 && i 0; + + } + + // if at least one digit was found + if( d>0 ) + { + // Note that this path allows a string w/o a decimal pt to trigger a match. + + if(iattrFlags = cwSetFlag(p->attrFlags,kRealFloatLexFl); + ++i; + break; + } + + } + + // match w/o suffix return + if( d>0 && (fl || n==1 || cwIsFlag(p->attrFlags,kRealFloatLexFl)) ) + return i; + } + + return 0; // no-match return + } + + unsigned _lexIntMatcher( lex_t* p, const char* cp, unsigned cn, const char* keyStr ) + { + unsigned i = 0; + bool signFl = false; + unsigned digitCnt = 0; + + for(; i= number of + // digits following the decimal point (in effect zeros are + // padded on the right side) then the value is an integer. + // + // The current implementation recognizes all numeric strings + // containing a decimal point as reals. + + // if no integer was found + if( digitCnt==0) + return 0; + + + // check for suffix + if(icurLine); + else + { + p->attrFlags = cwSetFlag(p->attrFlags,kIntUnsignedLexFl); + ++i; + } + break; + + default: + break; + } + } + + return i; + } + + unsigned _lexHexMatcher( lex_t* p, const char* cp, unsigned cn, const char* keyStr ) + { + unsigned i = 0; + + if( cn < 3 ) + return 0; + + if( cp[0]=='0' && cp[1]=='x') + for(i=2; icurLine); + } + + unsigned _lexQCharMatcher( lex_t* p, const char* cp, unsigned cn, const char* keyStr ) + { + unsigned i = 0; + if( i >= cn || cp[i]!='\'' ) + return 0; + + i+=2; + + if( i >= cn || cp[i]!='\'') + return 0; + + return 3; + } + + + unsigned _lexBlockCmtMatcher( lex_t* p, const char* cp, unsigned cn, const char* keyStr ) + { + unsigned n = strlen(p->blockBegCmtStr); + + if( strncmp( p->blockBegCmtStr, cp, n ) == 0 ) + { + unsigned i; + if((i = _lexScanTo(cp + n, cn-n,p->blockEndCmtStr)) == kInvalidIdx ) + { + cwLogError(kSyntaxErrorRC, "Missing end of block comment on line:%i.", p->curLine); + return 0; + } + + return n + i; + } + return 0; + } + + unsigned _lexLineCmtMatcher( lex_t* p, const char* cp, unsigned cn, const char* keyStr ) + { + unsigned n = strlen(p->lineCmtStr); + if( strncmp( p->lineCmtStr, cp, n ) == 0) + { + unsigned i; + const char newlineStr[] = "\n"; + if((i = _lexScanTo(cp + n, cn-n, newlineStr)) == kInvalidIdx ) + { + // no EOL was found so the comment must be on the last line of the source + return cn; + } + + return n + i; + } + return 0; + } + + rc_t _lexInstallMatcher( lex_t* p, unsigned typeId, lexMatcherFuncPtr_t funcPtr, const char* keyStr, lexUserMatcherPtr_t userPtr ) + { + assert( funcPtr==nullptr || userPtr==nullptr ); + assert( !(funcPtr==nullptr && userPtr==nullptr)); + + // if there is no space in the user token array - then expand it + if( p->mfi == p->mfn ) + { + int incr_cnt = 10; + lexMatcher* np = memAllocZ( p->mfn + incr_cnt ); + memcpy(np,p->mfp,p->mfi*sizeof(lexMatcher)); + memRelease(p->mfp); + p->mfp = np; + p->mfn += incr_cnt; + } + + p->mfp[p->mfi].tokenStr = nullptr; + p->mfp[p->mfi].typeId = typeId; + p->mfp[p->mfi].funcPtr = funcPtr; + p->mfp[p->mfi].userPtr = userPtr; + p->mfp[p->mfi].enableFl = true; + + if( keyStr != nullptr ) + { + // allocate space for the token string and store it + p->mfp[p->mfi].tokenStr = memDuplStr(keyStr); + } + + + p->mfi++; + return kOkRC; + } + rc_t _lexReset( lex_t* p ) + { + + p->ci = 0; + + p->curTokenId = kErrorLexTId; + p->curTokenCharIdx = kInvalidIdx; + p->curTokenCharCnt = 0; + + p->curLine = 0; + p->curCol = 0; + p->nextLine = 0; + p->nextCol = 0; + + p->lastRC = kOkRC; + + return kOkRC; + } + + rc_t _lexSetTextBuffer( lex_t* p, const char* cp, unsigned cn ) + { + p->cp = cp; + p->cn = cn; + + return _lexReset(p); + } + + lexMatcher* _lexFindUserToken( lex_t* p, unsigned id, const char* tokenStr ) + { + unsigned i = 0; + for(; imfi; ++i) + { + if( id != kInvalidId && p->mfp[i].typeId == id ) + return p->mfp + i; + + if( p->mfp[i].tokenStr != nullptr && tokenStr != nullptr && strcmp(p->mfp[i].tokenStr,tokenStr)==0 ) + return p->mfp + i; + + } + + return nullptr; + } + + +} // namespace cw + +cw::rc_t cw::lexCreate( lexH_t& hRef, const char* cp, unsigned cn, unsigned flags ) +{ + rc_t rc = kOkRC; + char dfltLineCmt[] = "//"; + char dfltBlockBegCmt[] = "/*"; + char dfltBlockEndCmt[] = "*/"; + lex_t* p = nullptr; + + if((rc = lexDestroy(hRef)) != kOkRC ) + return rc; + + p = memAllocZ(); + + p->flags = flags; + + _lexSetTextBuffer( p, cp, cn ); + + int init_mfn = 10; + p->mfp = memAllocZ( init_mfn ); + p->mfn = init_mfn; + p->mfi = 0; + + p->lineCmtStr = memDuplStr( dfltLineCmt ); + p->blockBegCmtStr = memDuplStr( dfltBlockBegCmt ); + p->blockEndCmtStr = memDuplStr( dfltBlockEndCmt ); + + _lexInstallMatcher( p, kSpaceLexTId, _lexSpaceMatcher, nullptr, nullptr ); + _lexInstallMatcher( p, kRealLexTId, _lexRealMatcher, nullptr, nullptr ); + _lexInstallMatcher( p, kIntLexTId, _lexIntMatcher, nullptr, nullptr ); + _lexInstallMatcher( p, kHexLexTId, _lexHexMatcher, nullptr, nullptr ); + _lexInstallMatcher( p, kIdentLexTId, _lexIdentMatcher, nullptr, nullptr ); + _lexInstallMatcher( p, kQStrLexTId, _lexQStrMatcher, nullptr, nullptr ); + _lexInstallMatcher( p, kBlockCmtLexTId, _lexBlockCmtMatcher, nullptr, nullptr ); + _lexInstallMatcher( p, kLineCmtLexTId, _lexLineCmtMatcher, nullptr, nullptr ); + + if( cwIsFlag(flags,kReturnQCharLexFl) ) + _lexInstallMatcher( p, kQCharLexTId, _lexQCharMatcher, nullptr, nullptr ); + + hRef.set(p); + + _lexReset(p); + + return rc; +} + +cw::rc_t cw::lexDestroy( lexH_t& hRef ) +{ + if( hRef.isValid() == false ) + return kOkRC; + + lex_t* p = _lexHandleToPtr(hRef); + + if( p != nullptr ) + { + + if( p->mfp != nullptr ) + { + unsigned i = 0; + + // free the user token strings + for(; imfi; ++i) + if( p->mfp[i].tokenStr != nullptr ) + memRelease(p->mfp[i].tokenStr); + + // free the matcher array + memRelease(p->mfp); + p->mfi = 0; + p->mfn = 0; + } + + memRelease(p->lineCmtStr); + memRelease(p->blockBegCmtStr); + memRelease(p->blockEndCmtStr); + memRelease(p->textBuf); + + // free the lexer object + memRelease(p); + hRef.set(nullptr); + } + + return kOkRC; +} + +cw::rc_t cw::lexReset( lexH_t h ) +{ + lex_t* p = _lexHandleToPtr(h); + return _lexReset(p); +} + + +bool cw::lexIsValid( lexH_t h ) +{ return h.isValid(); } + +cw::rc_t cw::lexSetTextBuffer( lexH_t h, const char* cp, unsigned cn ) +{ + lex_t* p = _lexHandleToPtr(h); + return _lexSetTextBuffer(p,cp,cn); +} + +cw::rc_t cw::lexSetFile( lexH_t h, const char* fn ) +{ + rc_t rc = kOkRC; + fileH_t fh; + lex_t* p = _lexHandleToPtr(h); + long n = 0; + + assert( fn != nullptr && p != nullptr ); + + // open the file + if((rc = fileOpen(fh,fn,kReadFileFl)) != kOkRC ) + return rc; + + // seek to the end of the file + if((rc = fileSeek(fh,kEndFileFl,0)) != kOkRC ) + return rc; + + // get the length of the file + if((rc = fileTell(fh,&n)) != kOkRC ) + return rc; + + // rewind to the beginning of the file + if((rc = fileSeek(fh,kBeginFileFl,0)) != kOkRC ) + return rc; + + // allocate the text buffer + if((p->textBuf = memResizeZ(p->textBuf, n+1)) == nullptr ) + { + rc = cwLogError(kMemAllocFailRC,"Unable to allocate the text file buffer for:'%s'.",fn); + goto errLabel; + } + + // read the file into the buffer + if((rc = fileRead(fh,p->textBuf,n)) != kOkRC ) + return rc; + + if((rc = _lexSetTextBuffer( p, p->textBuf, n )) != kOkRC ) + goto errLabel; + + errLabel: + // close the file + rc_t rc0 = fileClose(fh); + + if(rc != kOkRC ) + return rc; + + return rc0; +} + +cw::rc_t cw::lexRegisterToken( lexH_t h, unsigned id, const char* tokenStr ) +{ + lex_t* p = _lexHandleToPtr(h); + + // prevent duplicate tokens + if( _lexFindUserToken( p, id, tokenStr ) != nullptr ) + return cwLogError( kInvalidArgRC, "id:%i token:%s duplicates the token string or id", id, tokenStr ); + + + return _lexInstallMatcher( p, id, _lexExactStringMatcher, tokenStr, nullptr ); + + +} + +cw::rc_t cw::lexRegisterMatcher( lexH_t h, unsigned id, lexUserMatcherPtr_t userPtr ) +{ + lex_t* p = _lexHandleToPtr(h); + + // prevent duplicate tokens + if( _lexFindUserToken( p, id, nullptr ) != nullptr ) + return cwLogError(kInvalidArgRC, "A token matching function has already been installed for token id: %i", id ); + + return _lexInstallMatcher( p, id, nullptr, nullptr, userPtr ); +} + +cw::rc_t cw::lexEnableToken( lexH_t h, unsigned id, bool enableFl ) +{ + lex_t* p = _lexHandleToPtr(h); + + unsigned mi = 0; + for(; mimfi; ++mi) + if( p->mfp[mi].typeId == id ) + { + p->mfp[mi].enableFl = enableFl; + return kOkRC; + } + + return cwLogError( kInvalidArgRC, "%i is not a valid token type id.",id); +} + +unsigned cw::lexFilterFlags( lexH_t h ) +{ + lex_t* p = _lexHandleToPtr(h); + return p->flags; +} + +void cw::lexSetFilterFlags( lexH_t h, unsigned flags ) +{ + lex_t* p = _lexHandleToPtr(h); + p->flags = flags; +} + + +unsigned cw::lexGetNextToken( lexH_t h ) +{ + lex_t* p = _lexHandleToPtr(h); + + if( p->lastRC != kOkRC ) + return kErrorLexTId; + + while( p->ci < p->cn ) + { + unsigned i; + unsigned mi = 0; + unsigned maxCharCnt = 0; + unsigned maxIdx = kInvalidIdx; + + p->curTokenId = kErrorLexTId; + p->curTokenCharIdx = kInvalidIdx; + p->curTokenCharCnt = 0; + p->attrFlags = 0; + + // try each matcher + for(; mimfi; ++mi) + if( p->mfp[mi].enableFl ) + { + unsigned charCnt = 0; + if( p->mfp[mi].funcPtr != nullptr ) + charCnt = p->mfp[mi].funcPtr(p, p->cp + p->ci, p->cn - p->ci, p->mfp[mi].tokenStr ); + else + charCnt = p->mfp[mi].userPtr( p->cp + p->ci, p->cn - p->ci); + + // notice if the matcher set the error code + if( p->lastRC != kOkRC ) + return kErrorLexTId; + + // if this matched token is longer then the prev. matched token or + // if the prev matched token was an identifier and this matched token is an equal length user defined token + if( (charCnt > maxCharCnt) + || (charCnt>0 && charCnt==maxCharCnt && p->mfp[maxIdx].typeId==kIdentLexTId && p->mfp[mi].typeId >=kUserLexTId ) + || (charCnt>0 && charCntmfp[maxIdx].typeId==kIdentLexTId && p->mfp[mi].typeId >=kUserLexTId && cwIsFlag(p->flags,kUserDefPriorityLexFl)) + ) + { + maxCharCnt = charCnt; + maxIdx = mi; + } + + } + + // no token was matched + if( maxIdx == kInvalidIdx ) + { + if( cwIsFlag(p->flags,kReturnUnknownLexFl) ) + { + maxCharCnt = 1; + } + else + { + cwLogError( kSyntaxErrorRC, "Unable to recognize token:'%c' on line %i.",*(p->cp+p->ci), p->curLine); + return kErrorLexTId; + } + } + + // update the current line and column position + p->curLine = p->nextLine; + p->curCol = p->nextCol; + + + // find the next column and line position + for(i=0; icp[ p->ci + i ]) ) + { + p->nextLine++; + p->nextCol = 1; + } + else + p->nextCol++; + } + + bool returnFl = true; + + if( maxIdx != kInvalidIdx ) + { + // check the space token filter + if( (p->mfp[ maxIdx ].typeId == kSpaceLexTId) && (cwIsFlag(p->flags,kReturnSpaceLexFl)==0) ) + returnFl = false; + + // check the comment token filter + if( _lexIsCommentTypeId(p->mfp[ maxIdx ].typeId) && (cwIsFlag(p->flags,kReturnCommentsLexFl)==0) ) + returnFl = false; + } + + // update the lexer state + p->curTokenId = maxIdx==kInvalidIdx ? kUnknownLexTId : p->mfp[ maxIdx ].typeId; + p->curTokenCharIdx = p->ci; + p->curTokenCharCnt = maxCharCnt; + + // advance the text buffer + p->ci += maxCharCnt; + + if( returnFl ) + return p->curTokenId; + } + + p->lastRC = kEofRC; + + return kEofLexTId; + +} + +unsigned cw::lexTokenId( lexH_t h ) +{ + lex_t* p = _lexHandleToPtr(h); + + return p->curTokenId; +} + +const char* cw::lexTokenText( lexH_t h ) +{ + lex_t* p = _lexHandleToPtr(h); + + if( p->curTokenCharIdx == kInvalidIdx ) + return nullptr; + + unsigned n = p->curTokenId == kQStrLexTId ? 1 : 0; + + return p->cp + p->curTokenCharIdx + n; +} + + +unsigned cw::lexTokenCharCount( lexH_t h ) +{ + lex_t* p = _lexHandleToPtr(h); + + if( p->curTokenCharIdx == kInvalidIdx ) + return 0; + + unsigned n = p->curTokenId == kQStrLexTId ? 2 : 0; + + return p->curTokenCharCnt - n; +} + +int cw::lexTokenInt( lexH_t h ) +{ return strtol( lexTokenText(h),nullptr,0 ); } + +unsigned cw::lexTokenUInt( lexH_t h ) +{ return strtol( lexTokenText(h),nullptr,0 ); } + +float cw::lexTokenFloat( lexH_t h ) +{ return strtof( lexTokenText(h),nullptr ); } + +double cw::lexTokenDouble( lexH_t h ) +{ return strtod( lexTokenText(h),nullptr ); } + + +bool cw::lexTokenIsUnsigned( lexH_t h ) +{ + lex_t* p = _lexHandleToPtr(h); + return p->curTokenId == kIntLexTId && cwIsFlag(p->attrFlags,kIntUnsignedLexFl); +} + +bool cw::lexTokenIsSinglePrecision( lexH_t h ) +{ + lex_t* p = _lexHandleToPtr(h); + return p->curTokenId == kRealLexTId && cwIsFlag(p->attrFlags,kRealFloatLexFl); +} + +unsigned cw::lexCurrentLineNumber( lexH_t h ) +{ + lex_t* p = _lexHandleToPtr(h); + return p->curLine + 1; +} + +unsigned cw::lexCurrentColumnNumber( lexH_t h ) +{ + lex_t* p = _lexHandleToPtr(h); + return p->curCol + 1; +} + +unsigned cw::lexErrorRC( lexH_t h ) +{ + lex_t* p = _lexHandleToPtr(h); + return p->lastRC; +} + +const char* cw::lexIdToLabel( lexH_t h, unsigned typeId ) +{ + lex_t* p = _lexHandleToPtr(h); + + switch( typeId ) + { + case kErrorLexTId: return ""; + case kEofLexTId: return ""; + case kSpaceLexTId: return ""; + case kRealLexTId: return ""; + case kIntLexTId: return ""; + case kHexLexTId: return ""; + case kIdentLexTId: return ""; + case kQStrLexTId: return ""; + case kBlockCmtLexTId: return ""; + case kLineCmtLexTId: return ""; + default: + { + lexMatcher* mp; + if((mp = _lexFindUserToken(p,typeId,nullptr)) == nullptr ) + return ""; + return mp->tokenStr; + } + } + return ""; +} + + +namespace cw +{ + //{ { label:cwLexEx } + //( + // lexTest() gives a simple 'lex' example. + //) + + //( + void lexTest() + { + rc_t rc = kOkRC; + unsigned tid = kInvalidId; + lexH_t h = lexNullHandle; + + char buf[] = + "123ident0\n 123.456\nident0\n" + "0xa12+.2\n" + "// comment \n" + "/* block \n" + "comment */" + "\"quoted string\"" + "ident1" + "// last line comment"; + + // initialize a lexer with a buffer of text + if((rc = lexCreate(h,buf,strlen(buf), kReturnSpaceLexFl | kReturnCommentsLexFl)) != kOkRC ) + { + cwLogError(rc,"Lexer initialization failed."); + return; + } + + // register some additional recoginizers + lexRegisterToken(h,kUserLexTId+1,"+"); + lexRegisterToken(h,kUserLexTId+2,"-"); + + // ask for token id's + while( (tid = lexGetNextToken(h)) != kEofLexTId ) + { + // print information about each token + cwLogInfo("%i %i %s '%.*s' (%i) ", + lexCurrentLineNumber(h), + lexCurrentColumnNumber(h), + lexIdToLabel(h,tid), + lexTokenCharCount(h), + lexTokenText(h) , + lexTokenCharCount(h)); + + // if the token is a number ... + if( tid==kIntLexTId || tid==kRealLexTId || tid==kHexLexTId ) + { + // ... then request the numbers value + int iv = lexTokenInt(h); + double dv = lexTokenDouble(h); + + cwLogInfo("%i %f",iv,dv); + } + + cwLogInfo("\n"); + + // handle errors + if( tid == kErrorLexTId ) + { + cwLogInfo("Error:%i\n", lexErrorRC(h)); + break; + } + + } + + // finalize the lexer + lexDestroy(h); + + } + +} + +//) +//} diff --git a/cwLex.h b/cwLex.h new file mode 100644 index 0000000..7193859 --- /dev/null +++ b/cwLex.h @@ -0,0 +1,140 @@ +#ifndef cwLex_H +#define cwLex_H + + +//( { file_desc:"User configurable lexer for tokenizing text files." kw:[text]} + +namespace cw +{ + + // Predefined Lexer Id's + enum + { + kErrorLexTId, // 0 the lexer was unable to identify the current token + kUnknownLexTId, // 1 the token is of an unknown type (only used when kReturnUnknownLexFl is set) + kEofLexTId, // 2 the lexer reached the end of input + kSpaceLexTId, // 3 white space + kRealLexTId, // 4 real number (contains a decimal point or is in scientific notation) + kIntLexTId, // 5 decimal integer + kHexLexTId, // 6 hexidecimal integer + kIdentLexTId, // 7 identifier + kQStrLexTId, // 8 quoted string + kQCharLexTId, // 9 quoted char + kBlockCmtLexTId, // 10 block comment + kLineCmtLexTId, // 11 line comment + kUserLexTId // 12 user registered token (See lexRegisterToken().) + }; + + // Lexer control flags used with lexInit(). + enum + { + kReturnSpaceLexFl = 0x01, //< Return space tokens + kReturnCommentsLexFl = 0x02, //< Return comment tokens + kReturnUnknownLexFl = 0x04, //< Return unknown tokens + kReturnQCharLexFl = 0x08, //< Return quoted characters + kUserDefPriorityLexFl= 0x10 //< User defined tokens take priority even if a kIdentLexTId token has a longer match + }; + + + typedef handle lexH_t; + extern lexH_t lexNullHandle; + + // Iniitalize the lexer and receive a lexer handle in return. + // Set cp to nullptr if the buffer will be later via lexSetTextBuffer(); + // See the kXXXLexFl enum's above for possible flag values. + rc_t lexCreate( lexH_t& hRef, const char* cp, unsigned cn, unsigned flags ); + + // Finalize a lexer created by an earlier call to lexInit() + rc_t lexDestroy( lexH_t& hRef ); + + // Rewind the lexer to the begining of the buffer (the same as post initialize state) + rc_t lexReset( lexH_t h ); + + // Verify that a lexer handle is valid + bool lexIsValid( lexH_t h ); + + // Set a new text buffer and reset the lexer to the post initialize state. + rc_t lexSetTextBuffer( lexH_t h, const char* cp, unsigned cn ); + rc_t lexSetFile( lexH_t h, const char* fn ); + + // Register a user defined token. The id of the first user defined token should be + // kUserLexTId+1. Neither the id or token text can be used by a previously registered + // or built-in token. + rc_t lexRegisterToken( lexH_t h, unsigned id, const char* token ); + + // Register a user defined token recognition function. This function should return the count + // of initial, consecutive, characters in 'cp[cn]' which match its token pattern. + typedef unsigned (*lexUserMatcherPtr_t)( const char* cp, unsigned cn ); + + rc_t lexRegisterMatcher( lexH_t h, unsigned id, lexUserMatcherPtr_t funcPtr ); + + // Enable or disable the specified token type. + rc_t lexEnableToken( lexH_t h, unsigned id, bool enableFl ); + + // Get and set the lexer filter flags kReturnXXXLexFl. + // These flags can be safely enabled and disabled between + // calls to lexGetNextToken(). + unsigned lexFilterFlags( lexH_t h ); + void lexSetFilterFlags( lexH_t h, unsigned flags ); + + // Return the type id of the current token and advances to the next token + unsigned lexGetNextToken( lexH_t h ); + + // Return the type id associated with the current token. This is the same value + // returned by the previous call to lexGetNextToken(). + unsigned lexTokenId( lexH_t h ); + + // Return a pointer to the first character of text associated with the + // current token. The returned pointer directly references the text contained + // in the buffer given to the lexer in the call to lexInit(). The string + // is therefore not zero terminated. Use lexTokenCharCount() to get the + // length of the token string. + const char* lexTokenText( lexH_t h ); + + // Return the count of characters in the text associated with the current token. + // This is the only way to get this count since the string returned by + // lexTokenText() is not zero terminated. + unsigned lexTokenCharCount( lexH_t h ); + + // Return the value of the current token as an integer. + int lexTokenInt( lexH_t h ); + + // Return the value of the current token as an unsigned integer. + unsigned lexTokenUInt( lexH_t h ); + + // Return the value of the current token as a float. + float lexTokenFloat( lexH_t h ); + + // Return the value of the current token as a double. + double lexTokenDouble( lexH_t h ); + + // Return true if the current token is an int and it was suffixed + // with 'u' to indicate that it is unsigned. + bool lexTokenIsUnsigned( lexH_t h ); + + // Return true if the current token is a real and it was suffexed + // with 'f' to indicate that it is a single precision float. + bool lexTokenIsSinglePrecision( lexH_t h ); + + // Return the line number associated with the current token + unsigned lexCurrentLineNumber( lexH_t h ); + + // Return the starting column of the current token + unsigned lexCurrentColumnNumber( lexH_t h ); + + // Return the RC code associated with the last error + unsigned lexErrorRC( lexH_t h ); + + // Return the label associated with a token id + const char* lexIdToLabel( lexH_t h, unsigned typeId ); + + // Lexer testing stub. + void lexTest( ); + +} + +//) + + + +#endif diff --git a/cwLog.cpp b/cwLog.cpp new file mode 100644 index 0000000..6cf0238 --- /dev/null +++ b/cwLog.cpp @@ -0,0 +1,209 @@ +#include "cwCommon.h" +#include "cwLog.h" + +#include "cwCommonImpl.h" +#include "cwMem.h" + +namespace cw +{ + + typedef struct log_str + { + logOutputCbFunc_t outCbFunc; + void* outCbArg; + logFormatCbFunc_t fmtCbFunc; + void* fmtCbArg; + unsigned level; + } log_t; + + + logHandle_t __logGlobalHandle__; + logHandle_t logNullHandle; + +#define logHandleToPtr(h) handleToPtr(h) + + idLabelPair_t logLevelLabelArray[] = + { + { kDebug_LogLevel, "debug" }, + { kInfo_LogLevel, "info" }, + { kWarning_LogLevel, "warning" }, + { kError_LogLevel, "error" }, + { kFatal_LogLevel, "fatal" }, + { kInvalid_LogLevel, "" } + }; + +} + + + +cw::rc_t cw::logCreate( logHandle_t& hRef, unsigned level, logOutputCbFunc_t outCbFunc, void* outCbArg, logFormatCbFunc_t fmtCbFunc, void* fmtCbArg ) +{ + rc_t rc; + if((rc = logDestroy(hRef)) != kOkRC) + return rc; + + log_t* p = memAllocZ(); + p->outCbFunc = outCbFunc == nullptr ? logDefaultOutput : outCbFunc; + p->outCbArg = outCbArg; + p->fmtCbFunc = fmtCbFunc == nullptr ? logDefaultFormatter : fmtCbFunc; + p->fmtCbArg = fmtCbArg; + p->level = level; + hRef.p = p; + + return rc; +} + +cw::rc_t cw::logDestroy( logHandle_t& hRef ) +{ + rc_t rc = kOkRC; + + if( hRef.p == nullptr ) + return rc; + + memRelease( hRef.p ); + return rc; +} + +cw::rc_t cw::logMsg( logHandle_t h, unsigned level, const char* function, const char* filename, unsigned line, int systemErrorCode, rc_t rc, const char* fmt, va_list vl ) +{ + log_t* p = logHandleToPtr(h); + + va_list vl1; + va_copy(vl1,vl); + + int n = vsnprintf(nullptr,0,fmt,vl); + cwAssert(n != -1); + + if( n != -1 ) + { + + char msg[n+1]; + int m = vsnprintf(msg,n+1,fmt,vl1); + cwAssert(m==n); + + p->fmtCbFunc( p->fmtCbArg, p->outCbFunc, p->outCbArg, level, function, filename, line, systemErrorCode, rc, msg ); + } + + va_end(vl1); + return rc; +} + +cw::rc_t cw::logMsg( logHandle_t h, unsigned level, const char* function, const char* filename, unsigned line, int systemErrorCode, rc_t returnCode, const char* fmt, ... ) +{ + rc_t rc; + va_list vl; + va_start(vl,fmt); + rc = logMsg( h, level, function, filename, line, systemErrorCode, returnCode, fmt, vl ); + va_end(vl); + return rc; +} + +void cw::logSetLevel( logHandle_t h, unsigned level ) +{ + log_t* p = logHandleToPtr(h); + p->level = level; +} + +unsigned cw::logLevel( logHandle_t h ) +{ + log_t* p = logHandleToPtr(h); + return p->level; +} + + +const char* cw::logLevelToLabel( unsigned level ) +{ + const char* label; + if((label = idToLabel(logLevelLabelArray,level,kInvalid_LogLevel)) == nullptr) + label = ""; + + return label; +} + + + +void cw::logDefaultOutput( void* arg, unsigned level, const char* text ) +{ + FILE* f = level >= kWarning_LogLevel ? stderr : stdout; + fprintf(f,"%s",text); +} + +void cw::logDefaultFormatter( void* cbArg, logOutputCbFunc_t outFunc, void* outCbArg, unsigned level, const char* function, const char* filename, unsigned lineno, int sys_errno, rc_t rc, const char* msg ) +{ + // TODO: This code is avoids the use of dynamic memory allocation but relies on stack allocation. It's a security vulnerability. + // + + const char* systemLabel = sys_errno==0 ? "" : "System Error: "; + const char* systemMsg = sys_errno==0 ? "" : strerror(sys_errno); + const char* levelStr = logLevelToLabel(level); + + const char* rcFmt = "rc:%i"; + int rcn = snprintf(nullptr,0,rcFmt,rc); + char rcs[rcn+1]; + int rcm = snprintf(rcs,rcn+1,rcFmt,rc); + cwAssert(rcn==rcm); + const char* rcStr = rcs; + + const char* syFmt = "%s%s"; + int syn = snprintf(nullptr,0,syFmt,systemLabel,systemMsg); + char sys[syn+1]; + int sym = snprintf(sys,syn+1,syFmt,systemLabel,systemMsg); + cwAssert(syn==sym); + const char* syStr = sys; + + const char* loFmt = "%s line:%i %s"; + int lon = snprintf(nullptr,0,loFmt,function,lineno,filename); + char los[lon+1]; + int lom = snprintf(los,lon+1,loFmt,function,lineno,filename); + cwAssert(lon==lom); + const char* loStr = los; + + // don't print the function,file,line when this is an 'info' msg. + if( level == kInfo_LogLevel ) + loStr = ""; + + // dont' print the rc msg if this is info or debug + if( level < kWarning_LogLevel ) + rcStr = ""; + + // levelStr, msg,sys_msg, rc, function, lineno, filename + const char* fmt = "%s: %s %s %s %s\n"; + + int n = snprintf(nullptr,0,fmt,levelStr,msg,syStr,rcStr,loStr); + cwAssert(n != -1); + char s[n+1]; + int m = snprintf(s,n+1,fmt,levelStr,msg,syStr,rcStr,loStr); + cwAssert(m==n); + + outFunc(outCbArg,level,s); + +} + + +cw::rc_t cw::logCreateGlobal( unsigned level, logOutputCbFunc_t outCb, void* outCbArg, logFormatCbFunc_t fmtCb, void* fmtCbArg ) +{ + logHandle_t h; + rc_t rc; + + if((rc = logCreate(h, level, outCb, outCbArg, fmtCb, fmtCbArg )) == kOkRC ) + logSetGlobalHandle(h); + + return rc; + +} + +cw::rc_t cw::logDestroyGlobal() +{ + return logDestroy(__logGlobalHandle__); +} + + +void cw::logSetGlobalHandle( logHandle_t h ) +{ + __logGlobalHandle__ = h; +} + +cw::logHandle_t cw::logGlobalHandle() +{ + return __logGlobalHandle__; +} diff --git a/cwLog.h b/cwLog.h new file mode 100644 index 0000000..0ded120 --- /dev/null +++ b/cwLog.h @@ -0,0 +1,107 @@ +#ifndef cwLOG_H +#define cwLOG_H + +namespace cw +{ + + typedef enum + { + kInvalid_LogLevel, + kDebug_LogLevel, + kInfo_LogLevel, + kWarning_LogLevel, + kError_LogLevel, + kFatal_LogLevel, + } logLevelId_t; + + typedef handle logHandle_t; + + extern logHandle_t logNullHandle; + + typedef void (*logOutputCbFunc_t)( void* cbArg, unsigned level, const char* text ); + typedef void (*logFormatCbFunc_t)( void* cbArg, logOutputCbFunc_t outFunc, void* outCbArg, unsigned level, const char* function, const char* filename, unsigned line, int systemErrorCode, rc_t rc, const char* msg ); + + rc_t logCreate( logHandle_t& hRef, unsigned level=kDebug_LogLevel, logOutputCbFunc_t outCb=nullptr, void* outCbArg=nullptr, logFormatCbFunc_t fmtCb=nullptr, void* fmtCbArg=nullptr ); + rc_t logDestroy( logHandle_t& hRef ); + + rc_t logMsg( logHandle_t h, unsigned level, const char* function, const char* filename, unsigned line, int systemErrorCode, rc_t rc, const char* fmt, va_list vl ); + rc_t logMsg( logHandle_t h, unsigned level, const char* function, const char* filename, unsigned line, int systemErrorCode, rc_t rc, const char* fmt, ... ); + + void logSetLevel( logHandle_t h, unsigned level ); + unsigned logLevel( logHandle_t h ); + + const char* logLevelToLabel( unsigned level ); + + void logDefaultOutput( void* arg, unsigned level, const char* text ); + void logDefaultFormatter( void* cbArg, logOutputCbFunc_t outFunc, void* outCbArg, unsigned level, const char* function, const char* filename, unsigned line, int systemErrorCode, rc_t rc, const char* msg ); + + rc_t logCreateGlobal( unsigned level=kDebug_LogLevel, logOutputCbFunc_t outCb=nullptr, void* outCbArg=nullptr, logFormatCbFunc_t fmtCb=nullptr, void* fmtCbArg=nullptr ); + rc_t logDestroyGlobal( ); + + void logSetGlobalHandle( logHandle_t h ); + logHandle_t logGlobalHandle(); + +} + + + + +#define cwLogVDebugH(h,rc,fmt, vl) cw::logMsg( h, cw::kDebug_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, vl ) +#define cwLogDebugH( h,rc,fmt,...) cw::logMsg( h, cw::kDebug_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, ##__VA_ARGS__ ) + +#define cwLogVInfoH(h,rc,fmt, vl) cw::logMsg( h, cw::kInfo_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, vl ) +#define cwLogInfoH( h,rc,fmt,...) cw::logMsg( h, cw::kInfo_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, ##__VA_ARGS__ ) + +#define cwLogVWarningH(h,rc,fmt, vl) cw::logMsg( h, cw::kWarning_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, vl ) +#define cwLogWarningH( h,rc,fmt,...) cw::logMsg( h, cw::kWarning_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, ##__VA_ARGS__ ) + +#define cwLogVErrorH(h,rc,fmt, vl) cw::logMsg( h, cw::kError_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, vl ) +#define cwLogErrorH( h,rc,fmt,...) cw::logMsg( h, cw::kError_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, ##__VA_ARGS__ ) + +#define cwLogVSysErrorH(h,rc,sysRc,fmt, vl) cw::logMsg( h, cw::kError_LogLevel, __FUNCTION__, __FILE__, __LINE__, sysRc, rc, fmt, vl ) +#define cwLogSysErrorH( h,rc,sysRc,fmt,...) cw::logMsg( h, cw::kError_LogLevel, __FUNCTION__, __FILE__, __LINE__, sysRc, rc, fmt, ##__VA_ARGS__ ) + +#define cwLogVFatalH(h,rc,fmt, vl) cw::logMsg( h, cw::kFatal_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, vl ) +#define cwLogFatalH( h,rc,fmt,...) cw::logMsg( h, cw::kFatal_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, ##__VA_ARGS__ ) + +#ifdef cwLOG_DEBUG + +#define cwLogVDebugRC(rc,fmt, vl) cwLogVDebugH( cw::logGlobalHandle(), (rc), (fmt), (vl) ) +#define cwLogDebugRC( rc,fmt,...) cwLogDebugH( cw::logGlobalHandle(), (rc), (fmt), ##__VA_ARGS__ ) + +#define cwLogVDebug(fmt, vl) cwLogVDebugH( cw::logGlobalHandle(), cw::kOkRC, (fmt), (vl) ) +#define cwLogDebug( fmt,...) cwLogDebugH( cw::logGlobalHandle(), cw::kOkRC, (fmt), ##__VA_ARGS__ ) + +#else + +#define cwLogVDebugRC(rc,fmt, vl) +#define cwLogDebugRC( rc,fmt,...) + +#define cwLogVDebug(fmt, vl) +#define cwLogDebug( fmt,...) + +#endif + +#define cwLogVInfoRC(rc,fmt, vl) cwLogVInfoH( cw::logGlobalHandle(), (rc), (fmt), (vl) ) +#define cwLogInfoRC( rc,fmt,...) cwLogInfoH( cw::logGlobalHandle(), (rc), (fmt), ##__VA_ARGS__ ) + +#define cwLogVInfo(fmt, vl) cwLogVInfoH( cw::logGlobalHandle(), cw::kOkRC, (fmt), (vl) ) +#define cwLogInfo( fmt,...) cwLogInfoH( cw::logGlobalHandle(), cw::kOkRC, (fmt), ##__VA_ARGS__ ) + +#define cwLogVWarningRC(rc,fmt, vl) cwLogVWarningH( cw::logGlobalHandle(), (rc), (fmt), (vl) ) +#define cwLogWarningRC( rc,fmt,...) cwLogWarningH( cw::logGlobalHandle(), (rc), (fmt), ##__VA_ARGS__ ) + +#define cwLogVWarning(fmt, vl) cwLogVWarningH( cw::logGlobalHandle(), cw::kOkRC, (fmt), (vl) ) +#define cwLogWarning( fmt,...) cwLogWarningH( cw::logGlobalHandle(), cw::kOkRC, (fmt), ##__VA_ARGS__ ) + +#define cwLogVSysError(rc,sysRC,fmt, vl) cwLogVSysErrorH( cw::logGlobalHandle(), (rc), (sysRC), (fmt), (vl) ) +#define cwLogSysError( rc,sysRC,fmt,...) cwLogSysErrorH( cw::logGlobalHandle(), (rc), (sysRC), (fmt), ##__VA_ARGS__ ) + +#define cwLogVError(rc,fmt, vl) cwLogVErrorH( cw::logGlobalHandle(), (rc), (fmt), (vl) ) +#define cwLogError( rc,fmt,...) cwLogErrorH( cw::logGlobalHandle(), (rc), (fmt), ##__VA_ARGS__ ) + +#define cwLogVFatal(rc,fmt, vl) cwLogVFatalH( cw::logGlobalHandle(), (rc), (fmt), (vl) ) +#define cwLogFatal( rc,fmt,...) cwLogFatalH( cw::logGlobalHandle(), (rc), (fmt), ##__VA_ARGS__ ) + + +#endif diff --git a/cwMem.cpp b/cwMem.cpp new file mode 100644 index 0000000..56857d3 --- /dev/null +++ b/cwMem.cpp @@ -0,0 +1,95 @@ +#include "cwCommon.h" +#include "cwLog.h" +#include "cwCommonImpl.h" +#include "cwMem.h" + + +namespace cw +{ + + void* _memAlloc( void* p0, unsigned n, bool zeroFl ) + { + void* p = nullptr; // ptr to new block + unsigned p0N = 0; // size of existing block + + // if there is no existing block + if( p0 == nullptr ) + n += sizeof(unsigned); // add space for the size of the block + else + { + p0N = ((unsigned*)p0)[-1]; // get size of existing block + + // if the block is shrinking + if( p0N >= n ) + return p0; + } + + p = malloc(n); // allocate new memory + + // if expanding then copy in data from existing block + if( p0 != nullptr ) + { + memcpy(p,p0,p0N); + memFree(p0); // free the existing block + } + + // if requested zero the block + if( zeroFl ) + memset(((char*)p)+p0N,0,n-p0N); + + // get pointer to base of new block + unsigned* p1 = static_cast(p); + + p1[0] = p0N + n; // set size of new block + + // advance past the block size and return + return p1+1; + } + +} + +unsigned cw::memByteCount( const void* p ) +{ + return p==nullptr ? 0 : static_cast(p)[-1]; +} + + +char* cw::memAllocStr( const char* s ) +{ + char* s1 = nullptr; + + if( s != nullptr ) + { + unsigned sn = strlen(s); + s1 = static_cast(_memAlloc(nullptr,sn+1,false)); + memcpy(s1,s,sn); + s1[sn] = 0; + } + + return s1; +} + +void* cw::memAllocDupl( const void* p0, unsigned byteN ) +{ + if( p0 == nullptr || byteN == 0 ) + return nullptr; + + void* p1 = _memAlloc(nullptr,byteN,false); + memcpy(p1,p0,byteN); + return p1; +} + +void* cw::memAllocDupl( const void* p ) +{ + return memAllocDupl(p,memByteCount(p)); +} + + + +void cw::memFree( void* p ) +{ + if( p != nullptr) + { + free(static_cast(p)-1); + } +} diff --git a/cwMem.h b/cwMem.h new file mode 100644 index 0000000..080d95f --- /dev/null +++ b/cwMem.h @@ -0,0 +1,147 @@ +#ifndef cwMem_H +#define cwMem_H + +namespace cw +{ + + void* _memAlloc( void* p, unsigned n, bool zeroFl ); + + char* memAllocStr( const char* ); + void* memAllocDupl( const void* p, unsigned byteN ); + void* memAllocDupl( const void* p ); + void memFree( void* ); + + unsigned memByteCount( const void* p ); + + template + void memRelease(T& p) { memFree(p); p=nullptr; } + + template + T* memAllocZ(unsigned n=1) { return static_cast(_memAlloc(nullptr,n*sizeof(T),true)); } + + template + T* memAlloc(unsigned n=1) { return static_cast(_memAlloc(nullptr,n*sizeof(T),false)); } + + template + T* memResizeZ(T* p, unsigned n=1) { return static_cast(_memAlloc(p,n*sizeof(T),true)); } + + + template + size_t _memTextLength(const T* s ) + { + if( s == nullptr ) + return 0; + + // get length of source string + size_t n=0; + + for(; s[n]; ++n) + {} + + return n; + } + + template + T* memDuplStr( const T* s, size_t n ) + { + if( s == nullptr ) + return nullptr; + + n+=1; // add one for terminating zero + + // allocate space for new string + T* s1 = memAlloc(n); + + // copy in new string + for(size_t i=0; i + T* memDuplStr( const T* s ) + { + if( s == nullptr ) + return nullptr; + + + return memDuplStr(s,_memTextLength(s)); + } + + template + T* memReallocStr( T* s0, const T* s1 ) + { + if( s1 == nullptr ) + { + memFree(s0); + return nullptr; + } + + if( s0 == nullptr ) + return memDuplStr(s1); + + + size_t s0n = _memTextLength(s0); + size_t s1n = _memTextLength(s1); + + // if s1[] can't fit in space of s0[] + if( s1n > s0n ) + { + memFree(s0); + return memDuplStr(s1); + } + + // copy s1[] into s0[] + size_t i=0; + for(; s1[i]; ++i) + s0[i] = s1[i]; + s0[i+1] = 0; + + return s0; + } + + template + C* memPrintf(C* p0, const char* fmt, va_list vl0 ) + { + va_list vl1; + va_copy(vl1,vl0); + + size_t bufN = vsnprintf(nullptr,0,fmt,vl0); + + if( bufN == 0) + { + memFree(p0); + return nullptr; + } + + + C buf[ bufN + 1 ]; + size_t n = vsnprintf(buf,bufN,fmt,vl1); + + cwAssert(n <= bufN); + + buf[bufN] = 0; + + va_end(vl1); + + return memReallocStr(p0,buf); + } + + + template + C* memPrintf(C* p0, const char* fmt, ... ) + { + va_list vl; + va_start(vl,fmt); + C* p1 = memPrintf(p0,fmt,vl); + va_end(vl); + return p1; + } + + + +} + +#endif diff --git a/cwNumericConvert.h b/cwNumericConvert.h new file mode 100644 index 0000000..cb0397e --- /dev/null +++ b/cwNumericConvert.h @@ -0,0 +1,130 @@ +#ifndef cwNumericConvert_H +#define cwNumericConvert_H + +namespace cw +{ + /* + template< typename T > + T minimum_value() { return 0; } + + template <> inline char minimum_value(){ return 0; } + template <> inline int8_t minimum_value(){ return INT8_MIN; } + template <> inline int16_t minimum_value(){ return INT16_MIN; } + template <> inline int32_t minimum_value(){ return INT32_MIN; } + template <> inline int64_t minimum_value(){ return INT64_MIN; } + template <> inline float minimum_value(){ return FLT_MIN; } + template <> inline double minimum_value(){ return DBL_MIN; } + + template< typename T > + T maximum_value() { cwAssert(0); } + + template <> inline char maximum_value(){ return 255; } + template <> inline int8_t maximum_value(){ return INT8_MAX; } + template <> inline int16_t maximum_value(){ return INT16_MAX; } + template <> inline int32_t maximum_value(){ return INT32_MAX; } + template <> inline int64_t maximum_value(){ return INT64_MAX; } + template <> inline uint8_t maximum_value(){ return UINT8_MAX; } + template <> inline uint16_t maximum_value(){ return UINT16_MAX; } + template <> inline uint32_t maximum_value(){ return UINT32_MAX; } + template <> inline uint64_t maximum_value(){ return UINT64_MAX; } + template <> inline bool maximum_value(){ std::numeric_limits::max(); } + template <> inline float maximum_value(){ return FLT_MAX; } + template <> inline double maximum_value(){ return DBL_MAX; } + */ + + + template< typename SRC_t, typename DST_t > + rc_t numeric_convert( const SRC_t& src, DST_t& dst ) + { + // TODO: there is probably a way of using type_traits to make a more efficient comparison + // and avoid the double conversion + double d_min = std::numeric_limits::min(); + double d_max = std::numeric_limits::max(); + if( d_min <= src and src <= d_max ) + dst = src; + else + return cwLogError(kInvalidArgRC,"Numeric conversion failed. The source value is outside the range of the destination value." ); + + + return kOkRC; + } + + template< typename SRC_t, typename DST_t > + rc_t numeric_convert2( const SRC_t& src, DST_t& dst, const DST_t& minv, const DST_t& maxv ) + { + if( sizeof(SRC_t) < sizeof(DST_t) ) + { + dst = src; + } + else + { + if( minv <= src && src <= maxv ) + dst = src; + else + { + return cwLogError(kInvalidArgRC,"Numeric conversion failed. The source value is outside the range of the destination value." ); + } + } + + return kOkRC; + } + + + + template< typename T > + rc_t string_to_number( const char* s, T& valueRef ) + { + if( s == nullptr ) + valueRef = 0; + else + { + errno = 0; + long v = strtol(s,nullptr,10); + if( v == 0 and errno != 0) + return cwLogError(kOpFailRC,"String to number conversion failed on '%s'.", cwStringNullGuard(s)); + + return numeric_convert(v,valueRef); + + } + return kOkRC; + } + + template < > inline + rc_t string_to_number( const char* s, double& valueRef ) + { + if( s == nullptr ) + valueRef = 0; + else + { + errno = 0; + valueRef = strtod(s,nullptr); + if( valueRef == 0 and errno != 0) + return cwLogError(kOpFailRC,"String to number conversion failed on '%s'.", cwStringNullGuard(s)); + + } + return kOkRC; + } + + + template < > inline + rc_t string_to_number( const char* s, float& valueRef ) + { + double d; + rc_t rc; + if((rc = string_to_number(s,d)) != kOkRC ) + return rc; + + return numeric_convert(d,valueRef); + } + + + template< typename T > + void number_to_string( const T& v, const char* fmt, char* buf, int bufN ) + { snprintf(buf,bufN,fmt,v); } + +} +#endif + + + + diff --git a/cwObject.cpp b/cwObject.cpp new file mode 100644 index 0000000..d607bc4 --- /dev/null +++ b/cwObject.cpp @@ -0,0 +1,547 @@ +#include + +#include "cwCommon.h" +#include "cwLog.h" +#include "cwCommonImpl.h" +#include "cwMem.h" +#include "cwFile.h" +#include "cwLex.h" +#include "cwText.h" +#include "cwNumericConvert.h" +#include "cwObject.h" +#include "cwObjectTemplate.h" + + +namespace cw +{ + enum + { + kLCurlyLexTId = cw::kUserLexTId+1, + kRCurlyLexTId, + kLHardLexTId, + kRHardLexTId, + kColonLexTId, + kCommaLexTId, + kTrueLexTId, + kFalseLexTId, + kNullLexTId + }; + + idLabelPair_t _objTokenArray[] = + { + { kLCurlyLexTId, "{" }, + { kRCurlyLexTId, "}" }, + { kLHardLexTId, "[" }, + { kRHardLexTId, "]" }, + { kColonLexTId, ":" }, + { kCommaLexTId, "," }, + { kTrueLexTId, "true"}, + { kFalseLexTId, "false"}, + { kNullLexTId, "null" }, + { cw::kErrorLexTId,""} + + }; + + + + void _objTypeFree( object_t* o ) + { memRelease(o); } + + void _objTypeFreeString( object_t* o ) + { + memRelease( o->u.str ); + _objTypeFree(o); + } + + const char* _objTypeIdToLabel( objTypeId_t tid ); + + + void _objTypeNullToString( object_t* o, char* buf, unsigned bufN ) { snprintf(buf,bufN,"%s","NULL"); } + void _objTypeCharToString( object_t* o, char* buf, unsigned bufN ) { number_to_string(o->u.c,"%c",buf,bufN); } + void _objTypeInt8ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string(o->u.i8,"%i",buf,bufN); } + void _objTypeUInt8ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string(o->u.u8,"%i",buf,bufN); } + void _objTypeInt16ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string(o->u.i16,"%i",buf,bufN); } + void _objTypeUInt16ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string(o->u.u16,"%i",buf,bufN); } + void _objTypeInt32ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string(o->u.i32,"%i",buf,bufN); } + void _objTypeUInt32ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string(o->u.u32,"%i",buf,bufN); } + void _objTypeInt64ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string(o->u.i64,"%i",buf,bufN); } + void _objTypeUInt64ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string(o->u.u64,"%i",buf,bufN); } + void _objTypeBoolToString( object_t* o, char* buf, unsigned bufN ) { number_to_string(o->u.b,"%i",buf,bufN); } + void _objTypeFloatToString( object_t* o, char* buf, unsigned bufN ) { number_to_string(o->u.f,"%f",buf,bufN); } + void _objTypeDoubleToString( object_t* o, char* buf, unsigned bufN ) { number_to_string(o->u.d,"%f",buf,bufN); } + void _objTypeStringToString( object_t* o, char* buf, unsigned bufN ) { snprintf(buf,bufN,"%s",o->u.str); } + + + rc_t _objTypeValueFromChar( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.c,tid, dst,o->type->label); } + rc_t _objTypeValueFromInt8( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.i8,tid, dst,o->type->label); } + rc_t _objTypeValueFromUInt8( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.u8,tid, dst,o->type->label); } + rc_t _objTypeValueFromInt16( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.i16,tid,dst,o->type->label); } + rc_t _objTypeValueFromUInt16( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.u16,tid,dst,o->type->label); } + rc_t _objTypeValueFromInt32( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.i32,tid,dst,o->type->label); } + rc_t _objTypeValueFromUInt32( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.u32,tid,dst,o->type->label); } + rc_t _objTypeValueFromInt64( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.i64,tid,dst,o->type->label); } + rc_t _objTypeValueFromUInt64( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.u64,tid,dst,o->type->label); } + rc_t _objTypeValueFromFloat( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.f,tid, dst,o->type->label); } + rc_t _objTypeValueFromDouble( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.d,tid, dst,o->type->label); } + rc_t _objTypeValueFromBool( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.b,tid,dst,o->type->label); } + + rc_t _objTypeValueFromNonValue( const object_t* o, unsigned tid, void* dst ) + { return cwLogError(kInvalidArgRC, "There is no conversion from '%s' to '%s'.", _objTypeIdToLabel(tid), o->type->label); } + + rc_t _objTypeValueFromString( const object_t* o, unsigned tid, void* dst ) + { return _objTypeValueFromNonValue(o,tid,dst); } + + rc_t _objTypeValueFromVect( const object_t* o, unsigned tid, void* dst ) + { return _objTypeValueFromNonValue(o,tid,dst); } + + void _objTypePrintIndent( const char* text, unsigned indent, const char* indentStr=" " ) + { + for(unsigned i=0; itype->print(o,c); + printf(eolStr); + } + + void _objTypePrintNull( const object_t* o, print_ctx_t& c ) { printf("NULL "); } + void _objTypePrintError( const object_t* o, print_ctx_t& c ) { printf("Error "); } + void _objTypePrintChar( const object_t* o, print_ctx_t& c ) { printf("%c",o->u.c); } + void _objTypePrintInt8( const object_t* o, print_ctx_t& c ) { printf("%i",o->u.i8); } + void _objTypePrintUInt8( const object_t* o, print_ctx_t& c ) { printf("%i",o->u.u8); } + void _objTypePrintInt16( const object_t* o, print_ctx_t& c ) { printf("%i",o->u.i16); } + void _objTypePrintUInt16( const object_t* o, print_ctx_t& c ) { printf("%i",o->u.u16); } + void _objTypePrintInt32( const object_t* o, print_ctx_t& c ) { printf("%i",o->u.i32); } + void _objTypePrintUInt32( const object_t* o, print_ctx_t& c ) { printf("%i",o->u.u32); } + void _objTypePrintInt64( const object_t* o, print_ctx_t& c ) { printf("%li",o->u.i64); } + void _objTypePrintUInt64( const object_t* o, print_ctx_t& c ) { printf("%li",o->u.u64); } + void _objTypePrintBool( const object_t* o, print_ctx_t& c ) { printf("%s",o->u.b ? "true" : "false"); } + void _objTypePrintFloat( const object_t* o, print_ctx_t& c ) { printf("%f",o->u.f); } + void _objTypePrintDouble( const object_t* o, print_ctx_t& c ) { printf("%f",o->u.d); } + void _objTypePrintString( const object_t* o, print_ctx_t& c ) { printf("%s",o->u.str); } + void _objTypePrintVect( const object_t* o, print_ctx_t& c ) { printf(""); } + void _objTypePrintPair( const object_t* o, print_ctx_t& c ) + { + o->u.children->type->print(o->u.children,c); + printf(": "); + o->u.children->sibling->type->print(o->u.children->sibling,c); + } + + void _objTypePrintList( const object_t* o, print_ctx_t& c ) + { + const char* indentStr = c.listOnOneLineFl ? "" : " "; + + char bracketStr[] = { '[','\0','\0' }; + char eoValStr[] = { ',','\0','\0' }; + + if(!c.listOnOneLineFl) + { + bracketStr[1] = '\n'; + eoValStr[1] = '\n'; + } + + + _objTypePrintIndent(bracketStr,0); + c.indent += 2; + + for(const object_t* ch=o->u.children; ch!=nullptr; ch=ch->sibling) + { + if( ch->sibling == nullptr ) + eoValStr[0] = ' '; + + _objTypePrintChild(ch,c,eoValStr,indentStr); + } + + c.indent -= 2; + _objTypePrintIndent("]",c.listOnOneLineFl ? 0 : c.indent); + } + + void _objTypePrintDict( const object_t* o, print_ctx_t& c ) + { + _objTypePrintIndent("{\n",0); + c.indent += 2; + + for(const object_t* ch=o->u.children; ch!=nullptr; ch=ch->sibling) + _objTypePrintChild(ch,c); + + c.indent -= 2; + _objTypePrintIndent("}",c.indent); + } + + void _objTypePrintRoot( const object_t* o, print_ctx_t& c ) + { + _objTypePrintDict(o,c); + } + + objType_t _objTypeArray[] = + { + { kNullTId, "null", 0, _objTypeFree, _objTypeValueFromNonValue, _objTypePrintNull }, + { kErrorTId, "error", 0, _objTypeFree, _objTypeValueFromNonValue, _objTypePrintError }, + { kCharTId, "char", 0, _objTypeFree, _objTypeValueFromChar, _objTypePrintChar }, + { kInt8TId, "int8", 0, _objTypeFree, _objTypeValueFromInt8, _objTypePrintInt8 }, + { kUInt8TId, "uint8", 0, _objTypeFree, _objTypeValueFromUInt8, _objTypePrintUInt8 }, + { kInt16TId, "int16", 0, _objTypeFree, _objTypeValueFromInt16, _objTypePrintInt16 }, + { kUInt16TId, "uint16", 0, _objTypeFree, _objTypeValueFromUInt16, _objTypePrintUInt16 }, + { kInt32TId, "int32", 0, _objTypeFree, _objTypeValueFromInt32, _objTypePrintInt32 }, + { kUInt32TId, "uint32", 0, _objTypeFree, _objTypeValueFromUInt32, _objTypePrintUInt32 }, + { kInt64TId, "int64", 0, _objTypeFree, _objTypeValueFromInt64, _objTypePrintInt64 }, + { kUInt64TId, "uint64", 0, _objTypeFree, _objTypeValueFromUInt64, _objTypePrintUInt64 }, + { kBoolTId, "bool", 0, _objTypeFree, _objTypeValueFromBool, _objTypePrintBool }, + { kFloatTId, "float", 0, _objTypeFree, _objTypeValueFromFloat, _objTypePrintFloat }, + { kDoubleTId, "double", 0, _objTypeFree, _objTypeValueFromDouble, _objTypePrintDouble }, + { kStringTId, "string", 0, _objTypeFreeString, _objTypeValueFromString, _objTypePrintString }, + { kVectTId, "vect", 0, _objTypeFree, _objTypeValueFromVect, _objTypePrintVect }, + { kPairTId, "pair", kContainerFl | kValueContainerFl, _objTypeFree, _objTypeValueFromNonValue, _objTypePrintPair }, + { kListTId, "list", kContainerFl | kValueContainerFl, _objTypeFree, _objTypeValueFromNonValue, _objTypePrintList }, + { kDictTId, "dict", kContainerFl, _objTypeFree, _objTypeValueFromNonValue, _objTypePrintDict }, + { kRootTId, "root", kContainerFl | kValueContainerFl, _objTypeFree, _objTypeValueFromNonValue, _objTypePrintRoot }, + { kInvalidTId, "", 0, nullptr } + }; + + + + objType_t* _objIdToType( objTypeId_t tid ) + { + unsigned i; + for(i=0; _objTypeArray[i].id != kInvalidTId; ++i) + if( _objTypeArray[i].id == tid ) + return _objTypeArray + i; + + cwLogError(kInvalidIdRC,"The object type id %i is not valid.",tid); + return nullptr; + } + + const char* _objTypeIdToLabel( objTypeId_t tid ) + { + const objType_t* type; + if((type = _objIdToType(tid)) == nullptr ) + return ""; + + return type->label; + } + + + object_t* _objAllocate( objTypeId_t tid, object_t* parent ) + { + objType_t* type = nullptr; + if( tid != kInvalidTId ) + { + if((type = _objIdToType(tid)) == nullptr ) + { + cwLogError(kObjAllocFailRC,"Object allocation failed."); + return nullptr; + } + } + + object_t* o = memAllocZ(); + o->type = type; + o->parent = parent; + return o; + } + + rc_t _objSyntaxError( lexH_t lexH, const char* fmt, ... ) + { + va_list vl; + va_start(vl,fmt); + + cwLogVError( kSyntaxErrorRC, fmt, vl ); + cwLogError( kSyntaxErrorRC, "Error on line: %i.", lexCurrentLineNumber(lexH)); + va_end(vl); + return kSyntaxErrorRC; + } + + + rc_t _objVerifyParentIsValueContainer( lexH_t lexH, const object_t* parent, const char* msg ) + { + if( parent == nullptr ) + return _objSyntaxError(lexH,"The parent node must always be valid."); + + // it is legal for a parent of a value to be null when the value is the root element. + if( !(parent->is_value_container())) + return _objSyntaxError(lexH,"Value nodes of type '%s' must be contained by 'root', 'pair' or 'array' node.",msg); + + return kOkRC; + } + + object_t* _objAppendLeftMostNode( object_t* parent, object_t* newNode ) + { + if( newNode == nullptr ) + return nullptr; + + object_t* child = parent->u.children; + + if( parent->u.children == nullptr ) + parent->u.children = newNode; + else + { + while( child->sibling != nullptr ) + child = child->sibling; + + child->sibling = newNode; + } + + newNode->parent = parent; + return newNode; + } + + object_t* _objCreateConainerNode( lexH_t lexH, object_t* parent, objTypeId_t tid ) + { + if( _objVerifyParentIsValueContainer(lexH,parent,_objTypeIdToLabel(tid)) == kOkRC ) + return _objAppendLeftMostNode( parent, _objAllocate( tid, parent )); + + return nullptr; + } + +} + + +void cw::object_t::free() +{ + if( is_container() ) + { + object_t* o1 = nullptr; + for(object_t* o = u.children; o != nullptr; o=o1 ) + { + o1 = o->sibling; + o->free(); + } + } + + type->free(this); +} + + +unsigned cw::object_t::child_count() const +{ + unsigned n = 0; + if( is_container() && u.children != nullptr) + { + object_t* o = u.children; + + for(n=1; o->sibling != nullptr; o=o->sibling) + ++n; + } + return n; +} + +cw::rc_t cw::object_t::value( void* dst, unsigned dstTypeId ) { return type->value(this,dstTypeId,dst); } +cw::rc_t cw::object_t::value( char& v ) const { return type->value(this,kCharTId,&v); } +cw::rc_t cw::object_t::value( int8_t& v ) const { return type->value(this,kInt8TId,&v); } +cw::rc_t cw::object_t::value( uint8_t& v ) const { return type->value(this,kUInt8TId,&v); } +cw::rc_t cw::object_t::value( int16_t& v ) const { return type->value(this,kInt16TId,&v); } +cw::rc_t cw::object_t::value( uint16_t& v ) const { return type->value(this,kUInt16TId,&v); } +cw::rc_t cw::object_t::value( int32_t& v ) const { return type->value(this,kInt32TId,&v); } +cw::rc_t cw::object_t::value( uint32_t& v ) const { return type->value(this,kUInt32TId,&v); } +cw::rc_t cw::object_t::value( int64_t& v ) const { return type->value(this,kInt64TId,&v); } +cw::rc_t cw::object_t::value( uint64_t& v ) const { return type->value(this,kUInt64TId,&v); } +cw::rc_t cw::object_t::value( float& v ) const { return type->value(this,kFloatTId,&v); } +cw::rc_t cw::object_t::value( double& v ) const { return type->value(this,kDoubleTId,&v); } +cw::rc_t cw::object_t::value( char*& v ) const { return type->value(this,kStringTId,&v); } + +const char* cw::object_t::pair_label() const +{ + cwAssert( is_pair() ); + if( is_pair() ) + return u.children->u.str; + return nullptr; +} + +const struct cw::object_str* cw::object_t::pair_value() const +{ + cwAssert( is_pair() ); + if( is_pair() ) + return u.children->sibling; + return nullptr; +} + +const struct cw::object_str* cw::object_t::find( const char* label ) const +{ + if( is_container() ) + { + for(object_t* o=u.children; o!=nullptr; o=o->sibling) + { + if( o->is_pair() && textCompare(o->pair_label(),label) == 0 ) + return o->pair_value(); + + const object_t* ch; + if((ch = o->find(label)) != nullptr ) + return ch; + } + } + + return nullptr; +} + + +void cw::object_t::print(const print_ctx_t* c) const +{ + print_ctx_t ctx; + if( c != nullptr ) + ctx = *c; + type->print(this,ctx); +} + + + +cw::rc_t cw::objectFromString( const char* s, object_t*& objRef ) +{ + lexH_t lexH; + rc_t rc; + unsigned lexFlags = 0; + unsigned lexId = kErrorLexTId; + object_t* cnp = _objAllocate(kRootTId,nullptr); + object_t* root = cnp; + + objRef = nullptr; + + if((rc = lexCreate(lexH,s,textLength(s), lexFlags )) != kOkRC ) + return rc; + + // setup the lexer with additional tokens + for(unsigned i=0; _objTokenArray[i].id != cw::kErrorLexTId; ++i) + if((rc = lexRegisterToken( lexH, _objTokenArray[i].id, _objTokenArray[i].label )) != kOkRC ) + { + rc = cwLogError(rc,"Object lexer token registration failed on token id:%i : '%s'",_objTokenArray[i].id, _objTokenArray[i].label); + goto errLabel; + } + + // main parser loop + while((lexId = lexGetNextToken(lexH)) != cw::kErrorLexTId && (lexId != kEofLexTId) && (rc == kOkRC)) + { + + //printf("Lex:%s\n",lexIdToLabel(lexH,lexId)); + + switch( lexId ) + { + case kLCurlyLexTId: + cnp = _objCreateConainerNode( lexH, cnp, kDictTId ); + break; + + case kRCurlyLexTId: + if( cnp == nullptr ) + _objSyntaxError(lexH,"An end of 'object' was encountered without an associated 'object' start."); + else + cnp = cnp->parent; + break; + + case kLHardLexTId: + cnp = _objCreateConainerNode( lexH, cnp, kListTId ); + break; + + case kRHardLexTId: + if( cnp == nullptr ) + rc = _objSyntaxError(lexH,"An end of 'array' was encountered without an associated 'array' start."); + else + cnp = cnp->parent; + break; + + case kColonLexTId: + if( cnp == nullptr || !cnp->is_pair() ) + rc = _objSyntaxError(lexH,"A colon was encountered outside a 'pair' node."); + break; + + case kCommaLexTId: + if( cnp == nullptr || (!cnp->is_list() && !cnp->is_dict()) ) + rc = _objSyntaxError(lexH,"Unexpected comma outside of 'array' or 'object'."); + break; + + case kRealLexTId: + _objCreateValueNode( cnp, lexTokenDouble(lexH), "real" ); + break; + + case kIntLexTId: + _objCreateValueNode( cnp, lexTokenInt(lexH), "int" ); + break; + + case kHexLexTId: + _objCreateValueNode( cnp, lexTokenInt(lexH), "int", kHexFl ); + break; + + case kTrueLexTId: + _objCreateValueNode( cnp, true, "true" ); + break; + + case kFalseLexTId: + _objCreateValueNode( cnp, false, "false" ); + break; + + case kNullLexTId: + _objAppendLeftMostNode( cnp, _objAllocate( kNullTId, cnp )); + break; + + case kIdentLexTId: + case kQStrLexTId: + { + + // if the parent is an object then this string must be a pair label + if( cnp->is_dict() ) + cnp = _objAppendLeftMostNode( cnp, _objAllocate( kPairTId, cnp )); + + + char* v = memDuplStr(lexTokenText(lexH),lexTokenCharCount(lexH)); + unsigned identFl = lexId == kIdentLexTId ? kIdentFl : 0; + + + _objCreateValueNode( cnp, v, "string", identFl ); + } + break; + + case kEofLexTId: + break; + + default: + _objSyntaxError(lexH,"Unknown token type (%i) in text.", int(lexId) ); + } + + // if this is a pair node and it now has both values + // then make the parent 'object' the current node + if( cnp->is_pair() && cnp->child_count()==2 ) + cnp = cnp->parent; + + + } + + objRef = root; + + errLabel: + rc_t rc0 = lexDestroy(lexH); + + return rc != kOkRC ? rc : rc0; + +} + +cw::rc_t cw::objectFromFile( const char* fn, object_t*& objRef ) +{ + rc_t rc = kOkRC; + unsigned bufByteCnt = 0; + char* buf = NULL; + + if(( buf = fileFnToStr(fn, &bufByteCnt)) != NULL ) + { + rc = objectFromString( buf, objRef ); + memRelease(buf); + } + + return rc; +} + + +void cw::objectPrintTypes( object_t* o0 ) +{ + if( o0->is_container() ) + for(object_t* o = o0->u.children; o!=nullptr; o=o->sibling) + objectPrintTypes(o); + + printf("%s ",o0->type->label); +} + + + + + + diff --git a/cwObject.h b/cwObject.h new file mode 100644 index 0000000..88943a4 --- /dev/null +++ b/cwObject.h @@ -0,0 +1,165 @@ +#ifndef cwObject_H +#define cwObject_H + + +namespace cw +{ + + enum + { + kInvalidTId = 0x00000000, + kNullTId = 0x00000001, + kErrorTId = 0x00000002, + kCharTId = 0x00000004, + kInt8TId = 0x00000008, + kUInt8TId = 0x00000010, + kInt16TId = 0x00000020, + kUInt16TId = 0x00000040, + kInt32TId = 0x00000080, + kUInt32TId = 0x00000100, + kInt64TId = 0x00000200, + kUInt64TId = 0x00000400, + kFloatTId = 0x00000800, + kDoubleTId = 0x00001000, + kBoolTId = 0x00002000, + kStringTId = 0x00004000, + kVectTId = 0x00008000, + kPairTId = 0x00010000, + kListTId = 0x00020000, + kDictTId = 0x00040000, + kRootTId = 0x00080000, + + kHexFl = 0x10000000, + kIdentFl = 0x20000000 + + }; + + typedef unsigned objTypeId_t; + + + + enum + { + kValueContainerFl = 0x01, // root,pair, or list are the only legal value containers + kContainerFl = 0x02 + }; + + struct object_str; + struct vect_str; + + typedef struct print_ctx_str + { + unsigned indent = 0; + bool listOnOneLineFl = true; + } print_ctx_t; + + typedef struct type_str + { + objTypeId_t id; + const char* label; + unsigned flags; + + void (*free)( struct object_str* o ); + rc_t (*value)( const struct object_str* o, unsigned tid, void* dst ); + void (*print)( const struct object_str* o, print_ctx_t& c ); + } objType_t; + + + typedef struct object_str + { + objType_t* type = nullptr; + struct object_str* parent = nullptr; + struct object_str* sibling = nullptr; + union + { + char c; + + std::int8_t i8; + std::uint8_t u8; + std::int16_t i16; + std::uint16_t u16; + std::int32_t i32; + std::uint32_t u32; + std::int64_t i64; + std::uint64_t u64; + + bool b; + + float f; + double d; + + char* str; + struct vect_str* vect; + + struct object_str* children; // 'children' is valid when is_container()==true + } u; + + + void free(); + unsigned child_count() const; + + // Value containers are parents of leaf nodes. (A dictionary is not a value container because it's children are pairs with are not leaf nodes.) + inline bool is_value_container() const { return type != nullptr && cwIsFlag(type->flags,kValueContainerFl); } + + // Containers have children and use the object.u.children pointer. + inline bool is_container() const { return type != nullptr && cwIsFlag(type->flags,kContainerFl); } + inline bool is_pair() const { return type != nullptr && type->id == kPairTId; } + inline bool is_dict() const { return type != nullptr && type->id == kDictTId; } + inline bool is_list() const { return type != nullptr && type->id == kListTId; } + + rc_t value( void* dst, unsigned dstTypeId ); + rc_t value( char& v ) const; + rc_t value( int8_t& v ) const; + rc_t value( uint8_t& v ) const; + rc_t value( int16_t& v ) const; + rc_t value( uint16_t& v ) const; + rc_t value( int32_t& v ) const; + rc_t value( uint32_t& v ) const; + rc_t value( int64_t& v ) const; + rc_t value( uint64_t& v ) const; + rc_t value( float& v ) const; + rc_t value( double& v ) const; + rc_t value( char*& v ) const; + + const char* pair_label() const; + const struct object_str* pair_value() const; + const struct object_str* find( const char* label ) const; + + template< typename T > + rc_t get( const char* label, T& v ) const + { + const struct object_str* o; + if((o = find(label)) == NULL ) + return cwLogError(kInvalidIdRC,"The pair label '%s' could not be found.",cwStringNullGuard(label)); + return o->value(v); + } + + rc_t getv() const { return kOkRC; } + + template< typename T0, typename T1, typename... ARGS > + rc_t getv( T0 label, T1* valRef, ARGS ...args ) const + { + rc_t rc; + + if((rc = get(label,*valRef)) == kOkRC ) + rc = getv(args...); + return rc; + } + + + void print(const print_ctx_t* c=NULL) const; + + } object_t; + + + + unsigned object_child_count( const object_t* o ); + + + rc_t objectFromString( const char* s, object_t*& objRef ); + rc_t objectFromFile( const char* fn, object_t*& objRef ); + void objectPrintTypes( object_t* o ); + +} + +#endif diff --git a/cwObjectTemplate.h b/cwObjectTemplate.h new file mode 100644 index 0000000..89095e4 --- /dev/null +++ b/cwObjectTemplate.h @@ -0,0 +1,105 @@ +#ifndef cwObjectTemplate_H +#define cwObjectTemplate_H + + +namespace cw +{ + objType_t* _objIdToType( objTypeId_t tid ); + object_t* _objAllocate( objTypeId_t tid=kInvalidTId, object_t* parent=NULL ); + object_t* _objCreateConainerNode( lexH_t lexH, object_t* parent, objTypeId_t tid ); + object_t* _objAppendLeftMostNode( object_t* parent, object_t* newNode ); + + template< typename T > + object_t* _objSetLeafValue( object_t* obj, T value ) + { + return NULL; + } + + template<> object_t* _objSetLeafValue( object_t* obj, int8_t value ) + { + if( obj != NULL ) + { + obj->u.i8 = value; + obj->type = _objIdToType(kInt8TId); + } + return obj; + } + + template<> object_t* _objSetLeafValue( object_t* obj, int32_t value ) + { + if( obj != NULL ) + { + obj->u.i32 = value; + obj->type = _objIdToType(kInt32TId); + } + return obj; + } + + template<> object_t* _objSetLeafValue( object_t* obj, double value ) + { + if( obj != NULL ) + { + obj->u.d = value; + obj->type = _objIdToType(kDoubleTId); + } + return obj; + } + + template<> object_t* _objSetLeafValue< char*>( object_t* obj, char* value ) + { + if( obj != NULL ) + { + obj->u.str = value; + obj->type = _objIdToType(kStringTId); + } + return obj; + } + + template< typename T > + object_t*_objCreateValueNode( object_t* obj, T value, const char* msg, unsigned flags=0 ) + { + return NULL; + } + + template<> object_t* _objCreateValueNode( object_t* parent, int8_t value, const char* msg, unsigned flags ) + { return _objAppendLeftMostNode( parent, _objSetLeafValue( _objAllocate(), value ) ); } + + template<> object_t* _objCreateValueNode( object_t* parent, int32_t value, const char* msg, unsigned flags ) + { return _objAppendLeftMostNode( parent, _objSetLeafValue( _objAllocate(), value ) ); } + + template<> object_t* _objCreateValueNode( object_t* parent, double value, const char* msg, unsigned flags ) + { return _objAppendLeftMostNode( parent, _objSetLeafValue( _objAllocate(), value ) ); } + + template<> object_t* _objCreateValueNode< char*>( object_t* parent, char* value, const char* msg, unsigned flags ) + { return _objAppendLeftMostNode( parent, _objSetLeafValue( _objAllocate(), value ) ); } + + + template< typename T > + rc_t getObjectValue( const T& src, unsigned tid, void* dst, const char* typeLabel ) + { + rc_t rc = kOkRC; + switch( tid ) + { + case kCharTId: rc = numeric_convert( src, *static_cast(dst) ); break; + case kInt8TId: rc = numeric_convert( src, *static_cast(dst) ); break; + case kUInt8TId: rc = numeric_convert( src, *static_cast(dst) );break; + case kInt16TId: rc = numeric_convert( src, *static_cast(dst) );break; + case kUInt16TId: rc = numeric_convert( src, *static_cast(dst) );break; + case kInt32TId: rc = numeric_convert( src, *static_cast(dst) );break; + case kUInt32TId: rc = numeric_convert( src, *static_cast(dst) );break; + case kInt64TId: rc = numeric_convert( src, *static_cast(dst) );break; + case kUInt64TId: rc = numeric_convert( src, *static_cast(dst) );break; + case kFloatTId: rc = numeric_convert( src, *static_cast(dst) );break; + case kDoubleTId: rc = numeric_convert( src, *static_cast(dst) );break; + case kBoolTId: rc = numeric_convert( src, *static_cast(dst) );break; + default: + cwAssert(0); + rc = cwLogError(kInvalidArgRC,"Invalid destination type id: %i in conversion from '%s'.", tid, typeLabel ); + } + return rc; + } + + +} + +#endif diff --git a/cwText.cpp b/cwText.cpp new file mode 100644 index 0000000..9a06d24 --- /dev/null +++ b/cwText.cpp @@ -0,0 +1,17 @@ +#include "cwCommon.h" +#include "cwLog.h" +#include "cwText.h" +#include "cwCommonImpl.h" +#include "cwMem.h" + + +unsigned cw::textLength( const char* s ) +{ return s == nullptr ? 0 : strlen(s); } + +int cw::textCompare( const char* s0, const char* s1 ) +{ + if( s0 == nullptr || s1 == nullptr ) + return s0==s1 ? 0 : 1; // if both pointers are nullptr then trigger a match + + return strcmp(s0,s1); +} diff --git a/cwText.h b/cwText.h new file mode 100644 index 0000000..aeae373 --- /dev/null +++ b/cwText.h @@ -0,0 +1,13 @@ +#ifndef cwText_H +#define cwText_H + + +namespace cw +{ + unsigned textLength( const char* s ); + + // if both s0 and s1 are nullptr then a match is indicated + int textCompare( const char* s0, const char* s1 ); +} + +#endif diff --git a/cwTextBuf.cpp b/cwTextBuf.cpp new file mode 100644 index 0000000..f55bfa0 --- /dev/null +++ b/cwTextBuf.cpp @@ -0,0 +1,140 @@ +#include "cwCommon.h" +#include "cwLog.h" +#include "cwCommonImpl.h" +#include "cwMem.h" +#include "cwTextBuf.h" + +namespace cw +{ + typedef struct textBuf_str + { + char* buf; + unsigned expandCharN; // count of characters to expand buf + unsigned allocCharN; // current allocated size of buf + unsigned endN; // current count of character in buf + char* boolTrueText; + char* boolFalseText; + int intWidth; + unsigned intFlags; + int floatWidth; + int floatDecPlN; + } textBuf_t; + +} + +#define _textBufHandleToPtr(h) handleToPtr(h) + +cw::rc_t cw::textBufCreate( textBufH_t& hRef, unsigned initCharN, unsigned expandCharN ) +{ + rc_t rc; + if((rc = textBufDestroy(hRef)) != kOkRC ) + return rc; + + textBuf_t* p = memAllocZ(); + p->buf = memAllocZ(initCharN); + p->expandCharN = expandCharN; + p->boolTrueText = memDuplStr("true"); + p->boolFalseText = memDuplStr("false"); + hRef.set(p); + return rc; +} + +cw::rc_t cw::textBufDestroy(textBufH_t& hRef ) +{ + rc_t rc = kOkRC; + if( !hRef.isValid() ) + return rc; + + textBuf_t* p = _textBufHandleToPtr(hRef); + + memRelease(p->buf); + memRelease(p->boolTrueText); + memRelease(p->boolFalseText); + hRef.release(); + return rc; +} + +const char* cw::textBufText( textBufH_t h ) +{ + textBuf_t* p = _textBufHandleToPtr(h); + return p->buf; +} + +cw::rc_t cw::textBufPrintf( textBufH_t h, const char* fmt, va_list vl ) +{ + va_list vl1; + va_copy(vl1,vl); + + textBuf_t* p = _textBufHandleToPtr(h); + + int n = snprintf(nullptr,0,fmt,vl); + + + if( p->endN + n > p->allocCharN ) + { + unsigned minExpandCharN = (p->endN + n) - p->allocCharN; + unsigned expandCharN = std::max(minExpandCharN,p->expandCharN); + p->allocCharN =+ expandCharN; + p->buf = memResizeZ( p->buf, p->allocCharN ); + } + + int m = snprintf(p->buf + p->endN, n, fmt, vl ); + + cwAssert(m=n); + p->endN += n; + + va_end(vl1); + return kOkRC; +} + +cw::rc_t cw::textBufPrintf( textBufH_t h, const char* fmt, ... ) +{ + va_list vl; + va_start(vl,fmt); + rc_t rc = textBufPrintf(h,fmt,vl); + va_end(vl); + return rc; +} + +cw::rc_t cw::textBufPrintBool( textBufH_t h, bool v ) +{ + textBuf_t* p = _textBufHandleToPtr(h); + return textBufPrintf(h,"%s", v ? p->boolTrueText : p->boolFalseText); +} + +cw::rc_t cw::textBufPrintInt( textBufH_t h, int v ) +{ return textBufPrintf(h,"%i",v); } + +cw::rc_t cw::textBufPrintUInt( textBufH_t h, unsigned v ) +{ return textBufPrintf(h,"%i",v); } + +cw::rc_t cw::textBufPrintFloat( textBufH_t h, double v ) +{ return textBufPrintf(h,"%f",v); } + +cw::rc_t cw::textBufSetBoolFormat( textBufH_t h, bool v, const char* s) +{ + textBuf_t* p = _textBufHandleToPtr(h); + + if( v ) + p->boolTrueText = memReallocStr(p->boolTrueText,s); + else + p->boolFalseText = memReallocStr(p->boolFalseText,s); + return kOkRC; +} + +cw::rc_t cw::textBufSetIntFormat( textBufH_t h, unsigned width, unsigned flags ) +{ + textBuf_t* p = _textBufHandleToPtr(h); + p->intWidth = width; + p->intFlags = flags; + return kOkRC; +} + +cw::rc_t cw::textBufSetFloatFormat( textBufH_t h, unsigned width, unsigned decPlN ) +{ + textBuf_t* p = _textBufHandleToPtr(h); + p->floatWidth = width; + p->floatDecPlN = decPlN; + return kOkRC; +} + diff --git a/cwTextBuf.h b/cwTextBuf.h new file mode 100644 index 0000000..43a4e30 --- /dev/null +++ b/cwTextBuf.h @@ -0,0 +1,30 @@ +#ifndef cwTextBuf_H +#define cwTextBuf_H + + +namespace cw +{ + typedef handle textBufH_t; + + rc_t textBufCreate( textBufH_t& hRef, unsigned initCharN=1024, unsigned expandCharN=1024 ); + rc_t textBufDestroy(textBufH_t& hRef ); + + const char* textBufText( textBufH_t h); + + rc_t textBufPrintf( textBufH_t h, const char* fmt, va_list vl ); + rc_t textBufPrintf( textBufH_t h, const char* fmt, ... ); + + rc_t textBufPrintBool( textBufH_t h, bool v ); + rc_t textBufPrintInt( textBufH_t h, int v ); + rc_t textBufPrintUInt( textBufH_t h, unsigned v ); + rc_t textBufPrintFloat( textBufH_t h, double v ); + + rc_t textBufSetBoolFormat( textBufH_t h, bool v, const char* s); + rc_t textBufSetIntFormat( textBufH_t h, unsigned width, unsigned flags ); + rc_t textBufSetFloatFormat( textBufH_t h, unsigned width, unsigned decPlN ); + + + +} + +#endif diff --git a/cwThread.cpp b/cwThread.cpp new file mode 100644 index 0000000..727260f --- /dev/null +++ b/cwThread.cpp @@ -0,0 +1,262 @@ +#include "cwCommon.h" +#include "cwLog.h" +#include "cwCommonImpl.h" +#include "cwMem.h" +#include "cwThread.h" + +#include + +namespace cw +{ + + enum + { + kDoExitThFl = 0x01, + kDoPauseThFl = 0x02, + kDoRunThFl = 0x04 + }; + + typedef struct thread_str + { + pthread_t pThreadH; + kThreadStateId_t stateId; + threadFunc_t func; + void* funcArg; + unsigned doFlags; + unsigned stateMicros; + unsigned pauseMicros; + unsigned sleepMicros = 15000; + } thread_t; + +#define _threadHandleToPtr(h) handleToPtr(h) + + rc_t _waitForState( thread_t* p, unsigned stateId ) + { + unsigned waitTimeMicroSecs = 0; + + while( p->stateId != stateId && waitTimeMicroSecs < p->stateMicros ) + { + sleepUs( p->sleepMicros ); + waitTimeMicroSecs += p->sleepMicros; + } + + return p->stateId==stateId ? kOkRC : kTimeOutRC; + } + + void _threadCleanUpCallback(void* p) + { + ((thread_t*)p)->stateId = kExitedThId; + } + + + void* _threadCallback(void* param) + { + thread_t* p = (thread_t*)param; + + // set a clean up handler - this will be called when the + // thread terminates unexpectedly or pthread_cleanup_pop() is called. + pthread_cleanup_push(_threadCleanUpCallback,p); + + while( cwIsFlag(p->doFlags,kDoExitThFl) == false ) + { + + // if we are in the pause state + if( p->stateId == kPausedThId ) + { + + sleepUs( p->pauseMicros ); + + // check if we have been requested to leave the pause state + if( cwIsFlag(p->doFlags,kDoRunThFl) ) + { + p->doFlags = cwClrFlag(p->doFlags,kDoRunThFl); + p->stateId = kRunningThId; + } + } + else + { + // call the user-defined function + if( p->func(p->funcArg)==false ) + break; + + // check if we have been requested to enter the pause state + if( cwIsFlag(p->doFlags,kDoPauseThFl) ) + { + p->doFlags = cwClrFlag(p->doFlags,kDoPauseThFl); + p->stateId = kPausedThId; + } + } + } + + pthread_cleanup_pop(1); + + pthread_exit(NULL); + + return p; + } + +} + + +cw::rc_t cw::threadCreate( threadH_t& hRef, threadFunc_t func, void* funcArg, int stateMicros, int pauseMicros ) +{ + rc_t rc; + int sysRC; + + if((rc = threadDestroy(hRef)) != kOkRC ) + return rc; + + thread_t* p = memAllocZ(); + + p->func = func; + p->funcArg = funcArg; + p->stateMicros = stateMicros; + p->pauseMicros = pauseMicros; + p->stateId = kPausedThId; + + if((sysRC = pthread_create(&p->pThreadH,NULL, _threadCallback, (void*)p )) != 0 ) + { + p->stateId = kNotInitThId; + rc = cwLogSysError(kOpFailRC,sysRC,"Thread create failed."); + } + + hRef.set(p); + return rc; +} + +cw::rc_t cw::threadDestroy( threadH_t& hRef ) +{ + rc_t rc = kOkRC; + + if( !hRef.isValid() ) + return rc; + + thread_t* p = _threadHandleToPtr(hRef); + + // tell the thread to exit + p->doFlags = cwSetFlag(p->doFlags,kDoExitThFl); + + // wait for the thread to exit and then deallocate the thread object + if((rc = _waitForState(p,kExitedThId)) != kOkRC ) + return cwLogError(rc,"Thread timed out waiting for destroy."); + + hRef.release(); + + return rc; +} + + +cw::rc_t cw::threadPause( threadH_t& h, unsigned cmdFlags ) +{ + rc_t rc = kOkRC; + bool pauseFl = cwIsFlag(cmdFlags,kThreadPauseFl); + bool waitFl = cwIsFlag(cmdFlags,kThreadWaitFl); + thread_t* p = _threadHandleToPtr(h); + bool isPausedFl = p->stateId == kPausedThId; + unsigned waitId; + + if( isPausedFl == pauseFl ) + return kOkRC; + + if( pauseFl ) + { + p->doFlags = cwSetFlag(p->doFlags,kDoPauseThFl); + waitId = kPausedThId; + } + else + { + p->doFlags = cwSetFlag(p->doFlags,kDoRunThFl); + waitId = kRunningThId; + } + + if( waitFl ) + rc = _waitForState(p,waitId); + + if( rc != kOkRC ) + cwLogError(rc,"Thread timed out waiting for '%s'.", pauseFl ? "pause" : "un-pause"); + + return rc; + +} + +cw::kThreadStateId_t cw::threadState( threadH_t h ) +{ + thread_t* p = _threadHandleToPtr(h); + return p->stateId; +} + + +namespace cw +{ + bool _threadTestCb( void* p ) + { + unsigned* ip = (unsigned*)p; + ip[0]++; + return true; + } +} + +cw::rc_t cw::threadTest() +{ + threadH_t h; + unsigned val = 0; + rc_t rc; + char c = 0; + + if((rc = threadCreate(h,_threadTestCb,&val)) != kOkRC ) + return rc; + + if((rc = threadPause(h,0)) != kOkRC ) + goto errLabel; + + + cwLogInfo("o=print p=pause s=state q=quit\n"); + + while( c != 'q' ) + { + + c = (char)fgetc(stdin); + fflush(stdin); + + switch(c) + { + case 'o': + cwLogInfo("val: 0x%x\n",val); + break; + + case 's': + cwLogInfo("state=%i\n",threadState(h)); + break; + + case 'p': + { + if( threadState(h) == kPausedThId ) + rc = threadPause(h,kThreadWaitFl); + else + rc = threadPause(h,kThreadPauseFl|kThreadWaitFl); + + if( rc == kOkRC ) + cwLogInfo("new state:%i\n", threadState(h)); + else + { + cwLogError(rc,"threadPause() test failed."); + goto errLabel; + } + + } + break; + + case 'q': + break; + + //default: + //cwLogInfo("Unknown:%c\n",c); + + } + } + + errLabel: + rc_t rc0 = rc = threadDestroy(h); + + return rc == kOkRC ? rc0 : rc; +} diff --git a/cwThread.h b/cwThread.h new file mode 100644 index 0000000..5ed1e94 --- /dev/null +++ b/cwThread.h @@ -0,0 +1,30 @@ +#ifndef cwThread_H +#define cwThread_H + +namespace cw +{ + typedef enum + { + kNotInitThId, + kPausedThId, + kRunningThId, + kExitedThId + } kThreadStateId_t; + + typedef handle threadH_t; + + typedef bool (*threadFunc_t)( void* arg ); + + // stateMicros = time out duration for switching in/out of pause or in to exit + // pauseMicros = duration of thread sleep interval when in paused state. + rc_t threadCreate( threadH_t& hRef, threadFunc_t func, void* funcArg, int stateTimeOutMicros=100000, int pauseMicros=10000 ); + rc_t threadDestroy( threadH_t& hRef ); + + enum { kThreadPauseFl=0x01, kThreadWaitFl=0x02 }; + rc_t threadPause( threadH_t& h, unsigned cmdFlags ); + kThreadStateId_t threadState( threadH_t h ); + + rc_t threadTest(); +} + +#endif diff --git a/main.cfg b/main.cfg new file mode 100644 index 0000000..898901b --- /dev/null +++ b/main.cfg @@ -0,0 +1,8 @@ +{ + + a:1, + b:2, + c:[1.23,4.56,7.89] + + +} \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..72b163d --- /dev/null +++ b/main.cpp @@ -0,0 +1,174 @@ +#include "cwCommon.h" +#include "cwLog.h" +#include "cwCommonImpl.h" +#include "cwMem.h" +#include "cwFileSys.h" +#include "cwLex.h" +#include "cwNumericConvert.h" +#include "cwObject.h" +#include "cwThread.h" +#include "cwText.h" + +#include + + +void print() +{ + printf("\n"); +} + +template + void print(T0 t0, T1 t1, ARGS ...args) +{ + static const unsigned short int size = sizeof...(ARGS); + std::cout << t0 << ":" << t1 << " (" << size << "), "; + print(args...); +} + +void get(int) +{ + printf("\n"); +} + +template + void get(int n, T0 t0, T1* t1, ARGS ...args) +{ + std::cout << t0 << ":" " (" << n << "), "; + *t1 = n; + get(n+1,args...); +} + + +using namespace std; + + +void fileSysTest( cw::object_t* cfg, int argc, char* argv[] ) +{ + cw::fileSysPathPart_t* pp = cw::fileSysPathParts(__FILE__); + + cwLogInfo("dir:%s",pp->dirStr); + cwLogInfo("fn: %s",pp->fnStr); + cwLogInfo("ext:%s",pp->extStr); + + char* fn = cw::fileSysMakeFn( pp->dirStr, pp->fnStr, pp->extStr, nullptr ); + + cwLogInfo("fn: %s",fn); + + cw::memRelease(pp); + cw::memRelease(fn); + +} + +void numbCvtTest( cw::object_t* cfg, int argc, char* argv[] ) +{ + int8_t x0 = 3; + int x1 = 127; + + cw::numeric_convert( x1, x0 ); + printf("%i %i\n",x0,x1); + + + int v0; + double v1; + cw::string_to_number("123",v0); + cw::string_to_number("3.4",v1); + printf("%i %f\n",v0,v1 ); + +} + +void objectTest( cw::object_t* cfg, int argc, char* argv[] ) +{ + cw::object_t* o; + const char s [] = "{ a:1, b:2, c:[ 1.23, 4.56 ] }"; + cw::objectFromString(s,o); + + int v; + o->get("b",v); + printf("value:%i\n",v); + + o->print(); + + int a = 0; + int b = 0; + + o->getv("a",&a,"b",&b); + printf("G: %i %i\n",a,b); + + o->free(); + +} + +void threadTest( cw::object_t* cfg, int argc, char* argv[] ) +{ + cw::threadTest(); +} + +void variadicTplTest( cw::object_t* cfg, int argc, char* argv[] ) +{ + print("a", 1, "b", 3.14, "c",5L); + + int v0=0,v1=0,v2=0; + get(0, "a", &v0, "b", &v1, "c", &v2); + printf("%i %i %i",v0,v1,v2); + + printf("\n"); +} + + + +int main( int argc, char* argv[] ) +{ + + typedef struct func_str + { + const char* label; + void (*func)(cw::object_t* cfg, int argc, char* argv[] ); + } func_t; + + // function dispatch list + func_t modeArray[] = + { + { "fileSys", fileSysTest }, + { "numbCvt", numbCvtTest }, + { "object", objectTest }, + { "thread", threadTest }, + { "variadicTpl", variadicTplTest }, + { nullptr, nullptr } + }; + + // read the command line + cw::object_t* cfg = NULL; + const char* cfgFn = argc > 1 ? argv[1] : nullptr; + const char* mode = argc > 2 ? argv[2] : nullptr; + + + cw::logCreateGlobal(); + + // if valid command line args were given and the cfg file was successfully read + if( cfgFn != nullptr && mode != nullptr && objectFromFile( cfgFn, cfg ) == cw::kOkRC ) + { + int i; + // locate the requested function and call it + for(i=0; modeArray[i].label!=nullptr; ++i) + if( cw::textCompare(modeArray[i].label,mode)==0 ) + { + modeArray[i].func( cfg, argc-2, argv + 2 ); + break; + } + + // if the requested function was not found + if( modeArray[i].label == nullptr ) + cwLogError(cw::kInvalidArgRC,"The mode selector: '%s' is not valid.", cwStringNullGuard(mode)); + + cfg->free(); + } + + cw::logDestroyGlobal(); + + return 0; +} + + + + + diff --git a/valgrind_test.sh b/valgrind_test.sh new file mode 100755 index 0000000..7754f78 --- /dev/null +++ b/valgrind_test.sh @@ -0,0 +1,3 @@ +valgrind --track-origins=yes --leak-check=yes --log-file=vg0.txt ./cw_rt $1 $2 + +