Initial commit
This commit is contained in:
commit
3954d794f1
18
Makefile
Normal file
18
Makefile
Normal 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
9
README.md
Normal 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
66
cwCommon.h
Normal 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
76
cwCommonImpl.cpp
Normal 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
72
cwCommonImpl.h
Normal 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
799
cwFile.cpp
Normal 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
212
cwFile.h
Normal 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
345
cwFileSys.cpp
Normal 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
45
cwFileSys.h
Normal 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
922
cwLex.cpp
Normal 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
140
cwLex.h
Normal 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
209
cwLog.cpp
Normal 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
107
cwLog.h
Normal 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
95
cwMem.cpp
Normal 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
147
cwMem.h
Normal 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
130
cwNumericConvert.h
Normal 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
547
cwObject.cpp
Normal 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
165
cwObject.h
Normal 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
105
cwObjectTemplate.h
Normal 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
17
cwText.cpp
Normal 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
13
cwText.h
Normal 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
140
cwTextBuf.cpp
Normal 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
30
cwTextBuf.h
Normal 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
262
cwThread.cpp
Normal 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
30
cwThread.h
Normal 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
|
174
main.cpp
Normal file
174
main.cpp
Normal 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
3
valgrind_test.sh
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
valgrind --track-origins=yes --leak-check=yes --log-file=vg0.txt ./cw_rt $1 $2
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user