From 3954d794f1d06324b28757fd5b11dc862028b6cc Mon Sep 17 00:00:00 2001 From: kpl Date: Wed, 18 Dec 2019 22:24:12 -0500 Subject: [PATCH] Initial commit --- Makefile | 18 + README.md | 9 + cwCommon.h | 66 ++++ cwCommonImpl.cpp | 76 ++++ cwCommonImpl.h | 72 ++++ cwFile.cpp | 799 +++++++++++++++++++++++++++++++++++++++ cwFile.h | 212 +++++++++++ cwFileSys.cpp | 345 +++++++++++++++++ cwFileSys.h | 45 +++ cwLex.cpp | 922 +++++++++++++++++++++++++++++++++++++++++++++ cwLex.h | 140 +++++++ cwLog.cpp | 209 ++++++++++ cwLog.h | 107 ++++++ cwMem.cpp | 95 +++++ cwMem.h | 147 ++++++++ cwNumericConvert.h | 130 +++++++ cwObject.cpp | 547 +++++++++++++++++++++++++++ cwObject.h | 165 ++++++++ cwObjectTemplate.h | 105 ++++++ cwText.cpp | 17 + cwText.h | 13 + cwTextBuf.cpp | 140 +++++++ cwTextBuf.h | 30 ++ cwThread.cpp | 262 +++++++++++++ cwThread.h | 30 ++ main.cfg | 8 + main.cpp | 174 +++++++++ valgrind_test.sh | 3 + 28 files changed, 4886 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 cwCommon.h create mode 100644 cwCommonImpl.cpp create mode 100644 cwCommonImpl.h create mode 100644 cwFile.cpp create mode 100644 cwFile.h create mode 100644 cwFileSys.cpp create mode 100644 cwFileSys.h create mode 100644 cwLex.cpp create mode 100644 cwLex.h create mode 100644 cwLog.cpp create mode 100644 cwLog.h create mode 100644 cwMem.cpp create mode 100644 cwMem.h create mode 100644 cwNumericConvert.h create mode 100644 cwObject.cpp create mode 100644 cwObject.h create mode 100644 cwObjectTemplate.h create mode 100644 cwText.cpp create mode 100644 cwText.h create mode 100644 cwTextBuf.cpp create mode 100644 cwTextBuf.h create mode 100644 cwThread.cpp create mode 100644 cwThread.h create mode 100644 main.cfg create mode 100644 main.cpp create mode 100755 valgrind_test.sh 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 + +