Initial commit

This commit is contained in:
kpl 2019-12-18 22:24:12 -05:00
commit 3954d794f1
28 changed files with 4886 additions and 0 deletions

18
Makefile Normal file
View File

@ -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)

9
README.md Normal file
View File

@ -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.

66
cwCommon.h Normal file
View File

@ -0,0 +1,66 @@
#ifndef cwCOMMON_H
#define cwCOMMON_H
#include <cstdio> // declares 'NULL'
#include <cstdarg>
#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

76
cwCommonImpl.cpp Normal file
View File

@ -0,0 +1,76 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include <time.h>
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);
}

72
cwCommonImpl.h Normal file
View File

@ -0,0 +1,72 @@
#ifndef cwCommonImpl_H
#define cwCommonImpl_H
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <cctype>
#include <cerrno>
#include <climits>
#include <cinttypes>
#include <cfloat>
#include <algorithm> // std::min,std::max
#include <utility> // std::forward
#include <limits> // std::numeric_limits<
#if defined(cwLINUX) || defined(cwOSX)
#define cwPOSIX_FILE_SYS
#include <time.h> // 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

799
cwFile.cpp Normal file
View File

@ -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 <sys/stat.h>
#endif
namespace cw
{
typedef struct file_str
{
FILE* fp;
char* fnStr;
} file_t;
#define _fileHandleToPtr(h) handleToPtr<fileH_t,file_t>(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<char>(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<file_t>(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<char>(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<char>(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;
}

212
cwFile.h Normal file
View File

@ -0,0 +1,212 @@
#ifndef cwFile_H
#define cwFile_H
namespace cw
{
typedef handle<struct file_str> 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 <N> <char0> <char1> ... <char(N-1)>
// 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

345
cwFileSys.cpp Normal file
View File

@ -0,0 +1,345 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwFileSys.h"
#include "cwCommonImpl.h"
#include "cwMem.h"
#ifdef cwLINUX
#include <libgen.h>
#include <sys/stat.h>
#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);
if((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<char>( 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<fileSysPathPart_t>( 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;
}

45
cwFileSys.h Normal file
View File

@ -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

922
cwLex.cpp Normal file
View File

@ -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<lexH_t,lex_t>(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(; i<cn; ++i)
if( !isspace(cp[i]) )
break;
return i;
}
unsigned _lexRealMatcher( lex_t* p, const char* cp, unsigned cn, const char* keyStr )
{
unsigned i = 0;
unsigned n = 0; // decimal point counter
unsigned d = 0; // digit counter
bool fl = false; // expo flag
for(; i<cn && n<=1; ++i)
{
if( i==0 && cp[i]=='-' ) // allow a leading '-'
continue;
if( isdigit(cp[i]) ) // allow digits
{
++d;
continue;
}
if( cp[i] == '.' && n==0 ) // allow exactly one decimal point
++n;
else
break;
}
// if there was at least one digit and the next char is an 'e'
if( d>0 && i<cn && (cp[i] == 'e' || cp[i] == 'E') )
{
unsigned e=0;
++i;
unsigned j = i;
fl = false;
for(; i<cn; ++i)
{
if( i==j && cp[i]=='-' ) // allow the char following the e to be '-'
continue;
if( isdigit(cp[i]) )
{
++e;
++d;
continue;
}
// stop at the first non-digit
break;
}
// an exp exists if digits follwed the 'e'
fl = e > 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(i<cn)
{
// if the real has a suffix
switch(cp[i])
{
case 'F':
case 'f':
p->attrFlags = 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<cn; ++i)
{
if( i==0 && cp[i]=='-' )
{
signFl = true;
continue;
}
if( !isdigit(cp[i]) )
break;
++digitCnt;
}
// BUG BUG BUG
// If an integer is specified using 'e' notiation
// (see _lexRealMatcher()) and the number of exponent places
// specified following the 'e' is positive and >= 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(i<cn )
{
switch(cp[i])
{
case 'u':
case 'U':
if( signFl )
cwLogError(kSyntaxErrorRC,"A signed integer has a 'u' or 'U' suffix on line %i",p->curLine);
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; i<cn; ++i)
if( !isxdigit(cp[i]) )
break;
return i;
}
unsigned _lexIdentMatcher( lex_t* p, const char* cp, unsigned cn, const char* keyStr )
{
unsigned i = 0;
if( isalpha(cp[0]) || (cp[0]== '_'))
{
i = 1;
for(; i<cn; ++i)
if( !isalnum(cp[i]) && (cp[i] != '_') )
break;
}
return i;
}
unsigned _lexQStrMatcher( lex_t* p, const char* cp, unsigned cn, const char* keyStr )
{
bool escFl = false;
unsigned i = 0;
if( cp[i] != '"' )
return 0;
for(i=1; i<cn; ++i)
{
if( escFl )
{
escFl = false;
continue;
}
if( cp[i] == '\\' )
{
escFl = true;
continue;
}
if( cp[i] == '"' )
return i+1;
}
return cwLogError(kSyntaxErrorRC, "Missing string literal end quote on line: %i.", p->curLine);
}
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<lexMatcher>( 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(; i<p->mfi; ++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<lex_t>();
p->flags = flags;
_lexSetTextBuffer( p, cp, cn );
int init_mfn = 10;
p->mfp = memAllocZ<lexMatcher>( 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(; i<p->mfi; ++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<char>(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(; mi<p->mfi; ++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(; mi<p->mfi; ++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 && charCnt<maxCharCnt && p->mfp[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; i<maxCharCnt; ++i)
{
if( _lexIsNewline(p->cp[ 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 "<error>";
case kEofLexTId: return "<EOF>";
case kSpaceLexTId: return "<space>";
case kRealLexTId: return "<real>";
case kIntLexTId: return "<int>";
case kHexLexTId: return "<hex>";
case kIdentLexTId: return "<ident>";
case kQStrLexTId: return "<qstr>";
case kBlockCmtLexTId: return "<bcmt>";
case kLineCmtLexTId: return "<lcmt>";
default:
{
lexMatcher* mp;
if((mp = _lexFindUserToken(p,typeId,nullptr)) == nullptr )
return "<unknown>";
return mp->tokenStr;
}
}
return "<invalid>";
}
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);
}
}
//)
//}

140
cwLex.h Normal file
View File

@ -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<struct lex_str> 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

209
cwLog.cpp Normal file
View File

@ -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<logHandle_t,log_t>(h)
idLabelPair_t logLevelLabelArray[] =
{
{ kDebug_LogLevel, "debug" },
{ kInfo_LogLevel, "info" },
{ kWarning_LogLevel, "warning" },
{ kError_LogLevel, "error" },
{ kFatal_LogLevel, "fatal" },
{ kInvalid_LogLevel, "<invalid>" }
};
}
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<log_t>();
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 = "<unknown>";
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__;
}

107
cwLog.h Normal file
View File

@ -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<struct log_str> 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

95
cwMem.cpp Normal file
View File

@ -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<unsigned*>(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<const unsigned*>(p)[-1];
}
char* cw::memAllocStr( const char* s )
{
char* s1 = nullptr;
if( s != nullptr )
{
unsigned sn = strlen(s);
s1 = static_cast<char*>(_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<unsigned*>(p)-1);
}
}

147
cwMem.h Normal file
View File

@ -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<typename T>
void memRelease(T& p) { memFree(p); p=nullptr; }
template<typename T>
T* memAllocZ(unsigned n=1) { return static_cast<T*>(_memAlloc(nullptr,n*sizeof(T),true)); }
template<typename T>
T* memAlloc(unsigned n=1) { return static_cast<T*>(_memAlloc(nullptr,n*sizeof(T),false)); }
template<typename T>
T* memResizeZ(T* p, unsigned n=1) { return static_cast<T*>(_memAlloc(p,n*sizeof(T),true)); }
template<typename T>
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<typename T>
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<T>(n);
// copy in new string
for(size_t i=0; i<n-1; ++i)
s1[i] = s[i];
s1[n-1] = 0;
return s1;
}
template<typename T>
T* memDuplStr( const T* s )
{
if( s == nullptr )
return nullptr;
return memDuplStr(s,_memTextLength(s));
}
template<typename T>
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<typename C>
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<typename C>
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

130
cwNumericConvert.h Normal file
View File

@ -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<char>(){ return 0; }
template <> inline int8_t minimum_value<int8_t>(){ return INT8_MIN; }
template <> inline int16_t minimum_value<int16_t>(){ return INT16_MIN; }
template <> inline int32_t minimum_value<int32_t>(){ return INT32_MIN; }
template <> inline int64_t minimum_value<int64_t>(){ return INT64_MIN; }
template <> inline float minimum_value<float>(){ return FLT_MIN; }
template <> inline double minimum_value<double>(){ return DBL_MIN; }
template< typename T >
T maximum_value() { cwAssert(0); }
template <> inline char maximum_value<char>(){ return 255; }
template <> inline int8_t maximum_value<int8_t>(){ return INT8_MAX; }
template <> inline int16_t maximum_value<int16_t>(){ return INT16_MAX; }
template <> inline int32_t maximum_value<int32_t>(){ return INT32_MAX; }
template <> inline int64_t maximum_value<int64_t>(){ return INT64_MAX; }
template <> inline uint8_t maximum_value<uint8_t>(){ return UINT8_MAX; }
template <> inline uint16_t maximum_value<uint16_t>(){ return UINT16_MAX; }
template <> inline uint32_t maximum_value<uint32_t>(){ return UINT32_MAX; }
template <> inline uint64_t maximum_value<uint64_t>(){ return UINT64_MAX; }
template <> inline bool maximum_value<bool>(){ std::numeric_limits<bool>::max(); }
template <> inline float maximum_value<float>(){ return FLT_MAX; }
template <> inline double maximum_value<double>(){ 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<DST_t>::min();
double d_max = std::numeric_limits<DST_t>::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<double>( 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<float>( const char* s, float& valueRef )
{
double d;
rc_t rc;
if((rc = string_to_number<double>(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

547
cwObject.cpp Normal file
View File

@ -0,0 +1,547 @@
#include <type_traits>
#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<char>(o->u.c,"%c",buf,bufN); }
void _objTypeInt8ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string<int8_t>(o->u.i8,"%i",buf,bufN); }
void _objTypeUInt8ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string<uint8_t>(o->u.u8,"%i",buf,bufN); }
void _objTypeInt16ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string<int16_t>(o->u.i16,"%i",buf,bufN); }
void _objTypeUInt16ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string<uint16_t>(o->u.u16,"%i",buf,bufN); }
void _objTypeInt32ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string<int32_t>(o->u.i32,"%i",buf,bufN); }
void _objTypeUInt32ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string<uint32_t>(o->u.u32,"%i",buf,bufN); }
void _objTypeInt64ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string<int64_t>(o->u.i64,"%i",buf,bufN); }
void _objTypeUInt64ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string<uint64_t>(o->u.u64,"%i",buf,bufN); }
void _objTypeBoolToString( object_t* o, char* buf, unsigned bufN ) { number_to_string<bool>(o->u.b,"%i",buf,bufN); }
void _objTypeFloatToString( object_t* o, char* buf, unsigned bufN ) { number_to_string<float>(o->u.f,"%f",buf,bufN); }
void _objTypeDoubleToString( object_t* o, char* buf, unsigned bufN ) { number_to_string<double>(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; i<indent; ++i)
printf(indentStr);
printf("%s",text);
}
void _objTypePrintChild( const object_t* o, print_ctx_t& c, const char* eolStr=",\n", const char* indentStr=" " )
{
_objTypePrintIndent(" ",c.indent,indentStr);
o->type->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("<vect>"); }
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, "<invalid>", 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 "<invalid>";
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<object_t>();
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<char*>( 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);
}

165
cwObject.h Normal file
View File

@ -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

105
cwObjectTemplate.h Normal file
View File

@ -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<int8_t>( object_t* obj, int8_t value )
{
if( obj != NULL )
{
obj->u.i8 = value;
obj->type = _objIdToType(kInt8TId);
}
return obj;
}
template<> object_t* _objSetLeafValue<int32_t>( object_t* obj, int32_t value )
{
if( obj != NULL )
{
obj->u.i32 = value;
obj->type = _objIdToType(kInt32TId);
}
return obj;
}
template<> object_t* _objSetLeafValue<double>( 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<int8_t>( object_t* parent, int8_t value, const char* msg, unsigned flags )
{ return _objAppendLeftMostNode( parent, _objSetLeafValue( _objAllocate(), value ) ); }
template<> object_t* _objCreateValueNode<int32_t>( object_t* parent, int32_t value, const char* msg, unsigned flags )
{ return _objAppendLeftMostNode( parent, _objSetLeafValue( _objAllocate(), value ) ); }
template<> object_t* _objCreateValueNode<double>( 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<char*>(dst) ); break;
case kInt8TId: rc = numeric_convert( src, *static_cast<int8_t*>(dst) ); break;
case kUInt8TId: rc = numeric_convert( src, *static_cast<uint8_t*>(dst) );break;
case kInt16TId: rc = numeric_convert( src, *static_cast<int16_t*>(dst) );break;
case kUInt16TId: rc = numeric_convert( src, *static_cast<uint16_t*>(dst) );break;
case kInt32TId: rc = numeric_convert( src, *static_cast<int32_t*>(dst) );break;
case kUInt32TId: rc = numeric_convert( src, *static_cast<uint32_t*>(dst) );break;
case kInt64TId: rc = numeric_convert( src, *static_cast<int64_t*>(dst) );break;
case kUInt64TId: rc = numeric_convert( src, *static_cast<uint64_t*>(dst) );break;
case kFloatTId: rc = numeric_convert( src, *static_cast<float*>(dst) );break;
case kDoubleTId: rc = numeric_convert( src, *static_cast<double*>(dst) );break;
case kBoolTId: rc = numeric_convert( src, *static_cast<bool*>(dst) );break;
default:
cwAssert(0);
rc = cwLogError(kInvalidArgRC,"Invalid destination type id: %i in conversion from '%s'.", tid, typeLabel );
}
return rc;
}
}
#endif

17
cwText.cpp Normal file
View File

@ -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);
}

13
cwText.h Normal file
View File

@ -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

140
cwTextBuf.cpp Normal file
View File

@ -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<textBufH_t,textBuf_t>(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<textBuf_t>();
p->buf = memAllocZ<char>(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<char>( 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;
}

30
cwTextBuf.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef cwTextBuf_H
#define cwTextBuf_H
namespace cw
{
typedef handle<struct textBuf_str> 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

262
cwThread.cpp Normal file
View File

@ -0,0 +1,262 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwMem.h"
#include "cwThread.h"
#include <pthread.h>
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<threadH_t,thread_t>(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<thread_t>();
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;
}

30
cwThread.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef cwThread_H
#define cwThread_H
namespace cw
{
typedef enum
{
kNotInitThId,
kPausedThId,
kRunningThId,
kExitedThId
} kThreadStateId_t;
typedef handle<struct thread_str> 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

8
main.cfg Normal file
View File

@ -0,0 +1,8 @@
{
a:1,
b:2,
c:[1.23,4.56,7.89]
}

174
main.cpp Normal file
View File

@ -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 <iostream>
void print()
{
printf("\n");
}
template<typename T0, typename T1, typename... ARGS>
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<typename T0, typename T1, typename... ARGS>
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;
}

3
valgrind_test.sh Executable file
View File

@ -0,0 +1,3 @@
valgrind --track-origins=yes --leak-check=yes --log-file=vg0.txt ./cw_rt $1 $2