From f4c20290509d7ed3a8fd5304577e7a4cc0b9d974 Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Tue, 3 Apr 2007 03:49:53 +0000 Subject: Ok, no code is left in src, it's all in src/old. We'll gradually move code back into src as it's fixed and re-org'd. This includes tests, which, I may write a unit test system into libbu++ just to make my life easier. --- src/old/arraylist.cpp | 100 ++++ src/old/arraylist.h | 80 +++ src/old/cgi.cpp | 644 ++++++++++++++++++++ src/old/cgi.h | 196 +++++++ src/old/configmanagerbase.cpp | 63 ++ src/old/configmanagerbase.h | 24 + src/old/confpair.cpp | 2 + src/old/confpair.h | 81 +++ src/old/confpairbase.cpp | 17 + src/old/confpairbase.h | 24 + src/old/conftree.cpp | 9 + src/old/conftree.h | 19 + src/old/connection.cpp | 564 ++++++++++++++++++ src/old/connection.h | 411 +++++++++++++ src/old/connectionmanager.cpp | 397 +++++++++++++ src/old/connectionmanager.h | 169 ++++++ src/old/connectionmonitor.cpp | 10 + src/old/connectionmonitor.h | 47 ++ src/old/exceptionbase.cpp | 70 +++ src/old/exceptionbase.h | 105 ++++ src/old/exceptions.cpp | 8 + src/old/exceptions.h | 25 + src/old/flexbuf.cpp | 229 ++++++++ src/old/flexbuf.h | 162 ++++++ src/old/formula.cpp | 262 +++++++++ src/old/formula.h | 77 +++ src/old/fstring.cpp | 13 + src/old/fstring.h | 651 +++++++++++++++++++++ src/old/hash.cpp | 113 ++++ src/old/hash.h | 744 ++++++++++++++++++++++++ src/old/hashable.cpp | 1 + src/old/hashable.h | 12 + src/old/hashfunction.cpp | 10 + src/old/hashfunction.h | 48 ++ src/old/hashfunctioncasestring.cpp | 39 ++ src/old/hashfunctioncasestring.h | 28 + src/old/hashfunctionint.cpp | 20 + src/old/hashfunctionint.h | 26 + src/old/hashfunctionstring.cpp | 51 ++ src/old/hashfunctionstring.h | 27 + src/old/hashtable.cpp | 424 ++++++++++++++ src/old/hashtable.h | 308 ++++++++++ src/old/http.cpp | 377 ++++++++++++ src/old/http.h | 273 +++++++++ src/old/httpget.cpp | 263 +++++++++ src/old/httpget.h | 44 ++ src/old/linkedlist.cpp | 210 +++++++ src/old/linkedlist.h | 87 +++ src/old/linkmessage.cpp | 44 ++ src/old/linkmessage.h | 39 ++ src/old/linkmessenger.cpp | 41 ++ src/old/linkmessenger.h | 32 + src/old/list.cpp | 10 + src/old/list.h | 101 ++++ src/old/md5.cpp | 190 ++++++ src/old/md5.h | 81 +++ src/old/multilog.cpp | 102 ++++ src/old/multilog.h | 130 +++++ src/old/multilogchannel.cpp | 13 + src/old/multilogchannel.h | 46 ++ src/old/multilogtext.cpp | 188 ++++++ src/old/multilogtext.h | 70 +++ src/old/ordhash.cpp | 1 + src/old/ordhash.h | 104 ++++ src/old/paramproc.cpp | 514 ++++++++++++++++ src/old/paramproc.h | 153 +++++ src/old/plugger.cpp | 1 + src/old/plugger.h | 198 +++++++ src/old/pqueue.cpp | 33 ++ src/old/pqueue.h | 48 ++ src/old/programchain.cpp | 96 +++ src/old/programchain.h | 95 +++ src/old/programlink.cpp | 54 ++ src/old/programlink.h | 99 ++++ src/old/protocol.cpp | 20 + src/old/protocol.h | 62 ++ src/old/protocoltelnet.cpp | 316 ++++++++++ src/old/protocoltelnet.h | 77 +++ src/old/queue.cpp | 26 + src/old/queue.h | 45 ++ src/old/ringlist.cpp | 106 ++++ src/old/ringlist.h | 112 ++++ src/old/sbuffer.cpp | 67 +++ src/old/sbuffer.h | 40 ++ src/old/serializable.cpp | 8 + src/old/serializable.h | 34 ++ src/old/serializer.cpp | 338 +++++++++++ src/old/serializer.h | 80 +++ src/old/serializerbinary.cpp | 63 ++ src/old/serializerbinary.h | 24 + src/old/serializerbzip2.cpp | 88 +++ src/old/serializerbzip2.h | 27 + src/old/serializerconnection.cpp | 15 + src/old/serializerconnection.h | 24 + src/old/serializertext.cpp | 170 ++++++ src/old/serializertext.h | 49 ++ src/old/sfile.cpp | 74 +++ src/old/sfile.h | 29 + src/old/sha1.cpp | 161 +++++ src/old/sha1.h | 42 ++ src/old/singleton.h | 59 ++ src/old/sptr.cpp | 1 + src/old/sptr.h | 99 ++++ src/old/stack.cpp | 33 ++ src/old/stack.h | 50 ++ src/old/staticstring.cpp | 282 +++++++++ src/old/staticstring.h | 63 ++ src/old/stream.cpp | 10 + src/old/stream.h | 27 + src/old/stringrep.cpp | 19 + src/old/stringrep.h | 17 + src/old/tests/clistress.cpp | 20 + src/old/tests/confpair.cpp | 19 + src/old/tests/connect.cpp | 38 ++ src/old/tests/exception.cpp | 16 + src/old/tests/formula.cpp | 13 + src/old/tests/fstring.cpp | 48 ++ src/old/tests/hash.cpp | 116 ++++ src/old/tests/hashtest.cpp | 107 ++++ src/old/tests/hashtest2.cpp | 15 + src/old/tests/httpsrv/httpconnectionmonitor.cpp | 88 +++ src/old/tests/httpsrv/httpconnectionmonitor.h | 16 + src/old/tests/httpsrv/main.cpp | 22 + src/old/tests/log.cpp | 29 + src/old/tests/md5test.cpp | 19 + src/old/tests/ordhash.cpp | 48 ++ src/old/tests/param.cpp | 46 ++ src/old/tests/param.h | 21 + src/old/tests/plugin/main.cpp | 14 + src/old/tests/plugin/plugin.cpp | 10 + src/old/tests/plugin/plugin.h | 14 + src/old/tests/qsort.cpp | 228 ++++++++ src/old/tests/sbuffer.cpp | 27 + src/old/tests/serialize.cpp | 30 + src/old/tests/serializetext.cpp | 28 + src/old/tests/sha1.cpp | 44 ++ src/old/tests/sptr.cpp | 55 ++ src/old/tests/srvstress.cpp | 91 +++ src/old/tests/strhash.cpp | 12 + src/old/tests/teltest/main.cpp | 21 + src/old/tests/teltest/telnetmonitor.cpp | 54 ++ src/old/tests/teltest/telnetmonitor.h | 26 + src/old/tests/xmlreadtest.cpp | 29 + src/old/tests/xmlrepltest.cpp | 31 + src/old/tests/xmlwritetest.cpp | 48 ++ src/old/tokenstring.cpp | 163 ++++++ src/old/tokenstring.h | 114 ++++ src/old/tqsort.h | 207 +++++++ src/old/unit/hashtable/hashtable.cpp | 107 ++++ src/old/unit/xml/xml.cpp | 59 ++ src/old/xmldocument.cpp | 149 +++++ src/old/xmldocument.h | 171 ++++++ src/old/xmlfilereader.cpp | 58 ++ src/old/xmlfilereader.h | 47 ++ src/old/xmlfilewriter.cpp | 28 + src/old/xmlfilewriter.h | 45 ++ src/old/xmlnode.cpp | 445 ++++++++++++++ src/old/xmlnode.h | 236 ++++++++ src/old/xmlreader.cpp | 602 +++++++++++++++++++ src/old/xmlreader.h | 141 +++++ src/old/xmlstringreader.cpp | 38 ++ src/old/xmlstringreader.h | 49 ++ src/old/xmlstringwriter.cpp | 23 + src/old/xmlstringwriter.h | 50 ++ src/old/xmlwriter.cpp | 173 ++++++ src/old/xmlwriter.h | 96 +++ 166 files changed, 17248 insertions(+) create mode 100644 src/old/arraylist.cpp create mode 100644 src/old/arraylist.h create mode 100644 src/old/cgi.cpp create mode 100644 src/old/cgi.h create mode 100644 src/old/configmanagerbase.cpp create mode 100644 src/old/configmanagerbase.h create mode 100644 src/old/confpair.cpp create mode 100644 src/old/confpair.h create mode 100644 src/old/confpairbase.cpp create mode 100644 src/old/confpairbase.h create mode 100644 src/old/conftree.cpp create mode 100644 src/old/conftree.h create mode 100644 src/old/connection.cpp create mode 100644 src/old/connection.h create mode 100644 src/old/connectionmanager.cpp create mode 100644 src/old/connectionmanager.h create mode 100644 src/old/connectionmonitor.cpp create mode 100644 src/old/connectionmonitor.h create mode 100644 src/old/exceptionbase.cpp create mode 100644 src/old/exceptionbase.h create mode 100644 src/old/exceptions.cpp create mode 100644 src/old/exceptions.h create mode 100644 src/old/flexbuf.cpp create mode 100644 src/old/flexbuf.h create mode 100644 src/old/formula.cpp create mode 100644 src/old/formula.h create mode 100644 src/old/fstring.cpp create mode 100644 src/old/fstring.h create mode 100644 src/old/hash.cpp create mode 100644 src/old/hash.h create mode 100644 src/old/hashable.cpp create mode 100644 src/old/hashable.h create mode 100644 src/old/hashfunction.cpp create mode 100644 src/old/hashfunction.h create mode 100644 src/old/hashfunctioncasestring.cpp create mode 100644 src/old/hashfunctioncasestring.h create mode 100644 src/old/hashfunctionint.cpp create mode 100644 src/old/hashfunctionint.h create mode 100644 src/old/hashfunctionstring.cpp create mode 100644 src/old/hashfunctionstring.h create mode 100644 src/old/hashtable.cpp create mode 100644 src/old/hashtable.h create mode 100644 src/old/http.cpp create mode 100644 src/old/http.h create mode 100644 src/old/httpget.cpp create mode 100644 src/old/httpget.h create mode 100644 src/old/linkedlist.cpp create mode 100644 src/old/linkedlist.h create mode 100644 src/old/linkmessage.cpp create mode 100644 src/old/linkmessage.h create mode 100644 src/old/linkmessenger.cpp create mode 100644 src/old/linkmessenger.h create mode 100644 src/old/list.cpp create mode 100644 src/old/list.h create mode 100644 src/old/md5.cpp create mode 100644 src/old/md5.h create mode 100644 src/old/multilog.cpp create mode 100644 src/old/multilog.h create mode 100644 src/old/multilogchannel.cpp create mode 100644 src/old/multilogchannel.h create mode 100644 src/old/multilogtext.cpp create mode 100644 src/old/multilogtext.h create mode 100644 src/old/ordhash.cpp create mode 100644 src/old/ordhash.h create mode 100644 src/old/paramproc.cpp create mode 100644 src/old/paramproc.h create mode 100644 src/old/plugger.cpp create mode 100644 src/old/plugger.h create mode 100644 src/old/pqueue.cpp create mode 100644 src/old/pqueue.h create mode 100644 src/old/programchain.cpp create mode 100644 src/old/programchain.h create mode 100644 src/old/programlink.cpp create mode 100644 src/old/programlink.h create mode 100644 src/old/protocol.cpp create mode 100644 src/old/protocol.h create mode 100644 src/old/protocoltelnet.cpp create mode 100644 src/old/protocoltelnet.h create mode 100644 src/old/queue.cpp create mode 100644 src/old/queue.h create mode 100644 src/old/ringlist.cpp create mode 100644 src/old/ringlist.h create mode 100644 src/old/sbuffer.cpp create mode 100644 src/old/sbuffer.h create mode 100644 src/old/serializable.cpp create mode 100644 src/old/serializable.h create mode 100644 src/old/serializer.cpp create mode 100644 src/old/serializer.h create mode 100644 src/old/serializerbinary.cpp create mode 100644 src/old/serializerbinary.h create mode 100644 src/old/serializerbzip2.cpp create mode 100644 src/old/serializerbzip2.h create mode 100644 src/old/serializerconnection.cpp create mode 100644 src/old/serializerconnection.h create mode 100644 src/old/serializertext.cpp create mode 100644 src/old/serializertext.h create mode 100644 src/old/sfile.cpp create mode 100644 src/old/sfile.h create mode 100644 src/old/sha1.cpp create mode 100644 src/old/sha1.h create mode 100644 src/old/singleton.h create mode 100644 src/old/sptr.cpp create mode 100644 src/old/sptr.h create mode 100644 src/old/stack.cpp create mode 100644 src/old/stack.h create mode 100644 src/old/staticstring.cpp create mode 100644 src/old/staticstring.h create mode 100644 src/old/stream.cpp create mode 100644 src/old/stream.h create mode 100644 src/old/stringrep.cpp create mode 100644 src/old/stringrep.h create mode 100644 src/old/tests/clistress.cpp create mode 100644 src/old/tests/confpair.cpp create mode 100644 src/old/tests/connect.cpp create mode 100644 src/old/tests/exception.cpp create mode 100644 src/old/tests/formula.cpp create mode 100644 src/old/tests/fstring.cpp create mode 100644 src/old/tests/hash.cpp create mode 100644 src/old/tests/hashtest.cpp create mode 100644 src/old/tests/hashtest2.cpp create mode 100644 src/old/tests/httpsrv/httpconnectionmonitor.cpp create mode 100644 src/old/tests/httpsrv/httpconnectionmonitor.h create mode 100644 src/old/tests/httpsrv/main.cpp create mode 100644 src/old/tests/log.cpp create mode 100644 src/old/tests/md5test.cpp create mode 100644 src/old/tests/ordhash.cpp create mode 100644 src/old/tests/param.cpp create mode 100644 src/old/tests/param.h create mode 100644 src/old/tests/plugin/main.cpp create mode 100644 src/old/tests/plugin/plugin.cpp create mode 100644 src/old/tests/plugin/plugin.h create mode 100644 src/old/tests/qsort.cpp create mode 100644 src/old/tests/sbuffer.cpp create mode 100644 src/old/tests/serialize.cpp create mode 100644 src/old/tests/serializetext.cpp create mode 100644 src/old/tests/sha1.cpp create mode 100644 src/old/tests/sptr.cpp create mode 100644 src/old/tests/srvstress.cpp create mode 100644 src/old/tests/strhash.cpp create mode 100644 src/old/tests/teltest/main.cpp create mode 100644 src/old/tests/teltest/telnetmonitor.cpp create mode 100644 src/old/tests/teltest/telnetmonitor.h create mode 100644 src/old/tests/xmlreadtest.cpp create mode 100644 src/old/tests/xmlrepltest.cpp create mode 100644 src/old/tests/xmlwritetest.cpp create mode 100644 src/old/tokenstring.cpp create mode 100644 src/old/tokenstring.h create mode 100644 src/old/tqsort.h create mode 100644 src/old/unit/hashtable/hashtable.cpp create mode 100644 src/old/unit/xml/xml.cpp create mode 100644 src/old/xmldocument.cpp create mode 100644 src/old/xmldocument.h create mode 100644 src/old/xmlfilereader.cpp create mode 100644 src/old/xmlfilereader.h create mode 100644 src/old/xmlfilewriter.cpp create mode 100644 src/old/xmlfilewriter.h create mode 100644 src/old/xmlnode.cpp create mode 100644 src/old/xmlnode.h create mode 100644 src/old/xmlreader.cpp create mode 100644 src/old/xmlreader.h create mode 100644 src/old/xmlstringreader.cpp create mode 100644 src/old/xmlstringreader.h create mode 100644 src/old/xmlstringwriter.cpp create mode 100644 src/old/xmlstringwriter.h create mode 100644 src/old/xmlwriter.cpp create mode 100644 src/old/xmlwriter.h (limited to 'src/old') diff --git a/src/old/arraylist.cpp b/src/old/arraylist.cpp new file mode 100644 index 0000000..ef21426 --- /dev/null +++ b/src/old/arraylist.cpp @@ -0,0 +1,100 @@ +#include "arraylist.h" +#include +#include + +ArrayList::ArrayList( int initSize, int growByFactor ) +{ + apData = new void *[initSize]; + nSize = 0; + nCapacity = initSize; + nGrowByFactor = growByFactor; +} + +ArrayList::~ArrayList( ) +{ + delete[] apData; +} + +void *ArrayList::getAt( int index ) +{ + if( index < 0 || index > nSize ) + return NULL; + + return apData[index]; +} + +void ArrayList::append( void *data ) +{ + insertBefore( data, nSize ); +} + +void ArrayList::insertBefore( void *data, int pos ) +{ + if( pos < 0 || pos > nSize ) + return; + + checkResize(); + memmove( &apData[pos+1], &apData[pos], (nSize-pos)*sizeof(void*) ); + apData[pos] = data; + nSize++; +} + +int ArrayList::getSize( ) +{ + return nSize; +} + +bool ArrayList::isEmpty( ) +{ + return nSize==0; +} + +void ArrayList::deleteAt( int index ) +{ + if( index < 0 || index >= nSize ) + return; + + memmove( &apData[index], &apData[index+1], (nSize-index-1)*sizeof(void *) ); + nSize--; +} + +void ArrayList::empty() +{ + // Probably the easiest as far as things go. + nSize = 0; +} + +void ArrayList::resizeTo( int newSize ) +{ + void **apNew = new void *[newSize]; + memmove( apNew, apData, nSize*sizeof(void *) ); + nCapacity = newSize; + delete[] apData; + apData = apNew; +} + +void ArrayList::checkResize() +{ + if( nSize >= nCapacity ) + { + resizeTo( nCapacity + nGrowByFactor ); + } +} + +void ArrayList::setSize( int newSize ) +{ + if( newSize < 0 ) + return; + + nSize = newSize; + checkResize(); +} + +void ArrayList::setAt( int index, void *data ) +{ + if( index < 0 || index >= nSize ) + return; + + apData[index] = data; +} + diff --git a/src/old/arraylist.h b/src/old/arraylist.h new file mode 100644 index 0000000..0fda34a --- /dev/null +++ b/src/old/arraylist.h @@ -0,0 +1,80 @@ +/** \file arraylist.h + * Describes the ArrayList class. + *@author Mike Buland + */ +#ifndef ARRAY_LIST_H +#define ARRAY_LIST_H + +#include "list.h" + +/** A simple list which uses an array. This is a great choice if you won't do + * a lot of adding and deleting and need a fast random access list. Otherwise + * use the LinkedList. + *@author Mike Buland + */ +class ArrayList : public List +{ +public: + /** Creates an arraylist with some pre-defined specs spelled out. + *@param initSize the inital number of elements to allocate. + *@param growByFactor How much to increase the size of the array by + * each time we run out of room. + */ + ArrayList( int initSize=100, int growByFactor=10 ); + /** + * Destroy the ArrayList + */ + virtual ~ArrayList(); + + void *getAt( int nIndex ); + void append( void *pData ); + void insertBefore( void *pData, int nPos = 0 ); + int getSize( ); + bool isEmpty( ); + void deleteAt( int nIndex ); + void empty(); + void setSize( int nNewSize ); + void setAt( int nIndex, void *pData ); + +private: + /** + * Checks to see if the system needs to be resized, if it does, this will + * automatically resize based on your parameters. + */ + void checkResize(); + + /** + * Resize the system to a specified size. If it is larger, then all data + * will be retained, if smaller the elements at the end will be cut off. + *@param newSize The number of elements to include after resizing. + */ + void resizeTo( int newSize ); + + /** + * Actual master array of pointers. This is done to follow the List specs. + * All data transactions are performed with pointers or compatable + * primitive data-types. + */ + void **apData; + + /** + * The number of filled in elements in the array. This is the practical + * real size of the ArrayList for all userspace applications. + */ + int nSize; + + /** + * The number of elements allocated in memory. Not all of these have to be + * filled in, and it is usually larger than nSize so that adding and + * deleting elements is fast and easy. + */ + int nCapacity; + + /** + * The amount to grow by whenever the array needs resizing. + */ + int nGrowByFactor; +}; + +#endif + diff --git a/src/old/cgi.cpp b/src/old/cgi.cpp new file mode 100644 index 0000000..1fecbbe --- /dev/null +++ b/src/old/cgi.cpp @@ -0,0 +1,644 @@ +#include +#include +#include +#include +#include + +#include "cgi.h" + +Cgi::Cgi( const char *strSource ) : + aContent( new HashFunctionString(), 151, true ) +{ + int length, j, k, mode = 0, slen = 0; + char hexbuf[3] = { 0, 0, 0 }; + char *buf, chr; + Item *cur = NULL; + int nCur = 0; + + if( strSource != NULL ) + { + loadContent( strSource ); + } + + if( ( getenv( "CONTENT_LENGTH" ) ) ) + { + if( !strcmp + ( getenv( "CONTENT_TYPE" ), + "application/x-www-form-urlencoded" ) ) + { + length = atoi( getenv( "CONTENT_LENGTH" ) ); + buf = new char[length + 1]; + fread( buf, 1, length, stdin ); + cur = new Item( ); + aVars.append( cur ); + cur->type = VAR_STDINPUT; + for( j = 0; j < length; j++ ) + { + switch ( buf[j] ) + { + case '=': + cur->name = new char[slen + 1]; + slen = 0; + break; + + case '&': + cur->value = new char[slen + 1]; + cur->len = slen; + slen = 0; + cur = new Item( ); + aVars.append( cur ); + cur->type = VAR_STDINPUT; + break; + + default: + switch ( buf[j] ) + { + case '%': /* per-cents mean a hex-code for an ASCII char */ + j += 2; + slen++; + break; + + default: /* Nothing special, move along, folks... */ + slen++; + break; + } + break; + } + } + cur->value = new char[slen + 1]; + cur->len = slen; + slen = 0; + mode = 0; + cur = ( Item * ) aVars.getAt( 0 ); + k = 0; + nCur = 0; + for( j = 0; j < length; j++ ) + { + switch ( buf[j] ) + { + case '=': + mode = 1; + k = 0; + break; + + case '&': + mode = 0; + k = 0; + nCur++; + cur = ( Item * ) aVars.getAt( nCur ); + break; + + default: + switch ( buf[j] ) + { + case '%': /* per-cents mean a hex-code for an ASCII char */ + hexbuf[0] = buf[++j]; + hexbuf[1] = buf[++j]; + chr = ( char ) ( strtol( hexbuf, NULL, 16 ) ); + break; + + case '+': /* Pluses mean spaces, odd, I know... */ + chr = ' '; + break; + + default: /* Nothing special, move along, folks... */ + chr = buf[j]; + break; + } + if( mode == 0 ) + { + cur->name[k] = chr; + cur->name[++k] = '\0'; + } + else + { + cur->value[k] = chr; + cur->value[++k] = '\0'; + } + break; + } + } + delete buf; + } + else if( !strncmp + ( getenv( "CONTENT_TYPE" ), "multipart/form-data;", 20 ) ) + { + char *boundary, *oname; + int blen, j, k, olen; + + length = atoi( getenv( "CONTENT_LENGTH" ) ); + buf = new char[length + 1]; + fread( buf, 1, length, stdin ); + for( blen = 0; buf[blen + 1] != '\n'; blen++ ); + boundary = new char[blen + 1]; + memcpy( boundary, buf, blen ); + boundary[blen] = '\0'; + j = blen + 2; + for( ;; ) + { + cur = new Item( ); + aVars.append( cur ); + cur->type = VAR_STDINPUT; + if( !strncmp + ( buf + j, "Content-Disposition: form-data; name=\"", + 38 ) ) + { + j += 38; + for( k = 0; buf[j + k] != '\"'; k++ ); + oname = cur->name = new char[k + 1]; + memcpy( cur->name, buf + j, k ); + olen = k; + cur->name[k] = '\0'; + j += k + 1; + if( !strncmp( buf + j, "; filename=\"", 12 ) ) /* Must be a file */ + { + /* Acquire file name */ + j += 12; + for( k = 0; buf[j + k] != '\"'; k++ ); + cur->value = new char[k + 1]; + memcpy( cur->value, buf + j, k ); + cur->value[k] = '\0'; + cur->len = k; + j += k + 3; + + /* Acquire content type */ + if( !strncmp( "Content-Type: ", buf + j, 14 ) ) + { + j += 14; + cur = new Item( ); + aVars.append( cur ); + cur->type = VAR_STDINPUT; + cur->name = new char[olen + 1]; + memcpy( cur->name, oname, olen + 1 ); + for( k = 0; buf[j + k + 1] != '\n'; k++ ); + cur->value = new char[k + 1]; + memcpy( cur->value, buf + j, k ); + cur->value[k] = '\0'; + cur->len = k; + j += k; + } + else + { + cur = new Item( ); + aVars.append( cur ); + cur->type = VAR_STDINPUT; + cur->name = new char[olen + 1]; + memcpy( cur->name, oname, olen + 1 ); + cur->value = new char[1]; + cur->value[0] = '\0'; + cur->len = 0; + } + j += 4; + + /* Acquire content */ + cur = new Item( ); + aVars.append( cur ); + cur->type = VAR_STDINPUT; + cur->name = new char[olen + 1]; + memcpy( cur->name, oname, olen + 1 ); + if( !strncmp( buf + j + k, boundary, blen ) ) + { + cur->value = new char[1]; + cur->value[0] = '\0'; + j += blen + 4; + } + else if( !strncmp( buf + j + k + 1, boundary, blen ) ) + { + cur->value = new char[1]; + cur->value[0] = '\0'; + j += blen + 5; + } + else + { + for( k = 0; + strncmp( buf + j + k + 2, boundary, blen ); + k++ ); + cur->value = new char[k + 1]; + memcpy( cur->value, buf + j, k ); + cur->value[k] = '\0'; + cur->len = k; + j += k + blen + 4; + } + } + else + { + j += 4; + for( k = 0; + strncmp( buf + j + k + 2, boundary, blen ); + k++ ); + cur->value = new char[k + 1]; + memcpy( cur->value, buf + j, k ); + cur->value[k] = '\0'; + cur->len = k; + j += k + blen + 4; + } + if( buf[j + 1] == '\n' ) + j += 2; + if( j >= length ) + break; + } + else + { + cur->name = ( char * ) "ERROR"; + cur->value = ( char * ) "Error here"; + } + } + } + delete buf; + } + + if( ( buf = getenv( "HTTP_COOKIE" ) ) ) + { + int lbase = aVars.getSize( ); + length = strlen( buf ); + cur = new Item( ); + aVars.append( cur ); + cur->type = VAR_COOKIE; + for( j = 0; j < length; j++ ) + { + switch ( buf[j] ) + { + case '=': + cur->name = new char[slen + 1]; + slen = 0; + break; + + case ';': + cur->value = new char[slen + 1]; + cur->len = slen; + slen = 0; + cur = new Item( ); + aVars.append( cur ); + cur->type = VAR_COOKIE; + break; + + default: + switch ( buf[j] ) + { + case '%': /* per-cents mean a hex-code for an ASCII char */ + j += 2; + slen++; + break; + + default: /* Nothing special, move along, folks... */ + slen++; + break; + } + break; + } + } + cur->value = new char[slen + 1]; + cur->len = slen; + slen = 0; + cur = ( Item * ) aVars.getAt( lbase ); + mode = 0; + k = 0; + nCur = lbase; + for( j = 0; j < length; j++ ) + { + switch ( buf[j] ) + { + case '=': + mode = 1; + k = 0; + break; + + case ';': + mode = 0; + k = 0; + nCur++; + cur = ( Item * ) aVars.getAt( nCur ); + break; + + default: + switch ( buf[j] ) + { + case '%': /* per-cents mean a hex-code for an ASCII char */ + hexbuf[0] = buf[++j]; + hexbuf[1] = buf[++j]; + chr = ( char ) ( strtol( hexbuf, NULL, 16 ) ); + break; + + case '+': /* Pluses mean spaces, odd, I know... */ + chr = ' '; + break; + + case ' ': + continue; + break; + + default: /* Nothing special, move along, folks... */ + chr = buf[j]; + break; + } + if( mode == 0 ) + { + cur->name[k] = chr; + cur->name[++k] = '\0'; + } + else + { + cur->value[k] = chr; + cur->value[++k] = '\0'; + } + break; + } + } + } + + if( ( buf = getenv( "QUERY_STRING" ) ) ) + { + if( strlen( buf ) > 0 ) + { + int lbase = aVars.getSize( ); + length = strlen( buf ); + cur = new Item( ); + aVars.append( cur ); + cur->type = VAR_CMDLINE; + for( j = 0; j < length; j++ ) + { + switch ( buf[j] ) + { + case '=': + cur->name = new char[slen + 1]; + slen = 0; + break; + + case '&': + cur->value = new char[slen + 1]; + cur->len = slen; + slen = 0; + cur = new Item( ); + aVars.append( cur ); + cur->type = VAR_CMDLINE; + break; + + default: + switch ( buf[j] ) + { + case '%': /* per-cents mean a hex-code for an ASCII char */ + j += 2; + slen++; + break; + + default: /* Nothing special, move along, folks... */ + slen++; + break; + } + break; + } + } + cur->value = new char[slen + 1]; + cur->len = slen; + slen = 0; + cur = ( Item * ) aVars.getAt( lbase ); + nCur = lbase; + mode = 0; + k = 0; + for( j = 0; j < length; j++ ) + { + switch ( buf[j] ) + { + case '=': + mode = 1; + k = 0; + break; + + case '&': + mode = 0; + k = 0; + nCur++; + cur = ( Item * ) aVars.getAt( nCur ); + break; + + default: + switch ( buf[j] ) + { + case '%': /* per-cents mean a hex-code for an ASCII char */ + hexbuf[0] = buf[++j]; + hexbuf[1] = buf[++j]; + chr = ( char ) ( strtol( hexbuf, NULL, 16 ) ); + break; + + case '+': /* Pluses mean spaces, odd, I know... */ + chr = ' '; + break; + + default: /* Nothing special, move along, folks... */ + chr = buf[j]; + break; + } + if( mode == 0 ) + { + cur->name[k] = chr; + cur->name[++k] = '\0'; + } + else + { + cur->value[k] = chr; + cur->value[++k] = '\0'; + } + break; + } + } + } + } +} + +Cgi::~Cgi( ) +{ +} + +char *Cgi::getVarValue( const char *name, int skip, unsigned char type ) +{ + for( int j = 0; j < aVars.getSize( ); j++ ) + { + Item *cur = ( Item * ) aVars.getAt( j ); + if( !strcmp( cur->name, name ) ) + { + if( ( cur->type & type ) ) + { + if( skip <= 0 ) + { + return cur->value; + } + else + { + skip--; + } + } + } + } + return NULL; +} + +int Cgi::getVarLength( const char *name, int skip, unsigned char type ) +{ + for( int j = 0; j < aVars.getSize( ); j++ ) + { + Item *cur = ( Item * ) aVars.getAt( j ); + if( !strcmp( cur->name, name ) ) + { + if( ( cur->type & type ) ) + { + if( skip <= 0 ) + { + return cur->len; + } + else + { + skip--; + } + } + } + } + return -1; +} + +void Cgi::writeDebugInfo() +{ + printf( "
\n" );
+    printf( "0x%02X - stdInput | 0x%02X - cookie | 0x%02X - cmdLine\n\n",
+             VAR_STDINPUT, VAR_COOKIE, VAR_CMDLINE );
+    for( int j = 0; j < aVars.getSize(  ); j++ )
+    {
+        Item *item = ( Item * ) aVars.getAt( j );
+        printf("[%s] = \"%s\" [0x%02X]\n", item->name,
+                 item->value, item->type );
+    }
+    printf( "
\n" ); +} + +void Cgi::writeContentHeader( int type ) +{ + switch( type ) + { + case headerHTML: + printf("Content-type: text/html\n\n"); + break; + } +} + +void Cgi::writeContent( const char *name, ...) +{ + char *templ = (char *)aContent.get(name); + + if( templ ) + { + va_list ap; + + va_start (ap, name); + vprintf (templ, ap); + va_end (ap); + } + else + { + printf("Error finding content labeled \"%s\"\n", name ); + } +} + +void Cgi::loadContent( const char *strSource ) +{ + FILE *fh = NULL; + if( strSource == NULL ) + { + extern char *program_invocation_short_name; + char *tmpName = new char[strlen(program_invocation_short_name)+10]; + memset( tmpName, 0, strlen(program_invocation_short_name)+10 ); + strcpy( tmpName, program_invocation_short_name ); + strcat( tmpName, ".content" ); + fh = fopen( tmpName, "rt" ); + delete tmpName; + } + else + { + fh = fopen( strSource, "rt" ); + } + + if( fh == NULL ) return; + + struct stat xStats; + + fstat( fileno( fh ), &xStats ); + + char *bigBuf = new char[xStats.st_size+1]; + memset( bigBuf, 0, xStats.st_size+1 ); + fread( bigBuf, 1, xStats.st_size, fh ); + fclose( fh ); + + // Now we can actually load stuff from the file, first we need to make us up a format... + int lSize=0; + struct Content + { + char *name; + char *value; + } xCont; + int j = 0; + while( j < xStats.st_size ) + { + // We're looking for a content-block init statement + for( ; j < xStats.st_size; j++ ) + { + if( bigBuf[j] == '#' ) + { + if( bigBuf[j+1] == '{' ) + { + break; + } + } + } + j=j+2; + if( j >= xStats.st_size ) break; + for( ; bigBuf[j] == ' ' || bigBuf[j] == '\t'; j++ ); + for( lSize = 0; lSize+j < xStats.st_size && bigBuf[lSize+j] != '\n' && bigBuf[lSize+j] != '\r'; lSize++ ); + xCont.name = new char[lSize+1]; + memset( xCont.name, 0, lSize+1 ); + memcpy( xCont.name, &bigBuf[j], lSize ); + j += lSize+1; + + for( lSize = 0; lSize+j < xStats.st_size; lSize++ ) + { + if( bigBuf[lSize+j] == '#' ) + { + if( bigBuf[lSize+j+1] == '}' ) + { + break; + } + } + } + xCont.value = new char[lSize+1]; + memset( xCont.value, 0, lSize+1 ); + memcpy( xCont.value, &bigBuf[j], lSize ); + + aContent.insert( xCont.name, xCont.value ); + + j += lSize + 2; + } +} + +void Cgi::writeCookie( char const *name, char const *value, char const *expires, char const *path, char const *domain, bool secure ) +{ + printf("Set-Cookie: %s=%s", name, value ); + + if( expires != NULL ) + { + printf("; expires=%s", expires ); + } + + if( path != NULL ) + { + printf("; path=%s", path ); + } + + if( domain != NULL ) + { + printf("; domain=%s", domain ); + } + + if( secure ) + { + printf("; secure"); + } + + printf("\n"); +} diff --git a/src/old/cgi.h b/src/old/cgi.h new file mode 100644 index 0000000..01142b5 --- /dev/null +++ b/src/old/cgi.h @@ -0,0 +1,196 @@ +/**\file cgi.h + * Describes extra params needed to use the Cgi class as well as the class + * itself. + *@author Mike Buland + */ + +#include "linkedlist.h" +#include "hashtable.h" +#include "hashfunctionstring.h" + +#define VAR_STDINPUT 0x01 /**< Variable came from stdinput, web form */ +#define VAR_COOKIE 0x02 /**< Variable came from a cookie */ +#define VAR_CMDLINE 0x04 /**< Variable came from commandline / uri */ +#define VAR_ANY 0xFF /**< Mask including all other types */ + +/** + * Cgi header processor originally designed for apache cgi programs. When used + * from apache with what I beleive are some sort of standard set of command + * line parameters and environment variables. This always worked for all of my + * purposes. This class will automatically extract all data from the system + * that you need and places it into tables and things for easy access. + * There are three types of input that data can come from, StandardInput, + * CommandLine, and Cookies. StandardInput is when you get formdata in + * multi-part forms, Cookies should usually be cookies that you set, and + * command line is everything after the question mark in the URL. + * This also contains some simple helpers for putting templated data into the + * HTTP data feed. + *@author Mike Buland + */ +class Cgi +{ +public: + /** + * Create a complete CGI object, this object will automatically read data + * from all available sources and be ready for use on the very next line! + * If strSource is filled in it will also automatically read in a content + * file, which is a simple file format containing named blocks of reusable + * templates. + *@param strSource Set to a filename in order to load up a content file. + */ + Cgi( const char *strSource = NULL ); + + /** + * Destroy the cgi object. + */ + virtual ~Cgi( ); + + /** + * Get's the value for a variable as a character string. The name is the + * name that was given on the URL or in the form or cookie. Skip can be + * set to any value above zero to retreive subsequent variables with the + * same name. The most obvious use of this is when dealing with file + * uploads, each file upload sends you three variables with the same name + * and different content. Finally the variable type determines where you + * will accept this variable from. This is generally a bit of a security + * thing, if you store login info in a cookie and don't want people getting + * in by faking the appropriate URL. + *@param name The name of the variable you wish to retreive. + *@param skip THe number of variables with the given name to skip before + * returning something meaningful. The only way to determine how many + * variables with the same name there are is to skip until you get a NULL + * value returned. + *@param type Can be set to any combination of VAR_STDINPUT, VAR_COOKIE, + * VAR_CMDLINE, or just VAR_ANY. This takes bitflags, so you can or the + * values together. If a variable is found but came from the wrong source + * it won't match any other criteria and will be treated as though it + * doesn't exist. + *@returns A null-terminated string representing the value of the requested + * variable, or NULL if the variable did not exist. If a variable does + * exist but has no value the string returned will start with a NULL char, + * but be a valid string. + */ + char *getVarValue( const char *name, int skip=0, unsigned char type=VAR_ANY ); + + /** + * This functions identically in every way to getVarValue, except that + * instead of returning a pointer to the variable's value, it returns the + * length of the variable's value string. The params are the same and so + * a call to both functions with the same params should yeild a value and + * a corresponding length. + *@param name The name of the variable you wish to retreive. + *@param skip THe number of variables with the given name to skip before + * returning something meaningful. The only way to determine how many + * variables with the same name there are is to skip until you get a NULL + * value returned. + *@param type Can be set to any combination of VAR_STDINPUT, VAR_COOKIE, + * VAR_CMDLINE, or just VAR_ANY. This takes bitflags, so you can or the + * values together. If a variable is found but came from the wrong source + * it won't match any other criteria and will be treated as though it + * doesn't exist. + *@returns The length of the value-string of the requested variable. If + * the requested variable is not found, -1 is returned. + */ + int getVarLength( const char *name, int skip=0, unsigned char type=VAR_ANY ); + + /** + * A handy little function that writes a load of debug info related to + * parsing CGI params to the standard output in html. This is generally + * best used at the end of a page. + */ + void writeDebugInfo(); + + /** + * Write a content header to the standard output. This should also be the + * first thing that you do (except for writing cookies) after initializing + * the Cgi class. You can select a type of header or content from the + * header enum, and a properly formatted header will show up on the + * standard output. + *@param type Any value from the header enum in this class. The default is + * to write an html header, probably the most common as well. + */ + void writeContentHeader( int type=headerHTML ); + + /** + * Write content to the stnadard output. The content variable should have + * been loaded during construction of the Cgi object or with the + * loadContent function. The content variable should be formatted just like + * a printf string, so that anything you want to put into it will have a % + * symbol replacement code, like %s, %d, etc. Since this actually uses a + * type of printf function everything from those docs work here. + *@param name The name of the content variable to format and write to + * stnadard output. + *@param ... As many params as you want to include, ala printf. + */ + void writeContent( const char *name, ...); + + /** + * Load a content file. I don't want to describe the format here, you can + * just read the code or find an example for now. Sorry. + *@param strSource The name of the file to open and read in to get the + * content loaded. + */ + void loadContent( const char *strSource = NULL ); + + /** + * Write a cookie-set header to the output stream. This should be done + * before any other content-headers are written. The specifics of this + * function are very simple, since I rely on the user's understanding of + * how standard HTTP/1.1 or HTTP/1.0 cookie syntax works. If you don't + * care then just use the name and value and the defaults should keep you + * in good stead for a long time. + *@param name The name of the cookie variable to set. + *@param value The value to set to that variable. + *@param expires The formatted string value for the date and time this + * cookie should expire. A NULL here will put a "until the browser closes" + * tag in. + *@param path The path (URL) that this cookie belongs to. If you run a lot + * of hosted servers or sub-sites that may have some shared URL bits then + * you may want to set this. The cookie should only be sent to URL's that + * match this as their first part. + *@param domain The domain that is allowed to read this, if not set, it's + * the domain the web browser contacted when they got the cookie. + *@param secure I'm not sure, I think it's something to tell if the cookie + * is safe to keep because any potentially valuable data is encypted or + * otherwise unusable. I could be wrong. + */ + void writeCookie( char const *name, char const *value, char const *expires=NULL, char const *path=NULL, char const *domain=NULL, bool secure=false ); + + /** + * A simple helper class to contain variable data. + */ + class Item + { + public: + /** + * Build an empty Item. + */ + Item( ) + { + name = NULL; + value = NULL; + len = 0; + type = 0; + } + /** The name of the item. */ + char *name; + /** The value of the item. */ + char *value; + /** The length of the item's value. */ + unsigned long len; + /** The type of the item (where it came from). */ + unsigned char type; + }; + + /** Header values */ + enum + { + headerHTML + }; + +private: + /** Keeps track of all contained variables. */ + LinkedList aVars; + /** Keeps track of all content variables. */ + HashTable aContent; +}; diff --git a/src/old/configmanagerbase.cpp b/src/old/configmanagerbase.cpp new file mode 100644 index 0000000..ac55fe0 --- /dev/null +++ b/src/old/configmanagerbase.cpp @@ -0,0 +1,63 @@ +#include +#include +#include "xmlnode.h" +#include "xmlfilereader.h" +#include "configmanagerbase.h" + +ConfigManagerBase::ConfigManagerBase() +{ +} + +ConfigManagerBase::~ConfigManagerBase() +{ +} + +void ConfigManagerBase::addSearchPath( const std::string &sPath ) +{ + lSearchPath.push_back( sPath ); +} + +void ConfigManagerBase::loadConfig( const std::string &sFileName, const char *lpProfile ) +{ + // Try a few locations... + std::list::const_iterator i; + for( i = lSearchPath.begin(); i != lSearchPath.end(); i++ ) + { + if( parseConfig( (*i + sFileName).c_str(), lpProfile ) ) + { + break; + } + } +} + +bool ConfigManagerBase::parseConfig( const char *lpFileName, const char *lpProfile ) +{ + XmlNode *pRoot, *pCur; + XmlFileReader doc( lpFileName ); + + pRoot = doc.getRoot(); + if( pRoot == NULL ) + { + return false; + } + + if( strcmp("config", pRoot->getName() ) ) + { + return false; + } + + for( int j = 0;; j++ ) + { + pCur = pRoot->getChild( "profile", j ); + if( pCur == NULL ) + return false; + + if( !strcmp( pCur->getProperty("id"), lpProfile ) ) + { + return processProfile( pCur ); + } + } + + return false; +} + diff --git a/src/old/configmanagerbase.h b/src/old/configmanagerbase.h new file mode 100644 index 0000000..381cc1f --- /dev/null +++ b/src/old/configmanagerbase.h @@ -0,0 +1,24 @@ +#ifndef CONFIG_MANAGER_BASE_H +#define CONFIG_MANAGER_BASE_H + +#include +#include + +class ConfigManagerBase +{ +public: + ConfigManagerBase(); + virtual ~ConfigManagerBase(); + +public: + void addSearchPath( const std::string &sPath ); + void loadConfig( const std::string &sFileName, const char *lpProfile="default" ); + +private: + bool parseConfig( const char *lpFileName, const char *lpProfile ); + virtual bool processProfile( class XmlNode *pBase )=0; + + std::list lSearchPath; +}; + +#endif diff --git a/src/old/confpair.cpp b/src/old/confpair.cpp new file mode 100644 index 0000000..4741401 --- /dev/null +++ b/src/old/confpair.cpp @@ -0,0 +1,2 @@ +#include "confpair.h" + diff --git a/src/old/confpair.h b/src/old/confpair.h new file mode 100644 index 0000000..56eb06e --- /dev/null +++ b/src/old/confpair.h @@ -0,0 +1,81 @@ +#ifndef CONF_PAIR_H +#define CONF_PAIR_H + +#include +#include +#include +#include "confpairbase.h" + +/** + * + */ +template +class ConfPair : public ConfPairBase +{ +public: + ConfPair( const std::string &sName ) : + sName( sName ) + { } + + virtual ~ConfPair() + { } + + T &value() + { + return tValue; + } + + const std::string &name() + { + return sName; + } + + virtual void setFromString( const std::string &sStr ) + { + std::stringstream(sStr) >> tValue; + } + + virtual std::string getAsString() + { + std::stringstream tmp; + tmp << tValue; + return tmp.str(); + } + +private: + std::string sName; + T tValue; +}; + +template<> +void ConfPair::setFromString( const std::string &sStr ) +{ + tValue = sStr; +} + +template<> +std::string ConfPair::getAsString() +{ + return tValue; +} + +template<> +void ConfPair::setFromString( const std::string &sStr ) +{ + if( !strcasecmp( sStr.c_str(), "true" ) || + !strcasecmp( sStr.c_str(), "yes" ) || + !strcasecmp( sStr.c_str(), "on" ) ) + tValue = true; + else + tValue = false; +} + +template<> +std::string ConfPair::getAsString() +{ + if( tValue == true ) + return "True"; + return "False"; +} + +#endif diff --git a/src/old/confpairbase.cpp b/src/old/confpairbase.cpp new file mode 100644 index 0000000..1203dc0 --- /dev/null +++ b/src/old/confpairbase.cpp @@ -0,0 +1,17 @@ +#include "confpairbase.h" + +ConfPairBase::ConfPairBase() +{ +} + +ConfPairBase::~ConfPairBase() +{ +} + +ConfPairBase &ConfPairBase::operator=( const std::string &s ) +{ + setFromString( s ); + + return *this; +} + diff --git a/src/old/confpairbase.h b/src/old/confpairbase.h new file mode 100644 index 0000000..2530756 --- /dev/null +++ b/src/old/confpairbase.h @@ -0,0 +1,24 @@ +#ifndef CONF_PAIR_BASE_H +#define CONF_PAIR_BASE_H + +#include +#include +#include +#include + +class ConfPairBase +{ +public: + ConfPairBase(); + virtual ~ConfPairBase(); + + virtual void setFromString( const std::string &sStr )=0; + virtual std::string getAsString()=0; + + ConfPairBase &operator=( const std::string &s ); + +private: + +}; + +#endif diff --git a/src/old/conftree.cpp b/src/old/conftree.cpp new file mode 100644 index 0000000..d9a3a3f --- /dev/null +++ b/src/old/conftree.cpp @@ -0,0 +1,9 @@ +#include "conftree.h" + +ConfTree::ConfTree() +{ +} + +ConfTree::~ConfTree() +{ +} diff --git a/src/old/conftree.h b/src/old/conftree.h new file mode 100644 index 0000000..197b1ef --- /dev/null +++ b/src/old/conftree.h @@ -0,0 +1,19 @@ +#ifndef CONF_TREE_H +#define CONF_TREE_H + +#include + +/** + * + */ +class ConfTree +{ +public: + ConfTree(); + virtual ~ConfTree(); + +private: + +}; + +#endif diff --git a/src/old/connection.cpp b/src/old/connection.cpp new file mode 100644 index 0000000..efef144 --- /dev/null +++ b/src/old/connection.cpp @@ -0,0 +1,564 @@ +#include "connection.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "exceptions.h" + +// Read buffer size...maybe fix wierd issues... +#define RBS (1024*10) + +Connection::Connection() +{ + nSocket = -1; + bActive = false; + bDisconnectMe = false; + pProtocol = NULL; +} + +Connection::~Connection() +{ + if( pProtocol != NULL ) delete pProtocol; +} + +void Connection::ensureCapacity( int nSize ) +{ + xOutputBuf.ensureCapacity( nSize ); +} + +bool Connection::appendOutput( const char *lpOutput, int nSize ) +{ + return xOutputBuf.appendData( lpOutput, nSize ); +} + +bool Connection::appendOutput( const char lOutput ) +{ + return xOutputBuf.appendData( lOutput ); +} + +bool Connection::appendOutput( const short lOutput ) +{ + return xOutputBuf.appendData( lOutput ); +} + +bool Connection::appendOutput( const int lOutput ) +{ + return xOutputBuf.appendData( lOutput ); +} + +bool Connection::appendOutput( const long lOutput ) +{ + return xOutputBuf.appendData( lOutput ); +} + +bool Connection::appendOutput( const float lOutput ) +{ + return xOutputBuf.appendData( lOutput ); +} + +bool Connection::appendOutput( const double lOutput ) +{ + return xOutputBuf.appendData( lOutput ); +} + +bool Connection::appendOutput( const unsigned char lOutput ) +{ + return xOutputBuf.appendData( lOutput ); +} + +bool Connection::appendOutput( const unsigned short lOutput ) +{ + return xOutputBuf.appendData( lOutput ); +} + +bool Connection::appendOutput( const unsigned long lOutput ) +{ + return xOutputBuf.appendData( lOutput ); +} + +bool Connection::appendOutput( const unsigned int lOutput ) +{ + return xOutputBuf.appendData( lOutput ); +} + +bool Connection::appendInput( const char *lpInput, int nSize ) +{ + return xInputBuf.appendData( lpInput, nSize ); +} + +int Connection::scanInputFor( char cTarget ) +{ + const char *lpTmp = xInputBuf.getData(); + int jMax = xInputBuf.getLength(); + + for( int j = 0; j < jMax; j++ ) + { + if( lpTmp[j] == cTarget ) + { + return j; + } + } + + return -1; +} + +const char *Connection::getOutput() +{ + return xOutputBuf.getData(); +} + +const char *Connection::getInput() +{ + return xInputBuf.getData(); +} + +void Connection::setSocket( int nNewSocket ) +{ + nSocket = nNewSocket; +} + +int Connection::getSocket() +{ + return nSocket; +} + +bool Connection::isActive() +{ + return bActive; +} + +void Connection::close() +{ + //printf("Close called, socket is: %s\n", bActive?"Active":"Inactive" ); + if( bActive ) + { + fsync( nSocket ); + ::close( nSocket ); + //printf("Socket closed.\n"); + } + bActive = false; + //nSocket = -1; + xInputBuf.clearData(); + xOutputBuf.clearData(); + if( pProtocol != NULL ) + { + delete pProtocol; + pProtocol = NULL; + } +} + +bool Connection::open( int nNewSocket ) +{ + bActive = true; + setSocket( nNewSocket ); + bDisconnectMe = false; + + return true; +} + +bool Connection::open( const char *sAddr, int nPort, int nSec ) +{ + struct sockaddr_in xServerName; + bActive = false; + + /* Create the socket. */ + nSocket = socket( PF_INET, SOCK_STREAM, 0 ); + + if( nSocket < 0 ) + { + bActive = false; + return false; + } + + // These lines set the socket to non-blocking, a good thing? + int flags; + flags = fcntl(nSocket, F_GETFL, 0); + flags |= O_NONBLOCK; + if (fcntl(nSocket, F_SETFL, flags) < 0) + { + return false; + } + + /* Connect to the server. */ + //printf("Resolving hostname (%s)...\n", sAddr ); + { + struct hostent *hostinfo; + + xServerName.sin_family = AF_INET; + xServerName.sin_port = htons( nPort ); + hostinfo = gethostbyname( sAddr ); + if (hostinfo == NULL) + { + return false; + } + xServerName.sin_addr = *(struct in_addr *) hostinfo->h_addr; + } + + //printf("Making actual connection..."); + //fflush( stdout ); + connect( + nSocket, + (struct sockaddr *)&xServerName, + sizeof(xServerName) + ); + //printf("Connected.\n"); + + bActive = true; + bDisconnectMe = false; + + if( nSec > 0 ) + { + fd_set rfds, wfds, efds; + int retval; + + FD_ZERO(&rfds); + FD_SET(nSocket, &rfds); + FD_ZERO(&wfds); + FD_SET(nSocket, &wfds); + FD_ZERO(&efds); + FD_SET(nSocket, &efds); + + struct timeval tv; + tv.tv_sec = nSec; + tv.tv_usec = 0; + + retval = select( nSocket+1, &rfds, &wfds, &efds, &tv ); + + if( retval == 0 ) + { + close(); + throw ExceptionBase("Connection timeout.\n"); + } + + } + + /* + if( ret < 0 ) + { + return false; + }*/ + + return true; +} + +int Connection::readInput() +{ + char buffer[RBS]; + int nbytes; + int nTotalRead=0; + + for(;;) + { + //memset( buffer, 0, RBS ); + + nbytes = read( nSocket, buffer, RBS ); + if( nbytes < 0 && errno != 0 && errno != EAGAIN ) + { + //printf("errno: %d, %s\n", errno, strerror( errno ) ); + /* Read error. */ + //perror("readInput"); + throw ConnectionException( excodeReadError, "Read error: %s", strerror( errno ) ); + } + else + { + if( nbytes <= 0 ) + break; + nTotalRead += nbytes; + appendInput( buffer, nbytes ); + /* Data read. */ + if( nbytes < RBS ) + { + break; + } + + /* New test, if data is divisible by RBS bytes on some libs the + * read could block, this keeps it from happening. + */ + { + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(nSocket, &rfds); + struct timeval tv = { 0, 0 }; + int retval = select( nSocket+1, &rfds, NULL, NULL, &tv ); + if( retval == -1 ) + throw ConnectionException( + excodeBadReadError, + "Bad Read error" + ); + if( !FD_ISSET( nSocket, &rfds ) ) + break; + } + + } + } + + if( pProtocol != NULL && nTotalRead > 0 ) + { + pProtocol->onNewData(); + } + + return nTotalRead; +} + +bool Connection::readInput( int nSec, int nUSec, int *pnSecBack, int *pnUSecBack ) +{ + fd_set rfds, efds; + struct timeval tv, start, end; + struct timezone tz; + int retval; + + gettimeofday( &start, &tz ); + + FD_ZERO(&rfds); + FD_SET(nSocket, &rfds); + FD_ZERO(&efds); + FD_SET(nSocket, &efds); + + tv.tv_sec = nSec; + tv.tv_usec = nUSec; + + //printf("Starting at %d %d\n", nSec, nUSec ); + retval = select( nSocket+1, &rfds, NULL, NULL, &tv ); + + if( retval == -1 ) + { + // Oh my god!!! some kind of horrible problem!!!! + throw ConnectionException( excodeBadReadError, "Bad Read error"); + return false; + } + else if( retval ) + { + //printf("retval=%d, nSocket=%d,%d, sec=%d, usec=%d\n", retval, nSocket, FD_ISSET( nSocket, &rfds ), tv.tv_sec, tv.tv_usec ); + // None of them have data, but the connection is still active. + if( FD_ISSET( nSocket, &rfds ) ) + { + if( readInput() == 0 ) + { + throw ConnectionException( excodeConnectionClosed, "Connection closed"); } + } + } + + gettimeofday( &end, &tz ); + + int st, ust; + st = nSec - ( end.tv_sec - start.tv_sec ); + if( ( end.tv_usec - start.tv_usec ) > nUSec ) + { + (st)--; + ust = 1000000 - (end.tv_usec - start.tv_usec); + } + else + { + ust = nUSec - (end.tv_usec - start.tv_usec); + } + + if( st < 0 ) + { + st = ust = 0; + } + + if( pnSecBack ) + { + *pnSecBack = st; + *pnUSecBack = ust; + } + + //printf("New time: %d %d\n", *pnSecBack, *pnUSecBack ); + + return true; +} + +void Connection::waitForInput( int nBytesIn, int nSec, int nUSec ) +{ + int rlen = getInputAmnt(); + + if( rlen >= nBytesIn ) + return; + + while( rlen < nBytesIn ) + { + if( nSec == 0 && nUSec == 0 ) + { + throw ConnectionException( excodeSocketTimeout, "Timed out while waiting for %d bytes.", nBytesIn ); + } + readInput( nSec, nUSec, &nSec, &nUSec ); + rlen = getInputAmnt(); + } +} + +bool Connection::clearOutput() +{ + return xOutputBuf.clearData(); +} + +bool Connection::clearInput() +{ + return xInputBuf.clearData(); +} + +#define min( a, b ) ((asetConnection( this ); +} + +int Connection::getInputAmnt() +{ + return xInputBuf.getLength(); +} + +int Connection::getOutputAmnt() +{ + return xOutputBuf.getLength(); +} + +class Protocol *Connection::getProtocol() +{ + return pProtocol; +} + +void Connection::printInputDebug( const char *lpPrefix, FILE *fh, int nBytesMax ) +{ + printDataDebug( + (const unsigned char *)xInputBuf.getData(), + xInputBuf.getLength(), + "input", + lpPrefix, + fh, + nBytesMax + ); +} + +void Connection::printOutputDebug( const char *lpPrefix, FILE *fh, int nBytesMax ) +{ + printDataDebug( + (const unsigned char *)xOutputBuf.getData(), + xOutputBuf.getLength(), + "output", + lpPrefix, + fh, + nBytesMax + ); +} + +void Connection::printDataDebug( const unsigned char *pData, long nDataLen, const char *lpName, const char *lpPrefix, FILE *fh, int nBytesMax ) +{ + if( nBytesMax > 0 ) + { + nDataLen = (nBytesMax32 && pData[j+k]<=128)?(pData[j+k]):('.') ); + } + fprintf( fh, "\n"); + j += kmax; + if( j >= nDataLen ) break; + } + fprintf( fh, lpPrefix ); + for( int l = 0; l < 8*3+2*8+2; l++ ) fprintf( fh, (l!=8*3)?("-"):("+") ); fprintf( fh, "\n"); +} + diff --git a/src/old/connection.h b/src/old/connection.h new file mode 100644 index 0000000..0e991c7 --- /dev/null +++ b/src/old/connection.h @@ -0,0 +1,411 @@ +/**\file + * Contains the Connection class. + *@author Mike Buland + */ + +#ifndef CONNECTION_H +#define CONNECTION_H + +#include "multilog.h" +#include "flexbuf.h" +#include "protocol.h" + +/** Represents a single connection on a network. While these connections + * may be treated more or less just like files, occasionally problems arise + * when writing data at any time you feel like. Therefore you run all your + * data through a Connection, which buffers all data and makes sure no + * buffers are exceeded and nothing inappropriate for the recipient of the + * data is sent. + *@author Mike Buland + */ +class Connection +{ +public: + /** + * Construct a blank and non-connected Connection. The created object is + * not yet connected to anything, and most of the functions except open are + * unusable. + */ + Connection(); + + /** + * Destroy the connection, clean up all pending data requests and close the + * contained socket. This does not send out pending data, especially since + * such an operation could take considerable time, depending on the pending + * data and state of the receiving end. + */ + virtual ~Connection(); + + /** + * Open a connection to a remote server. This sets up this connection as + * a client instead of a server and does all of the work that needs to be + * done to actually open an INET_AF connection, which is a lot of work. + *@param sAddr The address to connect to. This can be in any format + * normally understood by your system to be an address, ip, domain name, + * etc. + *@param nPort The port number to connect to on the remote server. + *@returns True if the connection was successful and everything is setup, + * false if there were any of a dozen errors and the connection is not set. + *@todo Make this function add log entries to a standard MultiLog if + * something goes wrong. + */ + bool open( const char *sAddr, int nPort, int nSec=30 ); + + void ensureCapacity( int nSize ); + + /** Append the given data to the output. The data is presumed to be null + * terminated. To put binary data into the stream, use the other + * appendOutput function. This should be the only method used to + * communicate with the socket. + *@param lpOutput The data to add to the output queue. + *@param nSize How much data is in the lpOutput buffer. If this value + * is -1 then the program treats lpOutput as a null-terminated string. + *@returns True if everything is ok, false otherwise. + */ + bool appendOutput( const char *lpOutput, int nSize=-1 ); + + /** + * Append the character to the output. + *@param lOutput The character to add to the output queue. + *@returns True if everything is ok, false otherwise. + */ + bool appendOutput( const char lOutput ); + + /** + * Append the short to the output. + *@param lOutput The short to add to the output queue. + *@returns True if everything is ok, false otherwise. + */ + bool appendOutput( const short lOutput ); + + /** + * Append the int to the output. + *@param lOutput The int to add to the output queue. + *@returns True if everything is ok, false otherwise. + */ + bool appendOutput( const int lOutput ); + + /** + * Append the long to the output. + *@param lOutput The long to add to the output queue. + *@returns True if everything is ok, false otherwise. + */ + bool appendOutput( const long lOutput ); + + /** + * Append the float to the output. + *@param lOutput The float to add to the output queue. + *@returns True if everything is ok, false otherwise. + */ + bool appendOutput( const float lOutput ); + + /** + * Append the double to the output. + *@param lOutput The double to add to the output queue. + *@returns True if everything is ok, false otherwise. + */ + bool appendOutput( const double lOutput ); + + /** + * Append the unsigned char to the output. + *@param lOutput The unsigned char to add to the output queue. + *@returns True if everything is ok, false otherwise. + */ + bool appendOutput( const unsigned char lOutput ); + + /** + * Append the unsigned short to the output. + *@param lOutput The unsigned short to add to the output queue. + *@returns True if everything is ok, false otherwise. + */ + bool appendOutput( const unsigned short lOutput ); + + /** + * Append the unsigned int to the output. + *@param lOutput The unsigned int to add to the output queue. + *@returns True if everything is ok, false otherwise. + */ + bool appendOutput( const unsigned int lOutput ); + + /** + * Append the unsigned long to the output. + *@param lOutput The unsigned long to add to the output queue. + *@returns True if everything is ok, false otherwise. + */ + bool appendOutput( const unsigned long lOutput ); + + /** + * Writes all input data in the buffer in a dual-view ascii and hex display + * to a file. There are a number of options that also help with debugging. + *@param lpPrefix Text to be added to the begining of every line written + * out. The default is a blank string. + *@param fh The file to write the data to in text mode. This is stdout by + * default, but could be any already open file handle. + *@param nBytesMax The maximum number of bytes to write to the output. The + * amount of data can be overwhelming sometimes, so you can limit it. The + * default value is -1, which is also unlimited. + */ + void printInputDebug( const char *lpPrefix="", FILE *fh=stdout, int nBytesMax=-1 ); + + /** + * Writes all output data in the buffer in a dual-view ascii and hex display + * to a file. There are a number of options that also help with debugging. + *@param lpPrefix Text to be added to the begining of every line written + * out. The default is a blank string. + *@param fh The file to write the data to in text mode. This is stdout by + * default, but could be any already open file handle. + *@param nBytesMax The maximum number of bytes to write to the output. The + * amount of data can be overwhelming sometimes, so you can limit it. The + * default value is -1, which is also unlimited. + */ + void printOutputDebug( const char *lpPrefix="", FILE *fh=stdout, int nBytesMax=-1 ); + + /** + * This is the low-level generic function that is called by both + * printInputDebug and printOutputDebug. It works effectively just like + * both of them, except that you can give it a raw pointer to the data to + * print out. This probably doesn't belong in this class, but this was + * where I was when I needed it. + *@param pData A pointer to the data to write. This is not treated as a + * null terminated string, so make sure that the nDataLen param is set + * properly. + *@param nDataLen The number of bytes that are in pData and that you want to + * see. + *@param lpName The name of the data, this is used in the header where it + * says "Displaying nnn bytes of ." A good example would be input + * or output. + *@param lpPrefix Text to put before every line output. This just makes it + * easier to tell large blocks apart in the output. + *@param fh The file handle to write all data to. + *@param nBytesMax The maximum number of bytes. This parameter is stupid. + * If it is set to -1, then nDataLen is used, otherwise the smaller value is + * used as the number of bytes to output. + *@todo Put this function somewhere more deserving. + *@todo Remove the nBytesMax param, we need that in the other functions, + * not this one! + */ + void printDataDebug( const unsigned char *pData, long nDataLen, const char *lpName, const char *lpPrefix, FILE *fh, int nBytesMax ); + + /** Append the given data to the input. The data is presumed to be null + * terminated. To put binary data into the stream, use the other + * appendInput function. This is mainly used by internal routines. + *@param lpInput The data to add to the input queue. + *@param nSize How much data is in the lpInput buffer. If this value + * is -1 then the program treats lpOutput as a null-terminated string. + *@returns True if everything is ok, false otherwise. + */ + bool appendInput( const char *lpInput, int nSize=-1 ); + + /** Searches through the current pending input for a certain character. + * This is useful for finding out where exactly the end of a line is, for + * example, to see if a command has been entered yet. + *@param cTarget The character to search for. + *@returns The position of the target relative to the begining of the input + * or -1 if the target wasn't found. + */ + int scanInputFor( char cTarget ); + + /** Gets a pointer to the output buffer. This is mainly used by internal + * routines, and is cleared every click when data is sent out again. + *@returns A pointer to the buffer holding the pending output data. + */ + const char *getOutput(); + + /** Gets a pointer to the start of the input buffer's active data + * section. Use this to gain access to the input you need to do + * your job. + *@returns A pointer to the data in the input buffer. Do not delete this. + */ + const char *getInput(); + + /** Clears all pending output, this is mainly just used internally. + *@returns True if operation was a success, otherwise false. + */ + bool clearOutput(); + + /** Clears all pending input, weather it's been used or not. Please + * refrain from calling this during normal operation, use usedInput + * instead, it's much safer. + *@returns True if the operation was a success, false otherwise. + */ + bool clearInput(); + + /** Sets the socket that should be used internally. + *@param nNewSocket The new socket to work with. + */ + void setSocket( int nNewSocket ); + + /** Gets the handle (number) of the working socket. This can be a + * dangerous function to call, please refrain from calling it directly + * if any alternative can be found. + *@returns The number of the working socket. + */ + int getSocket(); + + /** Determines if the connection is still active. + *@returns True if the connection is active, false otherwise. + */ + bool isActive(); + + /** Clears all buffers and sets up the connection to be reused. + * Does not actually close the socket, that's handled by the + * ConnectionManager + */ + void close(); + + /** Opens a socket. Really just sets up the connection for use since + * the socket itself was created and opened by the ConnectionManager. + * This also calls setSocket so you don't have to. + *@param nNewSocket The socket to assosiate with. + */ + bool open( int nNewSocket ); + + /** + * Reads all pending input from the connection. If this is called outside + * of the ConnectionManager it will usually block indefinately waiting for + * new data. The only way to change this behaviour is to modify the socket + * low-level when you connect it manually, or, preferably use the other + * readInput function to control blocking time. + *@returns True socket is still connected, otherwise false. + */ + int readInput(); + + /** + * Reads all pending input from the connection, blocking up to nSec + * seconds and nUSec micro-seconds for the data. This uses select to + * simulate blocking, but has the same effect as standard io blocking. + * If you don't want to block, just set both values to zero. The back + * parameters are optional, set to null to not use them. The variables + * you pass in through the back parameters will contain the remaining + * time if data arrived before the max timeout was reached. + *@param nSec Max seconds to wait. + *@param nUSec Max micro-seconds to wait. + *@param pnSecBack The number of seconds remaining. + *@param pnUSecBack The number of micro-seconds remaining. + */ + bool readInput( int nSec, int nUSec, int *pnSecBack=NULL, int *pnUSecBack=NULL ); + + /** + * Waits until at least nBytesIn are read into the input buffer and ready + * to be used. Wait at most nSec seconds plus nUSec micro seconds. + * If the timeout is exceeded, this function throws an exception. If this + * function returns normally, you are guranteed to have at least nBytesIn + * bytes in your input buffer. + *@param nBytesIn Number of bytes to read. + *@param nSec The max seconds to wait. + *@param sUSec The max microseconds to wait. + */ + void waitForInput( int nBytesIn, int nSec, int nUSec ); + + /** Writes some data that is pending to the socket. + *@returns True if all data was written succesfully, false otherwise. + */ + bool writeOutput(); + + /** + * Writes all data that is pending on the socekt. + */ + bool writeAllOutput(); + + /** Determines if the connection has output waiting to go out. + *@returns true if there is pending output, otherwise false. + */ + bool hasOutput(); + + /** Sets internal flags so that this connection will be deleted next + * time through the ConnectionManager. + */ + void disconnect(); + + /** Determines if this connection is ready to be disconnected or not. + *@returns True if it is time to disconnect, false if it isn't. + */ + bool needDisconnect(); + + /** Tells the caller if there is pending input waiting to be processed. + *@returns True if there is pending input that has not been used, returns + * false if there isn't. + */ + bool hasInput(); + + /** Removes bytes from the begining of the input queue. Use this after + * getting the input and processing as much as you need to. + *@param nAmount The number of bytes used. + *@returns true if the update was successful, otherwise false. + */ + bool usedInput( int nAmount ); + + /** Sets the protocol to be used by this connection. All data in and out + * passes through the protocol object, which may process that data to + * filter out and process any special messages that may have been + * included. Everything that isn't processed can be accessed in the + * standard method. + *@param pNewProtocol A pointer to a protocol object that you want to + * use. + */ + void setProtocol( class Protocol *pNewProtocol ); + + /** Gets the number of bytes that are waiting in the input queue, the data + * that has yet to be processed. + *@returns The number of bytes in the input queue. + */ + int getInputAmnt(); + + /** Gets the number of bytes that are waiting in the output queue, the data + * that has yet to be sent to the connected socket. + *@returns The number of bytes in the input queue. + */ + int getOutputAmnt(); + + /** Gets a pointer to the protocol that is attatched to this connection + * object. This is useful to set modes, and send special commands in + * addition to the standard raw data reads and writes that are normally + * permitted. In fact, in everything besides a raw telnet protocol all + * data should be sent through the protocol and not the connection object. + *@returns A pointer to the Protocol assosiated with this connection. + */ + class Protocol *getProtocol(); + +private: + /** + * A buffer to keep data read from the socket in. This is filled in by + * the function readInput, which is automatically called by the + * ConnectionManager whenever new data is ready. + */ + FlexBuf xInputBuf; + + /** + * A buffer to keep data that should be sent to the socket. This is filled + * in by using the AppendOutput functions and is sent to the socket using + * the writeOutput function, which is automatically called every cycle by + * the ConnectionManager when there is pending data. + */ + FlexBuf xOutputBuf; + + /** + * The socket that the user is connected to. This is not the same as the + * socket number of the listening socket, this is the unique socket on the + * system that the data is coming to. + */ + int nSocket; + + /** + * True=active connection, False=connection lost + */ + bool bActive; + + /** + * True=disconnect next cycle (after data is transmitted), Flse=keep going. + */ + bool bDisconnectMe; + + /** + * A pointer to a protocol handler that can automatically process the data + * in the buffers. This is optional if you use the connections on your own + * but reccomended if you use this with the rest of the ConnectionManager + * system. + */ + class Protocol *pProtocol; +}; + +#endif diff --git a/src/old/connectionmanager.cpp b/src/old/connectionmanager.cpp new file mode 100644 index 0000000..ea60b2b --- /dev/null +++ b/src/old/connectionmanager.cpp @@ -0,0 +1,397 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "connectionmanager.h" +#include + +ConnectionManager::ConnectionManager( int nInitPool ) : + xLog( MultiLog::getInstance() ) +{ + //nMasterSocket = -1; + pMonitor = NULL; + for( int j = 0; j < nInitPool; j++ ) + { + lInactive.insert( lInactive.begin(), new Connection() ); + } + FD_ZERO (&fdActive); + FD_ZERO (&fdRead); + FD_ZERO (&fdWrite); + FD_ZERO (&fdException); +} + +ConnectionManager::~ConnectionManager() +{ + std::list::const_iterator i; + for( i = lActive.begin(); i != lActive.end(); i++ ) + { + delete (*i); + } + for( i = lInactive.begin(); i != lInactive.end(); i++ ) + { + delete (*i); + } +} + +bool ConnectionManager::startServer( int nPort ) +{ + /* Create the socket and set it up to accept connections. */ + struct sockaddr_in name; + + /* Give the socket a name. */ + name.sin_family = AF_INET; + name.sin_port = htons( nPort ); + + // I think this specifies who we will accept connections from, + // a good thing to make configurable later on + name.sin_addr.s_addr = htonl( INADDR_ANY ); + + return startServer( name ); +} + +bool ConnectionManager::startServer( const char *sAddr, int nPort ) +{ + /* Create the socket and set it up to accept connections. */ + struct sockaddr_in name; + + /* Give the socket a name. */ + name.sin_family = AF_INET; + name.sin_port = htons( nPort ); + + inet_aton( sAddr, &name.sin_addr ); + + return startServer( name ); +} + +bool ConnectionManager::startServer( struct sockaddr_in &name ) +{ + /* Create the socket. */ + int nMasterSocket = socket (PF_INET, SOCK_STREAM, 0); + if (nMasterSocket < 0) + { + xLog.LineLog( MultiLog::LError, "Couldn't create a listen socket."); + return false; + } + + int opt = 1; + setsockopt( + nMasterSocket, + SOL_SOCKET, + SO_REUSEADDR, + (char *)&opt, + sizeof(opt) + ); + + if (bind (nMasterSocket, (struct sockaddr *) &name, sizeof (name)) < 0) + { + xLog.LineLog( MultiLog::LError, "Couldn't bind to the listen socket."); + return false; + } + + if (listen (nMasterSocket, 40) < 0) + { + xLog.LineLog( MultiLog::LError, "Couldn't begin listening to the server socket."); + return false; + } + + /* Initialize the set of active sockets. */ + FD_SET (nMasterSocket, &fdActive); + + sMasterSocket[nMasterSocket] = name.sin_port; + + return true; +} + +bool ConnectionManager::startServer( int nPort, int nNumTries, int nTimeout ) +{ + struct timeval xTimeout; + + for( int j = 0; j < nNumTries; j++ ) + { + xLog.LineLog( MultiLog::LStatus, "Attempting to create server socket (attempt [%d/%d])...", j+1, nNumTries ); + if( startServer( nPort ) == true ) + { + return true; + } + else if( j < nNumTries-1 ) + { + xLog.LineLog( MultiLog::LStatus, "Waiting for %d secconds to allow port to clear...", nTimeout ); + xTimeout.tv_sec = nTimeout; + xTimeout.tv_usec = 0; + if (select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &xTimeout) < 0) { + xLog.LineLog( MultiLog::LError, "Error using select to sleep for a while."); + } + usleep( nTimeout ); + } + } + + return false; +} + +bool ConnectionManager::scanConnections( int nTimeout, bool bForceTimeout ) +{ + struct timeval xTimeout; + + xTimeout.tv_sec = nTimeout / 1000000; + xTimeout.tv_usec = nTimeout % 1000000; + + /* Block until input arrives on one or more active sockets. */ + fdRead = fdActive; + fdWrite = fdActive; + fdException = fdActive; + + // We removed the write checking because it just checks to see if you *can* + // write...that's stupid, they're all open, so it always exits immediately + // if there are ANY connections there... + if( TEMP_FAILURE_RETRY( select( FD_SETSIZE, &fdRead, (fd_set *)0/*&fdWrite*/, &fdException, &xTimeout ) ) < 0 ) + { + xLog.LineLog( MultiLog::LError, "Error attempting to scan open connections."); + perror("ConnectionManager"); + return false; + } + // Now we use select to sleep as well as to scan for connections, now we + // just need to fix the fact that if there are no connections, the seccond + // select call doesn't return until there is a connection... + if( bForceTimeout ) + { + if (select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &xTimeout) < 0) { + xLog.LineLog( MultiLog::LError, "Error using select to sleep for a while."); + } + } + + /* Service all the sockets with input pending. */ + for( int i = 0; i < FD_SETSIZE; ++i ) + { + if( FD_ISSET( i, &fdRead ) ) + { + if( sMasterSocket.find( i ) != sMasterSocket.end() ) + { + addConnection( i ); + } + else + { + Connection *pCon = findActiveConnection( i ); + if( pCon == NULL ) + { + xLog.LineLog( MultiLog::LError, "A connection object was lost, or never created!"); + return false; + } + + /* Data arriving on an already-connected socket. */ + if( pCon->readInput() == 0 ) + { + xLog.LineLog( MultiLog::LStatus, "Closing connection due to disconnect."); + close( i ); + FD_CLR( i, &fdActive ); + pMonitor->onClosedConnection( pCon ); + pCon->close(); + } + else + { + // We actually read something...but the connection handles + // protocol notification, so we don't need to do anything + // here... + } + } + } + } + std::list::iterator i; + for( i = lActive.begin(); i != lActive.end(); i++ ) + { + if( (*i)->isActive() == false ) + { + std::list::iterator l = i; + i--; + lInactive.insert( lInactive.end(), *l ); + lActive.erase( l ); + continue; + } + (*i)->getProtocol()->poll(); + if( (*i)->hasOutput() ) + { + (*i)->writeOutput(); + } + if( (*i)->needDisconnect() && !(*i)->hasOutput() ) + { + int prt = (*i)->getSocket(); + close( prt ); + FD_CLR( prt, &fdActive ); + pMonitor->onClosedConnection( *i ); + (*i)->close(); + lInactive.insert( lInactive.end(), *i ); + std::list::iterator l = i; + i--; + lActive.erase( l ); + xLog.LineLog( MultiLog::LStatus, "Closing connection due to server request."); + } + } + + return true; +} + +bool ConnectionManager::shutdownServer() +{ + while( !lActive.empty() ) + { + Connection *i = *(lActive.begin()); + if( i->isActive() ) + { + pMonitor->onClosedConnection( i ); + i->close(); + lInactive.insert( lInactive.end(), i ); + lActive.erase( lActive.begin() ); + } + } +/* + for( int i = 0; i < nPoolSize; i++ ) + { + + int prt = axConPool[i].getSocket(); + close( prt ); +// FD_CLR( prt, &fdActive ); + pMonitor->onClosedConnection( &axConPool[i] ); + axConPool[i].close(); + } +*/ + std::map::iterator i; + for( i = sMasterSocket.begin(); i != sMasterSocket.end(); i++ ) + { + int nSocket = (*i).first; + shutdown( nSocket, SHUT_RDWR ); + close( nSocket ); + } + + return true; +} + +bool ConnectionManager::broadcastMessage( const char *lpData, int nExcludeSocket ) +{ + std::list::const_iterator i; + for( i = lActive.begin(); i != lActive.end(); i++ ) + { + if( (*i)->isActive() && + (*i)->getSocket() != nExcludeSocket ) + { + (*i)->appendOutput( lpData ); + } + } + + return true; +} + +bool ConnectionManager::addConnection( int nSocket ) +{ + struct sockaddr_in clientname; + size_t size; + int newSocket; + + size = sizeof( clientname ); +#ifdef __CYGWIN__ + newSocket = accept( nSocket, (struct sockaddr *) &clientname, (int *)&size ); +#else + newSocket = accept( nSocket, (struct sockaddr *) &clientname, &size ); +#endif + if( newSocket < 0 ) + { + xLog.LineLog( MultiLog::LError, "Error accepting a new connection!" ); + return false; + } +// char *tmpa = inet_ntoa(clientname.sin_addr); + char tmpa[20]; + inet_ntop( AF_INET, (void *)&clientname.sin_addr, tmpa, 20 ); + xLog.LineLog( MultiLog::LStatus, "New connection from host %s, port %hd.", tmpa, ntohs (clientname.sin_port) ); +/* + int nCnt = 0; + for( int j = 0; j < nPoolSize; j++ ) + { + if( axConPool[j].isActive() ) + { + nCnt++; + } + } + xLog.LineLog( MultiLog::LStatus, "Connections %d/%d.", nCnt, nPoolSize ); + */ +// free( tmpa ); + FD_SET( newSocket, &fdActive ); + + //void nonblock(socket_t s) + { + int flags; + + flags = fcntl(newSocket, F_GETFL, 0); + flags |= O_NONBLOCK; + if (fcntl(newSocket, F_SETFL, flags) < 0) + { + return false; + } + } + + Connection *pCon = getInactiveConnection(); + pCon->open( newSocket ); + + pMonitor->onNewConnection( pCon, (*sMasterSocket.find(nSocket)).second ); + if( pCon->getProtocol() ) + pCon->getProtocol()->onNewConnection(); + + lActive.insert( lActive.end(), pCon ); + + return true; +} + +void ConnectionManager::connect( + const char *lpAddress, + int nPort, + int nProtocolPort, + Protocol *pNewProto + ) +{ + Connection *pCon = getInactiveConnection(); + pCon->open( lpAddress, nPort ); + int nSocket = pCon->getSocket(); + FD_SET( nSocket, &fdActive ); + + pCon->setProtocol( pNewProto ); + pMonitor->onNewClientConnection( pCon, nProtocolPort ); + if( pCon->getProtocol() ) + pCon->getProtocol()->onNewClientConnection(); + + lActive.insert( lActive.end(), pCon ); +} + +Connection *ConnectionManager::getInactiveConnection() +{ + if( lInactive.empty() ) + { + return new Connection(); + } + Connection *pCon = *(lInactive.begin()); + lInactive.erase( lInactive.begin() ); + return pCon; +} + +Connection *ConnectionManager::findActiveConnection( int nSocket ) +{ + std::list::const_iterator i; + for( i = lActive.begin(); i != lActive.end(); i++ ) + { + if( (*i)->getSocket() == nSocket ) + { + return *i; + } + } + + return NULL; +} + +void ConnectionManager::setConnectionMonitor( ConnectionMonitor *pNewMonitor ) +{ + pMonitor = pNewMonitor; +} diff --git a/src/old/connectionmanager.h b/src/old/connectionmanager.h new file mode 100644 index 0000000..cff036b --- /dev/null +++ b/src/old/connectionmanager.h @@ -0,0 +1,169 @@ +/** + *@file + * Contains the ConnectionManager. + *@author Mike Buland + */ + +#ifndef CONNECTIONMANAGER_H +#define CONNECTIONMANAGER_H + +#include "multilog.h" +#include "connection.h" +#include "connectionmonitor.h" +#include +#include +#include + +/** Manges incoming network connections as a server. Creates and works with + * Connection objects. All operations are performed on TCP/IP v4 right now, + * and on a single port, although any number of connections can be handled. + *@author Mike Buland + */ +class ConnectionManager +{ +public: + /** + * Sets up the basics, like storage for the pool, and so on. This does not + * actually start a server, bind to a port, or create a connection pool. + * That's all handled by startServer(). + */ + ConnectionManager( int nInitPool=40 ); + + /** + * Cleans up everything, and even clears out all still-connected Connection + * objects. + */ + virtual ~ConnectionManager(); + + /** + * Starts a server socket and binds to it, listening for new connections. + * Unlike the version of this that takes two parameters, this listens on + * all local addresses, or the virtual 0.0.0.0 address if available, which + * is mapped to all active local addresses. + *@param nPort The port to listen on. + *@returns True if the socket was bound to the port and serving was + * started. False if there was a problem connecting to the port. + */ + bool startServer( int nPort ); + + /** + * Starts a server socket and binds to it, listening only on the address + * specified. If you want to listen to all local addresses you can enter + * "0.0.0.0" for the address, but the version of this with one parameter + * is more universal. + *@param sAddr The local ip address to bind to + *@param nPort The port to listen on. + *@returns True if the socket was bound to the port and serving was + * started. False if there was a problem connecting to the port. + */ + bool startServer( const char *sAddr, int nPort ); + + /** + * I recomend probably not using this function on your own too much, it + * does the real work of setting up a socket, but requires a properly + * prepared sackaddr_in structure. This isn't too hard, but it's easier + * to use the other startServer functions. They call this function after + * some prepwork. + *@param name A properly formed sockaddr_in structure that will not be + * modified, but describes how to listen and to what to listen. + *@returns True on success. + */ + bool startServer( struct sockaddr_in &name ); + + /** + * This is identicle to the simpler startServer function except that it + * will automatically try to connect multiple times in case the first + * attempt or two doesn't work for some reason. Initially this was + * written to compensate for server sockets staying locked after they were + * closed for a while. + *@param nPort The port to listen on. + *@param nInitPool The size of the initial connection pool. This will + * grow automatically if necesarry. + *@param nNumTries The maximum number of times to try to connect. + *@param nTimeout The amount of time to wait in-between connection + * attempts. + *@returns True if the socket was bound to the port and serving was + * started. False if there was a problem connecting to the port. + */ + bool startServer( int nPort, int nNumTries, int nTimeout ); + + /** + * Scans all open connections, halting the calling processes until data + * is received or nTimeout ms have gone by. While waiting for the timeout + * to complete the process is placed into an idle mode. + *@param nTimeout The number of millisecconds to wait if there is nothing + * to actually do. + *@param bForceTimeout If set to true, this will force the scanner to wait + * for the timout to complete before returning, even if there was pending + * data. + */ + bool scanConnections( int nTimeout, bool bForceTimeout ); + + /** Shutdown the server and all assosiated sockets. + *@returns True if every socket was closed without problem. + */ + bool shutdownServer(); + + /** Sends a message directly to every connected port. + *@param lpData A null-terminated string of data to send. + *@param nExcludeSocket An optional socket to exclude from the broadcast. + *@returns True if every socket that should have gotten the message did. + */ + bool broadcastMessage( const char *lpData, int nExcludeSocket=-1 ); + + /** Sets a monitor for the manager. The monitor is sent notifications + * whenever a socket is connected, disconnected, or whenever an error + * occurs. + *@param pNewMonitor A pointer to a preconstructed ConnectionMonitor + */ + void setConnectionMonitor( ConnectionMonitor *pNewMonitor ); + + void connect( const char *lpAddress, int nPort, int nProtocolPort, Protocol *pNewProto ); + +private: + /** + * Take care of the work of actually accepting a connection. This will + * accept the connection, set the initial modes, and add it to the master + * list of active connections, as well as fire off any messages that need + * to be handled by anything else. + *@param nSocket The handle of the listening socket that had an incoming + * connection. + *@returns True if everything worked, False otherwise. + */ + bool addConnection( int nSocket ); + + /** + * Seraches the internal lists of connections for one with a specific + * socket. + *@param nSocket The socket the connection is using for communication. + * This is the unique socket and not the one that the connection was + * initially to. + *@returns NULL if no connection was found, otherwise a pointer to a live + * Connection object. + */ + Connection *findActiveConnection( int nSocket ); + + /** + * Searches the connection pool for an object that isn't in use yet, and + * returns it, ready to be filled in and used. + *@returns An unused connection object ready for use. + *@todo Check this code over to insure that the pool grows appropriately + * when enough extra connections are detected. + */ + Connection *getInactiveConnection(); + + std::map sMasterSocket; + //int nMasterSocket; /**< The listening or server socket. */ + fd_set fdActive; /**< The active socket set. */ + fd_set fdRead; /**< The sockets ready for a read. */ + fd_set fdWrite; /**< The sockets ready for a write. */ + fd_set fdException; /**< The sockets that have gotten errors. */ + std::list lInactive; /**< The pool of inactive Connections */ + std::list lActive; /**< The pool of active Connections */ + MultiLog &xLog; /**< A handle to the active multilog. */ + + /** The ConnectionMonitor to notify of new connections. */ + ConnectionMonitor *pMonitor; +}; + +#endif diff --git a/src/old/connectionmonitor.cpp b/src/old/connectionmonitor.cpp new file mode 100644 index 0000000..4f90ee6 --- /dev/null +++ b/src/old/connectionmonitor.cpp @@ -0,0 +1,10 @@ +#include "connectionmonitor.h" + +ConnectionMonitor::ConnectionMonitor() +{ +} + +ConnectionMonitor::~ConnectionMonitor() +{ +} + diff --git a/src/old/connectionmonitor.h b/src/old/connectionmonitor.h new file mode 100644 index 0000000..9910556 --- /dev/null +++ b/src/old/connectionmonitor.h @@ -0,0 +1,47 @@ +/**@file + * Describes the ConnectionMonitor class. + */ +#ifndef CONNECTIONMONITOR_H +#define CONNECTIONMONITOR_H + +#include "connection.h" + +/** Connection Monitor defines the base class of the objects that will be + * notified whenever a connection is created or destroyed. + *@author Mike Buland + */ +class ConnectionMonitor +{ +public: + /** + * This is only here for completeness. It does nothing. + */ + ConnectionMonitor(); + + /** + * This is only here for completeness. It does nothing. + */ + virtual ~ConnectionMonitor(); + + /** Receives the notification that new connection was received. + *@param pCon The connection that was created. + *@param nSocket The socket that the client connected to, used to determine + * which protocol to apply. + *@returns Should return a true value if everything is OK, a false to + * force a shutdown. + */ + virtual bool onNewConnection( Connection *pCon, int nPort ) = 0; + virtual bool onNewClientConnection( Connection *pCon, int nPort ) + { + return onNewConnection( pCon, nPort ); + }; + + /** Receives the notification that a connection was closed. + *@param pCon The connection that was closed. + *@returns Should return a true value if everything is OK, a false to + * force a shutdown. + */ + virtual bool onClosedConnection( Connection *pCon ) = 0; +}; + +#endif diff --git a/src/old/exceptionbase.cpp b/src/old/exceptionbase.cpp new file mode 100644 index 0000000..f3d22da --- /dev/null +++ b/src/old/exceptionbase.cpp @@ -0,0 +1,70 @@ +#include "exceptionbase.h" +#include + +ExceptionBase::ExceptionBase( const char *lpFormat, ... ) throw() : + nErrorCode( 0 ), + sWhat( NULL ) +{ + va_list ap; + + va_start(ap, lpFormat); + setWhat( lpFormat, ap ); + va_end(ap); +} + +ExceptionBase::ExceptionBase( int nCode, const char *lpFormat, ... ) throw() : + nErrorCode( nCode ), + sWhat( NULL ) +{ + va_list ap; + + va_start(ap, lpFormat); + setWhat( lpFormat, ap ); + va_end(ap); +} + +ExceptionBase::ExceptionBase( int nCode ) throw() : + nErrorCode( nCode ), + sWhat( NULL ) +{ +} + +ExceptionBase::~ExceptionBase() throw() +{ + if( sWhat ) + { + delete[] sWhat; + sWhat = NULL; + } +} + +void ExceptionBase::setWhat( const char *lpFormat, va_list &vargs ) +{ + if( sWhat ) delete[] sWhat; + int nSize; + + nSize = vsnprintf( NULL, 0, lpFormat, vargs ); + sWhat = new char[nSize+1]; + vsnprintf( sWhat, nSize+1, lpFormat, vargs ); +} + +void ExceptionBase::setWhat( const char *lpText ) +{ + if( sWhat ) delete[] sWhat; + int nSize; + + nSize = strlen( lpText ); + sWhat = new char[nSize+1]; + strcpy( sWhat, lpText ); +} + +const char *ExceptionBase::what() const throw() +{ + return sWhat; +} + +int ExceptionBase::getErrorCode() +{ + return nErrorCode; +} + diff --git a/src/old/exceptionbase.h b/src/old/exceptionbase.h new file mode 100644 index 0000000..6f1eca7 --- /dev/null +++ b/src/old/exceptionbase.h @@ -0,0 +1,105 @@ +#ifndef EXCEPTION_BASE_H +#define EXCEPTION_BASE_H + +#include +#include +#include + +/** + * A generalized Exception base class. This is nice for making general and + * flexible child classes that can create new error code classes. + */ +class ExceptionBase : public std::exception +{ +public: + /** + * Construct an exception with an error code of zero, but with a + * description. The use of this is not reccomended most of the time, it's + * generally best to include an error code with the exception so your + * program can handle the exception in a better way. + * @param sFormat The format of the text. See printf for more info. + */ + ExceptionBase( const char *sFormat, ... ) throw(); + + /** + * + * @param nCode + * @param sFormat + */ + ExceptionBase( int nCode, const char *sFormat, ... ) throw(); + + /** + * + * @param nCode + * @return + */ + ExceptionBase( int nCode=0 ) throw(); + + /** + * + * @return + */ + virtual ~ExceptionBase() throw(); + + /** + * + * @return + */ + virtual const char *what() const throw(); + + /** + * + * @return + */ + int getErrorCode(); + + /** + * + * @param lpFormat + * @param vargs + */ + void setWhat( const char *lpFormat, va_list &vargs ); + + /** + * + * @param lpText + */ + void setWhat( const char *lpText ); + +private: + int nErrorCode; /**< The code for the error that occured. */ + char *sWhat; /**< The text string telling people what went wrong. */ +}; + +#define subExceptionDecl( name ) \ +class name : public ExceptionBase \ +{ \ + public: \ + name( const char *sFormat, ... ) throw (); \ + name( int nCode, const char *sFormat, ... ) throw(); \ + name( int nCode=0 ) throw (); \ +}; + +#define subExceptionDef( name ) \ +name::name( const char *lpFormat, ... ) throw() : \ + ExceptionBase( 0 ) \ +{ \ + va_list ap; \ + va_start( ap, lpFormat ); \ + setWhat( lpFormat, ap ); \ + va_end( ap ); \ +} \ +name::name( int nCode, const char *lpFormat, ... ) throw() : \ + ExceptionBase( nCode ) \ +{ \ + va_list ap; \ + va_start( ap, lpFormat ); \ + setWhat( lpFormat, ap ); \ + va_end( ap ); \ +} \ +name::name( int nCode ) throw() : \ + ExceptionBase( nCode ) \ +{ \ +} + +#endif diff --git a/src/old/exceptions.cpp b/src/old/exceptions.cpp new file mode 100644 index 0000000..ce79a5e --- /dev/null +++ b/src/old/exceptions.cpp @@ -0,0 +1,8 @@ +#include "exceptions.h" +#include + +subExceptionDef( XmlException ) +subExceptionDef( FileException ) +subExceptionDef( ConnectionException ) +subExceptionDef( PluginException ) + diff --git a/src/old/exceptions.h b/src/old/exceptions.h new file mode 100644 index 0000000..0ab2b15 --- /dev/null +++ b/src/old/exceptions.h @@ -0,0 +1,25 @@ +#ifndef EXCEPTIONS_H +#define EXCEPTIONS_H + +#include "exceptionbase.h" +#include + +subExceptionDecl( XmlException ) +subExceptionDecl( FileException ) +subExceptionDecl( ConnectionException ) +subExceptionDecl( PluginException ) + +enum eFileException +{ + excodeEOF +}; + +enum eConnectionException +{ + excodeReadError, + excodeBadReadError, + excodeConnectionClosed, + excodeSocketTimeout +}; + +#endif diff --git a/src/old/flexbuf.cpp b/src/old/flexbuf.cpp new file mode 100644 index 0000000..6d55294 --- /dev/null +++ b/src/old/flexbuf.cpp @@ -0,0 +1,229 @@ +#include "flexbuf.h" +#include + +FlexBuf::FlexBuf() +{ + lpBuf = new char[1024]; + nLastChar = 0; + nFirstChar = 0; + nSize = 1024; + nFill = 0; + clearData(); +} + +FlexBuf::~FlexBuf() +{ + delete[] lpBuf; +} + +bool FlexBuf::appendData( const char *lpData, int nDSize ) +{ + int nStrLen; + if( nDSize < 0 ) + { + nStrLen = strlen( lpData ); + } + else + { + nStrLen = nDSize; + } + + if( nLastChar + nStrLen + 1 > nSize ) + { + if( nFill + nStrLen + 1 < nSize ) + { + memcpy( lpBuf, lpBuf+nFirstChar, nFill ); + nLastChar -= nFirstChar; + nFirstChar = 0; + } + else + { + nSize += nStrLen+1; + char *lpNewBuf = new char[nSize]; + memcpy( lpNewBuf, lpBuf+nFirstChar, nFill ); + delete[] lpBuf; + lpBuf = lpNewBuf; + nLastChar -= nFirstChar; + nFirstChar = 0; + } + } + + memcpy( &lpBuf[nLastChar], lpData, nStrLen ); + nLastChar += nStrLen; + nFill += nStrLen; + lpBuf[nLastChar] = '\0'; + + return true; +} + +bool FlexBuf::appendData( const char lData ) +{ + if( nLastChar + 2 > nSize ) + { + if( nFill+2 < nSize ) + { + memcpy( lpBuf, lpBuf+nFirstChar, nFill ); + nLastChar -= nFirstChar; + nFirstChar = 0; + } + else + { + nSize += 1024; + char *lpNewBuf = new char[nSize]; + memcpy( lpNewBuf, lpBuf+nFirstChar, nFill ); + delete[] lpBuf; + lpBuf = lpNewBuf; + nLastChar -= nFirstChar; + nFirstChar = 0; + } + } + + lpBuf[nLastChar] = lData; + nLastChar++; + nFill++; + lpBuf[nLastChar] = '\0'; + + return true; +} + +bool FlexBuf::appendData( const short lData ) +{ + return appendData( (const char *)&lData, sizeof(short) ); +} + +bool FlexBuf::appendData( const int lData ) +{ + return appendData( (const char *)&lData, sizeof(int) ); +} + +bool FlexBuf::appendData( const long lData ) +{ + return appendData( (const char *)&lData, sizeof(long) ); +} + +bool FlexBuf::appendData( const float lData ) +{ + return appendData( (const char *)&lData, sizeof(float) ); +} + +bool FlexBuf::appendData( const double lData ) +{ + return appendData( (const char *)&lData, sizeof(double) ); +} + +bool FlexBuf::appendData( const unsigned char lData ) +{ + return appendData( (const char)lData ); +} + +bool FlexBuf::appendData( const unsigned short lData ) +{ + return appendData( (const char *)&lData, sizeof(short) ); +} + +bool FlexBuf::appendData( const unsigned long lData ) +{ + return appendData( (const char *)&lData, sizeof(long) ); +} + +bool FlexBuf::appendData( const unsigned int lData ) +{ + return appendData( (const char *)&lData, sizeof(int) ); +} + +bool FlexBuf::clearData() +{ + nFirstChar = nLastChar = nFill = 0; + lpBuf[nLastChar] = '\0'; + + return true; +} + +const char *FlexBuf::getData() +{ + return (lpBuf+nFirstChar); +} + +int FlexBuf::getLength() +{ + return nFill; +} + +int FlexBuf::getCapacity() +{ + return nSize; +} + +bool FlexBuf::usedData( int nAmount ) +{ + // Remove from the end if negative + if( nAmount < 0 ) + { + if( nFill+nAmount < 0 ) + { + nFill = nFirstChar = nLastChar = 0; + return true; + } + nLastChar += nAmount; + nFill += nAmount; + return true; + } + if( nAmount > nFill ) + { + nAmount = nSize; +// return false; + } + + //nLastChar -= nAmount; + nFirstChar += nAmount; + nFill -= nAmount; + + if( nFill == 0 ) + { + nFirstChar = nLastChar = 0; + } + + //if( nLastChar > 0 ) + //{ + //memmove( lpBuf, &lpBuf[nAmount], nLastChar ); + //} + + return true; +} + +int FlexBuf::findChar( char cTarget ) +{ + for( int j = nFirstChar; j < nLastChar; j++ ) + { + if( lpBuf[j] == cTarget ) + { + return j; + } + } + + return -1; +} + +void FlexBuf::ensureCapacity( int nAmount ) +{ + if( nLastChar + nAmount + 1 > nSize ) + { + if( nFill + nAmount + 1 < nSize ) + { + memcpy( lpBuf, lpBuf+nFirstChar, nFill ); + nLastChar -= nFirstChar; + nFirstChar = 0; + } + else + { + nSize += nAmount+1; + char *lpNewBuf = new char[nSize]; + memcpy( lpNewBuf, lpBuf+nFirstChar, nFill ); + delete[] lpBuf; + lpBuf = lpNewBuf; + nLastChar -= nFirstChar; + nFirstChar = 0; + } + } +} + diff --git a/src/old/flexbuf.h b/src/old/flexbuf.h new file mode 100644 index 0000000..7d7f11a --- /dev/null +++ b/src/old/flexbuf.h @@ -0,0 +1,162 @@ +/**\flexbuf.h + * Describes the FlexBuf class. + *@author Mike Buland + */ + +#ifndef FLEXBUF_H +#define FLEXBUF_H + +/** Stores any amount of data, but starts small, growing as necesarry. + * It is optimized to work with stream type situations, with data being + * added to the end while it is being taken from the begning. + *@todo Set this class up to auto-shrink back to a specified sized buffer each + * time it has shrunk below that for enough operations. + *@author Mike Buland + */ +class FlexBuf +{ +public: + /** + * Construct a blank FlexBuf containing about 1k of buffer space. + */ + FlexBuf(); + + /** + * Clean up the FlexBuf, delete all buffers. + */ + virtual ~FlexBuf(); + + /** Appends a whole string of data to the buffer. The string + * must be null terminated. + *@param lpData The data to append to the buffer. + *@param nDSize The size of the data described by lpData. If this + * value is -1 lpData is treated as a null-terminated string. + *@returns True if no problems occured, false otherwise. + */ + bool appendData( const char *lpData, int nDSize=-1 ); + + /** Appends a single character to the end of the buffer. + *@param lData The character to append to the buffer. + *@returns True if no problems occured, false otherwise. + */ + bool appendData( const char lData ); + + /** + * Append the short to the buffer. + *@param lData The short to add to the buffer queue. + *@returns True if everything is ok, false otherwise. + */ + bool appendData( const short lData ); + + /** + * Append the int to the buffer. + *@param lData The int to add to the buffer queue. + *@returns True if everything is ok, false otherwise. + */ + bool appendData( const int lData ); + + /** + * Append the long to the buffer. + *@param lData The long to add to the buffer queue. + *@returns True if everything is ok, false otherwise. + */ + bool appendData( const long lData ); + + /** + * Append the float to the buffer. + *@param lData The float to add to the buffer queue. + *@returns True if everything is ok, false otherwise. + */ + bool appendData( const float lData ); + + /** + * Append the double to the buffer. + *@param lData The double to add to the buffer queue. + *@returns True if everything is ok, false otherwise. + */ + bool appendData( const double lData ); + + /** + * Append the unsigned char to the buffer. + *@param lData The unsigned char to add to the buffer queue. + *@returns True if everything is ok, false otherwise. + */ + bool appendData( const unsigned char lData ); + + /** + * Append the unsigned short to the buffer. + *@param lData The unsigned short to add to the buffer queue. + *@returns True if everything is ok, false otherwise. + */ + bool appendData( const unsigned short lData ); + + /** + * Append the unsigned int to the buffer. + *@param lData The unsigned int to add to the buffer queue. + *@returns True if everything is ok, false otherwise. + */ + bool appendData( const unsigned int lData ); + + /** + * Append the unsigned long to the buffer. + *@param lData The unsigned long to add to the buffer queue. + *@returns True if everything is ok, false otherwise. + */ + bool appendData( const unsigned long lData ); + + /** Removes all pending data from the buffer. + *@returns True if no problems occured, false otherwise. + */ + bool clearData(); + + /** Gets a pointer to the internal buffer, at the begining of the current + * data stream. + *@returns A pointer to the internal data buffer. + */ + const char *getData(); + + /** Gets the length of the current buffer (how much data is really in the + * buffer, not it's current capacity, for that check getCapacity) + *@returns The length of the current buffer. + */ + int getLength(); + + /** Gets the current capacity of the FlexBuf. If the size nears this value + * then the entire buffer is resized to accomidate more data. + *@returns The current capacity of the FlexBuf. + */ + int getCapacity(); + + /** + * Removes nAmount bytes from the begning of the buffer. Actually, if + * nAmount happens to be negative it will remove tha absolute value of + * nValue bytes from the end of the buffer, like the old delData command. + *@param nAmount The number of bytes used. + *@returns True if everything was successful, false if there was an error. + */ + bool usedData( int nAmount ); + + /** Finds the first instance of the given character in the buffer and + * returns an index to it. + *@param cTarget The character you're looking for. + *@returns The index of the first instance of the given character, or + * -1 if it just wasn't found. + */ + int findChar( char cTarget ); + + void ensureCapacity( int nAmount ); + +private: + /** The raw storage location of the FlexBuf. */ + char *lpBuf; + /** The real size of the FlexBuf. */ + int nSize; + /** Where the last char is. */ + int nLastChar; + /** Where the first char is. */ + int nFirstChar; + /** The amount of real data in the FlexBuf. This is effectively nLastChar-nFirstChar. */ + int nFill; +}; + +#endif diff --git a/src/old/formula.cpp b/src/old/formula.cpp new file mode 100644 index 0000000..cf63cf3 --- /dev/null +++ b/src/old/formula.cpp @@ -0,0 +1,262 @@ +#include "formula.h" + +subExceptionDef( ParseException ); + +Formula::Formula() +{ + hVars["pi"] = M_PI; + hVars["e"] = M_E; + + hFunc["sin"] = FuncSin(); +} + +Formula::~Formula() +{ +} + +double Formula::run( char *sFormula ) +{ + for(;;) + { + uint8_t tNum = nextToken( &sFormula ); + if( tNum == symEOS ) + break; + else if( tNum == symSubtract ) + { + tNum = nextToken( &sFormula ); + if( tNum != symNumber ) + throw ParseException("Unary minus must be followed by a number, " + "variable, function, or parenthesis."); + sValue.top() = -sValue.top(); + } + else if( tNum == symOpenParen ) + { + sOper.push( tNum ); + continue; + } + +oppart: uint8_t tOpr = nextToken( &sFormula ); + if( tOpr == symEOS ) + { + //printf("EOS "); + reduce(); + return sValue.top(); + break; + } + if( !sOper.empty() && getPrec( sOper.top() ) > getPrec( tOpr ) ) + { + reduce(); + } + if( tOpr != symCloseParen ) + { + sOper.push( tOpr ); + } + else + { + reduce( true ); + goto oppart; + } + } + return sValue.top(); +} + +void Formula::reduce( bool bCloseParen ) +{ + while( !sOper.empty() ) + { + uint8_t nOpr = sOper.top(); + if( nOpr == symOpenParen ) + { + //printf("Found ( stopping reduction.\n"); + if( bCloseParen == true ) + sOper.pop(); + return; + } + sOper.pop(); + + double dTop = sValue.top(); + sValue.pop(); + + switch( nOpr ) + { + case symAdd: + //printf("%f + %f = %f\n", sValue.top(), dTop, sValue.top()+dTop ); + sValue.top() += dTop; + break; + + case symSubtract: + //printf("%f - %f = %f\n", sValue.top(), dTop, sValue.top()-dTop ); + sValue.top() -= dTop; + break; + + case symMultiply: + //printf("%f * %f = %f\n", sValue.top(), dTop, sValue.top()*dTop ); + sValue.top() *= dTop; + break; + + case symDivide: + //printf("%f / %f = %f\n", sValue.top(), dTop, sValue.top()/dTop ); + sValue.top() /= dTop; + break; + + case symExponent: + //printf("%f ^ %f = %f\n", sValue.top(), dTop, pow(sValue.top(),dTop) ); + sValue.top() = pow( sValue.top(), dTop ); + break; + + case symModulus: + //printf("%f %% %f = %f\n", sValue.top(), dTop, fmod(sValue.top(),dTop) ); + sValue.top() = fmod( sValue.top(), dTop ); + break; + } + } + + if( bCloseParen == true ) + { + throw ParseException("Close-paren found without matching open-paren."); + } +} + +uint8_t Formula::getPrec( uint8_t nOper ) +{ + switch( nOper ) + { + case symNumber: + case symVariable: + case symOpenParen: + case symCloseParen: + return 0; + + case symAdd: + case symSubtract: + return 1; + + case symMultiply: + case symDivide: + case symModulus: + return 2; + + case symExponent: + return 3; + + default: + return 0; + } +} + +uint8_t Formula::nextToken( char **sBuf ) +{ + for(;;) + { + char cbuf = **sBuf; + ++(*sBuf); + switch( cbuf ) + { + case '+': + return symAdd; + + case '-': + return symSubtract; + + case '*': + return symMultiply; + + case '/': + return symDivide; + + case '^': + return symExponent; + + case '%': + return symModulus; + + case '(': + return symOpenParen; + + case ')': + return symCloseParen; + + case ' ': + case '\t': + case '\n': + case '\r': + break; + + case '\0': + return symEOS; + + default: + if( cbuf == '.' || (cbuf >= '0' && cbuf <= '9') ) + { + char num[50]={cbuf}; + int nPos = 1; + bool bDot = false; + + for(;;) + { + cbuf = **sBuf; + if( cbuf == '.' ) + { + if( bDot == false ) + bDot = true; + else + throw ParseException( + "Numbers cannot have more than one " + ". in them." + ); + } + if( cbuf == '.' || (cbuf >= '0' && cbuf <= '9') ) + { + num[nPos++] = cbuf; + } + else + { + num[nPos] = '\0'; + sValue.push( strtod( num, NULL ) ); + return symNumber; + } + ++(*sBuf); + } + } + else if( (cbuf >= 'a' && cbuf <= 'z') || + (cbuf >= 'A' && cbuf <= 'Z') || + (cbuf == '_') ) + { + char tok[50]={cbuf}; + int nPos = 1; + + for(;;) + { + cbuf = **sBuf; + if( (cbuf >= 'a' && cbuf <= 'z') || + (cbuf >= 'A' && cbuf <= 'Z') || + (cbuf >= '0' && cbuf <= '9') || + cbuf == '_' || cbuf == '.' || cbuf == ':' ) + { + tok[nPos++] = cbuf; + } + else + { + tok[nPos] = '\0'; + //printf("Checking variable \"%s\"\n", tok ); + try + { + sValue.push( hVars[tok] ); + return symNumber; + } + catch( HashException &e ) + { + throw ParseException( + "No variable named \"%s\" exists.", + tok + ); + } + } + ++(*sBuf); + } + } + break; + } + } +} + diff --git a/src/old/formula.h b/src/old/formula.h new file mode 100644 index 0000000..939eb09 --- /dev/null +++ b/src/old/formula.h @@ -0,0 +1,77 @@ +#ifndef FORMULA_H +#define FORMULA_H + +#include + +#include +#include +#include "sbuffer.h" + +#include "exceptionbase.h" +#include "hash.h" + +subExceptionDecl( ParseException ); + +/** + * Implements a very simple formula parser that allows use of variables and + * custom functions. This is based on a simple calculator-type parser that + * executes as it processes, accounting for operator precedence and grouping. + */ +class Formula +{ +public: + Formula(); + virtual ~Formula(); + + double run( char *sFormula ); + + typedef Hash varHash; + varHash hVars; + + typedef struct Func + { + double operator()( double x ) + { + return 0.0; + } + } Func; + + typedef Hash funcHash; + funcHash hFunc; + + typedef struct FuncSin : Func + { + double operator()( double x ) + { + return sin( x ); + } + } FuncSin; + +private: + enum + { + symEOS, + symAdd, + symSubtract, + symMultiply, + symDivide, + symOpenParen, + symCloseParen, + symNumber, + symVariable, + symExponent, + symModulus + }; + + typedef uint8_t symType; + + std::stack sOper; + std::stack sValue; + +private: + symType getPrec( symType nOper ); + symType nextToken( char **sBuf ); + void reduce( bool bCloseParen = false ); +}; + +#endif diff --git a/src/old/fstring.cpp b/src/old/fstring.cpp new file mode 100644 index 0000000..82d024d --- /dev/null +++ b/src/old/fstring.cpp @@ -0,0 +1,13 @@ +#include "fstring.h" +#include "hash.h" + +template<> uint32_t __calcHashCode( const FString &k ) +{ + return __calcHashCode( k.c_str() ); +} + +template<> bool __cmpHashKeys( const FString &a, const FString &b ) +{ + return a == b; +} + diff --git a/src/old/fstring.h b/src/old/fstring.h new file mode 100644 index 0000000..c5397cc --- /dev/null +++ b/src/old/fstring.h @@ -0,0 +1,651 @@ +#ifndef F_STRING_H +#define F_STRING_H + +#include +#include +#include "serializable.h" +#include "serializer.h" + +template< typename chr > +struct FStringChunk +{ + long nLength; + chr *pData; + FStringChunk *pNext; +}; + +/** + * Flexible String class. This class was designed with string passing and + * generation in mind. Like the standard string class you can specify what + * datatype to use for each character. Unlike the standard string class, + * collection of appended and prepended terms is done lazily, making long + * operations that involve many appends very inexpensive. In addition internal + * ref-counting means that if you pass strings around between functions there's + * almost no overhead in time or memory since a reference is created and no + * data is actually copied. This also means that you never need to put any + * FBasicString into a ref-counting container class. + */ +template< typename chr, typename chralloc=std::allocator, typename chunkalloc=std::allocator > > +class FBasicString : public Serializable +{ +#ifndef VALTEST +#define cpy( dest, src, size ) memcpy( dest, src, size*sizeof(chr) ) +#endif +private: + typedef struct FStringChunk Chunk; + typedef struct FBasicString MyType; + +public: + FBasicString() : + nLength( 0 ), + pnRefs( NULL ), + pFirst( NULL ), + pLast( NULL ) + { + } + + FBasicString( const chr *pData ) : + nLength( 0 ), + pnRefs( NULL ), + pFirst( NULL ), + pLast( NULL ) + { + append( pData ); + } + + FBasicString( const chr *pData, long nLength ) : + nLength( 0 ), + pnRefs( NULL ), + pFirst( NULL ), + pLast( NULL ) + { + append( pData, nLength ); + } + + FBasicString( const MyType &rSrc ) : + nLength( 0 ), + pnRefs( NULL ), + pFirst( NULL ), + pLast( NULL ) + { + // Here we have no choice but to copy, since the other guy is a const. + // In the case that the source were flat, we could get a reference, it + // would make some things faster, but not matter in many other cases. + + joinShare( rSrc ); + //copyFrom( rSrc ); + } + + FBasicString( const MyType &rSrc, long nLength ) : + nLength( 0 ), + pnRefs( NULL ), + pFirst( NULL ), + pLast( NULL ) + { + append( rSrc.pFirst->pData, nLength ); + } + + FBasicString( const MyType &rSrc, long nStart, long nLength ) : + nLength( 0 ), + pnRefs( NULL ), + pFirst( NULL ), + pLast( NULL ) + { + append( rSrc.pFirst->pData+nStart, nLength ); + } + + FBasicString( long nSize ) : + nLength( nSize ), + pnRefs( NULL ), + pFirst( NULL ), + pLast( NULL ) + { + pFirst = pLast = newChunk( nSize ); + } + + virtual ~FBasicString() + { + clear(); + } + + void append( const chr *pData ) + { + long nLen; + for( nLen = 0; pData[nLen] != (chr)0; nLen++ ); + + Chunk *pNew = newChunk( nLen ); + cpy( pNew->pData, pData, nLen ); + + appendChunk( pNew ); + } + + void append( const chr *pData, long nLen ) + { + Chunk *pNew = newChunk( nLen ); + + cpy( pNew->pData, pData, nLen ); + + appendChunk( pNew ); + } + + void prepend( const chr *pData ) + { + long nLen; + for( nLen = 0; pData[nLen] != (chr)0; nLen++ ); + + Chunk *pNew = newChunk( nLen ); + cpy( pNew->pData, pData, nLen ); + + prependChunk( pNew ); + } + + void prepend( const chr *pData, long nLen ) + { + Chunk *pNew = newChunk( nLen ); + + cpy( pNew->pData, pData, nLen ); + + prependChunk( pNew ); + } + + void clear() + { + realClear(); + } + + void resize( long nNewSize ) + { + if( nLength == nNewSize ) + return; + + flatten(); + + Chunk *pNew = newChunk( nNewSize ); + long nNewLen = (nNewSizepData, pFirst->pData, nNewLen ); + pNew->pData[nNewLen] = (chr)0; + aChr.deallocate( pFirst->pData, pFirst->nLength+1 ); + aChunk.deallocate( pFirst, 1 ); + pFirst = pLast = pNew; + nLength = nNewSize; + } + + long getSize() const + { + return nLength; + } + + chr *getStr() + { + if( pFirst == NULL ) + return NULL; + + flatten(); + return pFirst->pData; + } + + const chr *getStr() const + { + if( pFirst == NULL ) + return NULL; + + flatten(); + return pFirst->pData; + } + + chr *c_str() + { + if( pFirst == NULL ) + return NULL; + + flatten(); + return pFirst->pData; + } + + const chr *c_str() const + { + if( pFirst == NULL ) + return NULL; + + flatten(); + return pFirst->pData; + } + + MyType &operator +=( const chr *pData ) + { + append( pData ); + + return (*this); + } + + MyType &operator +=( const MyType &rSrc ) + { + rSrc.flatten(); + append( rSrc.pFirst->pData, rSrc.nLength ); + + return (*this); + } + + MyType &operator +=( const chr pData ) + { + chr tmp[2] = { pData, (chr)0 }; + append( tmp ); + + return (*this); + } + + MyType &operator =( const chr *pData ) + { + clear(); + append( pData ); + + return (*this); + } + + MyType &operator =( const MyType &rSrc ) + { + //if( rSrc.isFlat() ) + //{ + joinShare( rSrc ); + //} + //else + //{ + // copyFrom( rSrc ); + //} + // + + return (*this); + } + + bool operator ==( const chr *pData ) const + { + if( pFirst == NULL ) { + if( pData == NULL ) + return true; + return false; + } + + flatten(); + const chr *a = pData; + chr *b = pFirst->pData; + for( ; *a!=(chr)0; a++, b++ ) + { + if( *a != *b ) + return false; + } + + return true; + } + + bool operator ==( const MyType &pData ) const + { + if( pFirst == pData.pFirst ) + return true; + if( pFirst == NULL ) + return false; + + flatten(); + pData.flatten(); + const chr *a = pData.pFirst->pData; + chr *b = pFirst->pData; + for( ; *a!=(chr)0; a++, b++ ) + { + if( *a != *b ) + return false; + } + + return true; + } + + bool operator !=(const chr *pData ) const + { + return !(*this == pData); + } + + bool operator !=(const MyType &pData ) const + { + return !(*this == pData); + } + + chr &operator[]( long nIndex ) + { + flatten(); + + return pFirst->pData[nIndex]; + } + + const chr &operator[]( long nIndex ) const + { + flatten(); + + return pFirst->pData[nIndex]; + } + + bool isWS( long nIndex ) const + { + flatten(); + + return pFirst->pData[nIndex]==' ' || pFirst->pData[nIndex]=='\t' + || pFirst->pData[nIndex]=='\r' || pFirst->pData[nIndex]=='\n'; + } + + bool isAlpha( long nIndex ) const + { + flatten(); + + return (pFirst->pData[nIndex] >= 'a' && pFirst->pData[nIndex] <= 'z') + || (pFirst->pData[nIndex] >= 'A' && pFirst->pData[nIndex] <= 'Z'); + } + + void toLower() + { + flatten(); + unShare(); + + for( long j = 0; j < nLength; j++ ) + { + if( pFirst->pData[j] >= 'A' && pFirst->pData[j] <= 'Z' ) + pFirst->pData[j] -= 'A'-'a'; + } + } + + void toUpper() + { + flatten(); + unShare(); + + for( long j = 0; j < nLength; j++ ) + { + if( pFirst->pData[j] >= 'a' && pFirst->pData[j] <= 'z' ) + pFirst->pData[j] += 'A'-'a'; + } + } + + void serialize( class Serializer &ar ) + { + if( ar.isLoading() ) + { + clear(); + long nLen; + ar >> nLen; + + Chunk *pNew = newChunk( nLen ); + ar.read( pNew->pData, nLen*sizeof(chr) ); + appendChunk( pNew ); + } + else + { + flatten(); + + ar << nLength; + ar.write( pFirst->pData, nLength*sizeof(chr) ); + } + } + +private: + void flatten() const + { + if( isFlat() ) + return; + + if( pFirst == NULL ) + return; + + unShare(); + + Chunk *pNew = newChunk( nLength ); + chr *pos = pNew->pData; + Chunk *i = pFirst; + for(;;) + { + cpy( pos, i->pData, i->nLength ); + pos += i->nLength; + i = i->pNext; + if( i == NULL ) + break; + } + realClear(); + + pLast = pFirst = pNew; + nLength = pNew->nLength; + } + + void realClear() const + { + if( pFirst == NULL ) + return; + + if( isShared() ) + { + decRefs(); + } + else + { + Chunk *i = pFirst; + for(;;) + { + Chunk *n = i->pNext; + aChr.deallocate( i->pData, i->nLength+1 ); + aChunk.deallocate( i, 1 ); + if( n == NULL ) + break; + i = n; + } + pFirst = pLast = NULL; + nLength = 0; + } + } + + void copyFrom( const FBasicString &rSrc ) + { + if( rSrc.pFirst == NULL ) + return; + + decRefs(); + + Chunk *pNew = newChunk( rSrc.nLength ); + chr *pos = pNew->pData; + Chunk *i = rSrc.pFirst; + for(;;) + { + cpy( pos, i->pData, i->nLength ); + pos += i->nLength; + i = i->pNext; + if( i == NULL ) + break; + } + clear(); + + appendChunk( pNew ); + } + + bool isFlat() const + { + return (pFirst == pLast); + } + + bool isShared() const + { + return (pnRefs != NULL); + } + + Chunk *newChunk() const + { + Chunk *pNew = aChunk.allocate( 1 ); + pNew->pNext = NULL; + return pNew; + } + + Chunk *newChunk( long nLen ) const + { + Chunk *pNew = aChunk.allocate( 1 ); + pNew->pNext = NULL; + pNew->nLength = nLen; + pNew->pData = aChr.allocate( nLen+1 ); + pNew->pData[nLen] = (chr)0; + return pNew; + } + + void appendChunk( Chunk *pNewChunk ) + { + unShare(); + + if( pFirst == NULL ) + pLast = pFirst = pNewChunk; + else + { + pLast->pNext = pNewChunk; + pLast = pNewChunk; + } + + nLength += pNewChunk->nLength; + } + + void prependChunk( Chunk *pNewChunk ) + { + unShare(); + + if( pFirst == NULL ) + pLast = pFirst = pNewChunk; + else + { + pNewChunk->pNext = pFirst; + pFirst = pNewChunk; + } + + nLength += pNewChunk->nLength; + } + + void joinShare( MyType &rSrc ) + { + clear(); + + if( !rSrc.isFlat() ) + rSrc.flatten(); + + rSrc.initCount(); + pnRefs = rSrc.pnRefs; + (*pnRefs)++; + nLength = rSrc.nLength; + pFirst = rSrc.pFirst; + pLast = rSrc.pLast; + } + + void joinShare( const MyType &rSrc ) + { + clear(); + + rSrc.flatten(); + + if( !rSrc.isShared() ) + { + rSrc.pnRefs = new uint32_t; + (*rSrc.pnRefs) = 1; + } + pnRefs = rSrc.pnRefs; + (*pnRefs)++; + nLength = rSrc.nLength; + pFirst = rSrc.pFirst; + pLast = rSrc.pLast; + } + + /** + * This takes an object that was shared and makes a copy of the base data + * that was being shared so that this copy can be changed. This should be + * added before any call that will change this object; + */ + void unShare() const + { + if( isShared() == false ) + return; + + Chunk *pNew = newChunk( nLength ); + chr *pos = pNew->pData; + Chunk *i = pFirst; + for(;;) + { + cpy( pos, i->pData, i->nLength ); + pos += i->nLength; + i = i->pNext; + if( i == NULL ) + break; + } + decRefs(); + pLast = pFirst = pNew; + nLength = pNew->nLength; + } + + /** + * This decrements our ref count and pulls us out of the share. If the ref + * count hits zero because of this, it destroys the share. This is not + * safe to call on it's own, it's much better to call unShare. + */ + void decRefs() const + { + if( isShared() ) + { + (*pnRefs)--; + if( (*pnRefs) == 0 ) + destroyShare(); + else + { + pnRefs = NULL; + pFirst = NULL; + pLast = NULL; + nLength = 0; + } + } + } + + /** + * While the unShare function removes an instance from a share, this + * function destroys the data that was in the share, removing the share + * itself. This should only be called when the refcount for the share has + * or is about to reach zero. + */ + void destroyShare() const + { + delete pnRefs; + pnRefs = NULL; + realClear(); + } + +#ifdef VALTEST + void cpy( chr *dest, const chr *src, long count ) const + { + for( int j = 0; j < count; j++ ) + { + *dest = *src; + dest++; + src++; + } + } +#endif + + void initCount() const + { + if( !isShared() ) + { + pnRefs = new uint32_t; + (*pnRefs) = 1; + } + } + +private: + mutable long nLength; + mutable uint32_t *pnRefs; + mutable Chunk *pFirst; + mutable Chunk *pLast; + + mutable chralloc aChr; + mutable chunkalloc aChunk; +}; + +typedef FBasicString FString; + +#include "hash.h" +template<> uint32_t __calcHashCode( const FString &k ); +template<> bool __cmpHashKeys( const FString &a, const FString &b ); + + +#endif diff --git a/src/old/hash.cpp b/src/old/hash.cpp new file mode 100644 index 0000000..c52e6b1 --- /dev/null +++ b/src/old/hash.cpp @@ -0,0 +1,113 @@ +#include "hash.h" + +subExceptionDef( HashException ) + +template<> uint32_t __calcHashCode( const int &k ) +{ + return k; +} + +template<> bool __cmpHashKeys( const int &a, const int &b ) +{ + return a == b; +} + +template<> uint32_t __calcHashCode( const unsigned int &k ) +{ + return k; +} + +template<> bool __cmpHashKeys( const unsigned int &a, const unsigned int &b ) +{ + return a == b; +} + +template<> +uint32_t __calcHashCode( const char * const &k ) +{ + if (k == NULL) + { + return 0; + } + + unsigned long int nPos = 0; + for( const char *s = k; *s; s++ ) + { + nPos = *s + (nPos << 6) + (nPos << 16) - nPos; + } + + return nPos; +} + +template<> bool __cmpHashKeys( const char * const &a, const char * const &b ) +{ + if( a == b ) + return true; + + for(int j=0; a[j] == b[j]; j++ ) + if( a[j] == '\0' ) + return true; + + return false; +} + +template<> +uint32_t __calcHashCode( char * const &k ) +{ + if (k == NULL) + { + return 0; + } + + unsigned long int nPos = 0; + for( const char *s = k; *s; s++ ) + { + nPos = *s + (nPos << 6) + (nPos << 16) - nPos; + } + + return nPos; +} + +template<> bool __cmpHashKeys( char * const &a, char * const &b ) +{ + if( a == b ) + return true; + + for(int j=0; a[j] == b[j]; j++ ) + if( a[j] == '\0' ) + return true; + + return false; +} + +template<> uint32_t __calcHashCode( const std::string &k ) +{ + std::string::size_type j, sz = k.size(); + const char *s = k.c_str(); + + unsigned long int nPos = 0; + for( j = 0; j < sz; j++, s++ ) + { + nPos = *s + (nPos << 6) + (nPos << 16) - nPos; + } + + return nPos; +} + +template<> bool __cmpHashKeys( const std::string &a, const std::string &b ) +{ + return a == b; +} + +template<> uint32_t __calcHashCode( const Hashable &k ) +{ + return 0; + //return k.getHashCode(); +} + +template<> bool __cmpHashKeys( const Hashable &a, const Hashable &b ) +{ + return false; + //return a.compareForHash( b ); +} + diff --git a/src/old/hash.h b/src/old/hash.h new file mode 100644 index 0000000..e819379 --- /dev/null +++ b/src/old/hash.h @@ -0,0 +1,744 @@ +#ifndef HASH_H +#define HASH_H + +#include +#include +#include +#include +#include +#include "exceptionbase.h" +#include "hashable.h" +#include "serializable.h" +#include "serializer.h" + +#define bitsToBytes( n ) (n/32+(n%32>0 ? 1 : 0)) + +subExceptionDecl( HashException ) + +enum eHashException +{ + excodeNotFilled +}; + +template +uint32_t __calcHashCode( const T &k ); + +template +bool __cmpHashKeys( const T &a, const T &b ); + +struct __calcNextTSize_fast +{ + uint32_t operator()( uint32_t nCapacity, uint32_t nFill, uint32_t nDeleted ) const + { + if( nDeleted >= nCapacity/2 ) + return nCapacity; + return nCapacity*2+1; + } +}; + +template, typename valuealloc = std::allocator, typename challoc = std::allocator > +class Hash; + +template< typename key, typename _value, typename sizecalc = __calcNextTSize_fast, typename keyalloc = std::allocator, typename valuealloc = std::allocator<_value>, typename challoc = std::allocator > +struct HashProxy +{ + friend class Hash; +private: + HashProxy( Hash &h, key *k, uint32_t nPos, uint32_t hash ) : + hsh( h ), + pKey( k ), + nPos( nPos ), + hash( hash ), + bFilled( false ) + { + } + + HashProxy( Hash &h, uint32_t nPos, _value *pValue ) : + hsh( h ), + nPos( nPos ), + pValue( pValue ), + bFilled( true ) + { + } + + Hash &hsh; + key *pKey; + uint32_t nPos; + _value *pValue; + uint32_t hash; + bool bFilled; + +public: + operator _value &() + { + if( bFilled == false ) + throw HashException( + excodeNotFilled, + "No data assosiated with that key." + ); + return *pValue; + } + + _value &value() + { + if( bFilled == false ) + throw HashException( + excodeNotFilled, + "No data assosiated with that key." + ); + return *pValue; + } + + bool isFilled() + { + return bFilled; + } + + void erase() + { + if( bFilled ) + { + hsh._erase( nPos ); + hsh.onDelete(); + } + } + + _value operator=( _value nval ) + { + if( bFilled ) + { + hsh.va.destroy( pValue ); + hsh.va.construct( pValue, nval ); + hsh.onUpdate(); + } + else + { + hsh.fill( nPos, *pKey, nval, hash ); + hsh.onInsert(); + } + + return nval; + } + + _value *operator->() + { + if( bFilled == false ) + throw HashException( + excodeNotFilled, + "No data assosiated with that key." + ); + return pValue; + } +}; + +template +class Hash +{ + friend struct HashProxy; +public: + Hash() : + nCapacity( 11 ), + nFilled( 0 ), + nDeleted( 0 ), + bFilled( NULL ), + bDeleted( NULL ), + aKeys( NULL ), + aValues( NULL ), + aHashCodes( NULL ) + { + nKeysSize = bitsToBytes( nCapacity ); + bFilled = ca.allocate( nKeysSize ); + bDeleted = ca.allocate( nKeysSize ); + clearBits(); + + aHashCodes = ca.allocate( nCapacity ); + aKeys = ka.allocate( nCapacity ); + aValues = va.allocate( nCapacity ); + } + + Hash( const Hash &src ) : + nCapacity( src.nCapacity ), + nFilled( 0 ), + nDeleted( 0 ), + bFilled( NULL ), + bDeleted( NULL ), + aKeys( NULL ), + aValues( NULL ), + aHashCodes( NULL ) + { + nKeysSize = bitsToBytes( nCapacity ); + bFilled = ca.allocate( nKeysSize ); + bDeleted = ca.allocate( nKeysSize ); + clearBits(); + + aHashCodes = ca.allocate( nCapacity ); + aKeys = ka.allocate( nCapacity ); + aValues = va.allocate( nCapacity ); + + for( uint32_t j = 0; j < src.nCapacity; j++ ) + { + if( src.isFilled( j ) ) + { + insert( src.aKeys[j], src.aValues[j] ); + } + } + } + + Hash &operator=( const Hash &src ) + { + for( uint32_t j = 0; j < nCapacity; j++ ) + { + if( isFilled( j ) ) + if( !isDeleted( j ) ) + { + va.destroy( &aValues[j] ); + ka.destroy( &aKeys[j] ); + } + } + va.deallocate( aValues, nCapacity ); + ka.deallocate( aKeys, nCapacity ); + ca.deallocate( bFilled, nKeysSize ); + ca.deallocate( bDeleted, nKeysSize ); + ca.deallocate( aHashCodes, nCapacity ); + + nFilled = 0; + nDeleted = 0; + nCapacity = src.nCapacity; + nKeysSize = bitsToBytes( nCapacity ); + bFilled = ca.allocate( nKeysSize ); + bDeleted = ca.allocate( nKeysSize ); + clearBits(); + + aHashCodes = ca.allocate( nCapacity ); + aKeys = ka.allocate( nCapacity ); + aValues = va.allocate( nCapacity ); + + for( uint32_t j = 0; j < src.nCapacity; j++ ) + { + if( src.isFilled( j ) ) + { + insert( src.aKeys[j], src.aValues[j] ); + } + } + + return *this; + } + + virtual ~Hash() + { + for( uint32_t j = 0; j < nCapacity; j++ ) + { + if( isFilled( j ) ) + if( !isDeleted( j ) ) + { + va.destroy( &aValues[j] ); + ka.destroy( &aKeys[j] ); + } + } + va.deallocate( aValues, nCapacity ); + ka.deallocate( aKeys, nCapacity ); + ca.deallocate( bFilled, nKeysSize ); + ca.deallocate( bDeleted, nKeysSize ); + ca.deallocate( aHashCodes, nCapacity ); + } + + uint32_t getCapacity() + { + return nCapacity; + } + + uint32_t getFill() + { + return nFilled; + } + + uint32_t size() + { + return nFilled-nDeleted; + } + + uint32_t getDeleted() + { + return nDeleted; + } + + virtual HashProxy operator[]( key k ) + { + uint32_t hash = __calcHashCode( k ); + bool bFill; + uint32_t nPos = probe( hash, k, bFill ); + + if( bFill ) + { + return HashProxy( *this, nPos, &aValues[nPos] ); + } + else + { + return HashProxy( *this, &k, nPos, hash ); + } + } + + virtual void insert( key k, value v ) + { + uint32_t hash = __calcHashCode( k ); + bool bFill; + uint32_t nPos = probe( hash, k, bFill ); + + if( bFill ) + { + va.destroy( &aValues[nPos] ); + va.construct( &aValues[nPos], v ); + onUpdate(); + } + else + { + fill( nPos, k, v, hash ); + onInsert(); + } + } + + virtual void erase( key k ) + { + uint32_t hash = __calcHashCode( k ); + bool bFill; + uint32_t nPos = probe( hash, k, bFill ); + + if( bFill ) + { + _erase( nPos ); + onDelete(); + } + } + + struct iterator; + virtual void erase( struct iterator &i ) + { + if( this != &i.hsh ) + throw HashException("This iterator didn't come from this Hash."); + if( isFilled( i.nPos ) && !isDeleted( i.nPos ) ) + { + _erase( i.nPos ); + onDelete(); + } + } + + virtual void clear() + { + for( uint32_t j = 0; j < nCapacity; j++ ) + { + if( isFilled( j ) ) + if( !isDeleted( j ) ) + { + va.destroy( &aValues[j] ); + ka.destroy( &aKeys[j] ); + onDelete(); + } + } + + clearBits(); + } + + virtual value &get( key k ) + { + uint32_t hash = __calcHashCode( k ); + bool bFill; + uint32_t nPos = probe( hash, k, bFill ); + + if( bFill ) + { + return aValues[nPos]; + } + else + { + throw HashException( + excodeNotFilled, + "No data assosiated with that key." + ); + } + } + + virtual bool has( key k ) + { + bool bFill; + probe( __calcHashCode( k ), k, bFill, false ); + + return bFill; + } + + typedef struct iterator + { + friend class Hash; + private: + iterator( Hash &hsh ) : + hsh( hsh ), + nPos( 0 ), + bFinished( false ) + { + nPos = hsh.getFirstPos( bFinished ); + } + + iterator( Hash &hsh, bool bDone ) : + hsh( hsh ), + nPos( 0 ), + bFinished( bDone ) + { + } + + Hash &hsh; + uint32_t nPos; + bool bFinished; + + public: + iterator operator++( int ) + { + if( bFinished == false ) + nPos = hsh.getNextPos( nPos, bFinished ); + + return *this; + } + + iterator operator++() + { + if( bFinished == false ) + nPos = hsh.getNextPos( nPos, bFinished ); + + return *this; + } + + bool operator==( const iterator &oth ) + { + if( bFinished != oth.bFinished ) + return false; + if( bFinished == true ) + { + return true; + } + else + { + if( oth.nPos == nPos ) + return true; + return false; + } + } + + bool operator!=( const iterator &oth ) + { + return !(*this == oth ); + } + + iterator operator=( const iterator &oth ) + { + if( &hsh != &oth.hsh ) + throw HashException( + "Cannot mix iterators from different hash objects."); + nPos = oth.nPos; + bFinished = oth.bFinished; + } + + std::pair operator *() + { + return hsh.getAtPos( nPos ); + } + + key &getKey() + { + return hsh.getKeyAtPos( nPos ); + } + + value &getValue() + { + return hsh.getValueAtPos( nPos ); + } + }; + + iterator begin() + { + return iterator( *this ); + } + + iterator end() + { + return iterator( *this, true ); + } + + std::list getKeys() + { + std::list lKeys; + + for( uint32_t j = 0; j < nCapacity; j++ ) + { + if( isFilled( j ) ) + { + if( !isDeleted( j ) ) + { + lKeys.push_back( aKeys[j] ); + } + } + } + + return lKeys; + } + +protected: + virtual void onInsert() {} + virtual void onUpdate() {} + virtual void onDelete() {} + virtual void onReHash() {} + + virtual void clearBits() + { + for( uint32_t j = 0; j < nKeysSize; j++ ) + { + bFilled[j] = bDeleted[j] = 0; + } + } + + virtual void fill( uint32_t loc, key &k, value &v, uint32_t hash ) + { + bFilled[loc/32] |= (1<<(loc%32)); + va.construct( &aValues[loc], v ); + ka.construct( &aKeys[loc], k ); + aHashCodes[loc] = hash; + nFilled++; + //printf("Filled: %d, Deleted: %d, Capacity: %d\n", + // nFilled, nDeleted, nCapacity ); + } + + virtual void _erase( uint32_t loc ) + { + bDeleted[loc/32] |= (1<<(loc%32)); + va.destroy( &aValues[loc] ); + ka.destroy( &aKeys[loc] ); + nDeleted++; + //printf("Filled: %d, Deleted: %d, Capacity: %d\n", + // nFilled, nDeleted, nCapacity ); + } + + virtual std::pair getAtPos( uint32_t nPos ) + { + return std::pair(aKeys[nPos],aValues[nPos]); + } + + virtual key &getKeyAtPos( uint32_t nPos ) + { + return aKeys[nPos]; + } + + virtual value &getValueAtPos( uint32_t nPos ) + { + return aValues[nPos]; + } + + virtual uint32_t getFirstPos( bool &bFinished ) + { + for( uint32_t j = 0; j < nCapacity; j++ ) + { + if( isFilled( j ) ) + if( !isDeleted( j ) ) + return j; + } + + bFinished = true; + return 0; + } + + virtual uint32_t getNextPos( uint32_t nPos, bool &bFinished ) + { + for( uint32_t j = nPos+1; j < nCapacity; j++ ) + { + if( isFilled( j ) ) + if( !isDeleted( j ) ) + return j; + } + + bFinished = true; + return 0; + } + + uint32_t probe( uint32_t hash, key k, bool &bFill, bool rehash=true ) + { + uint32_t nCur = hash%nCapacity; + + // First we scan to see if the key is already there, abort if we + // run out of probing room, or we find a non-filled entry + for( int8_t j = 0; + isFilled( nCur ) && j < 32; + nCur = (nCur + (1< uint32_t __calcHashCode( const int &k ); +template<> bool __cmpHashKeys( const int &a, const int &b ); + +template<> uint32_t __calcHashCode( const unsigned int &k ); +template<> bool __cmpHashKeys( const unsigned int &a, const unsigned int &b ); + +template<> uint32_t __calcHashCode( const char * const &k ); +template<> bool __cmpHashKeys( const char * const &a, const char * const &b ); + +template<> uint32_t __calcHashCode( char * const &k ); +template<> bool __cmpHashKeys( char * const &a, char * const &b ); + +template<> uint32_t __calcHashCode( const std::string &k ); +template<> bool __cmpHashKeys( const std::string &a, const std::string &b ); + +template<> uint32_t __calcHashCode( const Hashable &k ); +template<> bool __cmpHashKeys( const Hashable &a, const Hashable &b ); + +template +Serializer &operator<<( Serializer &ar, Hash &h ) +{ + ar << h.size(); + for( typename Hash::iterator i = h.begin(); i != h.end(); i++ ) + { + std::pair p = *i; + ar << p.first << p.second; + } + + return ar; +} + +template +Serializer &operator>>( Serializer &ar, Hash &h ) +{ + h.clear(); + uint32_t nSize; + ar >> nSize; + + for( uint32_t j = 0; j < nSize; j++ ) + { + key k; value v; + ar >> k >> v; + h.insert( k, v ); + } + + return ar; +} + +template +Serializer &operator&&( Serializer &ar, Hash &h ) +{ + if( ar.isLoading() ) + { + return ar >> h; + } + else + { + return ar << h; + } +} + +#endif diff --git a/src/old/hashable.cpp b/src/old/hashable.cpp new file mode 100644 index 0000000..8565956 --- /dev/null +++ b/src/old/hashable.cpp @@ -0,0 +1 @@ +#include "hashable.h" diff --git a/src/old/hashable.h b/src/old/hashable.h new file mode 100644 index 0000000..98643d5 --- /dev/null +++ b/src/old/hashable.h @@ -0,0 +1,12 @@ +#ifndef HASHABLE_H +#define HASHABLE_H + +class Hashable +{ +public: + virtual ~Hashable() {}; + virtual unsigned long int getHashCode() = 0; + virtual bool compareForHash( Hashable &other ) = 0; +}; + +#endif diff --git a/src/old/hashfunction.cpp b/src/old/hashfunction.cpp new file mode 100644 index 0000000..51f2259 --- /dev/null +++ b/src/old/hashfunction.cpp @@ -0,0 +1,10 @@ +#include "hashfunction.h" + +HashFunction::HashFunction() +{ +} + +HashFunction::~HashFunction() +{ +} + diff --git a/src/old/hashfunction.h b/src/old/hashfunction.h new file mode 100644 index 0000000..cbcf70f --- /dev/null +++ b/src/old/hashfunction.h @@ -0,0 +1,48 @@ +#ifndef HASH_FUNCTION +#define HASH_FUNCTION + +/** This represents the shell of a hash function. It must be aggregated in + * order to be used. Please read about it's two functions for specificatins + * relating to what values will be passed to them and what they should return + * for creating your own hash functions. + *@author Mike Buland. + */ +class HashFunction +{ +public: + /** + * Standard Constructor. + */ + HashFunction(); + + /** + * Standard Deconstructor. + */ + virtual ~HashFunction(); + + /** Hashes the value represnted by id. This must return a fairly unique + * number in the range of 0-2^32 (or whatever the size of an unsigned long + * is on your system) based on the id given. The faster the number changes + * the better in a general sence. The return value will be the index + * (after probing takes place) to the data assosiated with an id, so this + * function should always produce the same number for any given id. + *@param id The identifier to use to create a unique numerical identifier. + *@returns A mostly unique numerical identifier generated using the given + * id. + */ + virtual unsigned long int hash( const void *id ) = 0; + + /** This function must compare two ids in the format that this hashfunction + * accepts. For example, if the hash function hashes strings it should + * probably { return strcmp( id1, id2 ) == 0 }. + *@param id1 One value to use in the comparison + *@param id2 Another value to use in the comparison + *@returns True if the two values match, otherwise false. + */ + virtual bool cmpIDs( const void *id1, const void *id2 ) = 0; + +// virtual void *createPersistantID( const void *id ) = 0; +// virtual void destroyPersistantID( const void *id ) = 0; +}; + +#endif diff --git a/src/old/hashfunctioncasestring.cpp b/src/old/hashfunctioncasestring.cpp new file mode 100644 index 0000000..6361f45 --- /dev/null +++ b/src/old/hashfunctioncasestring.cpp @@ -0,0 +1,39 @@ +#include +#include +#include +#include "hashfunctioncasestring.h" + +HashFunctionCaseString::HashFunctionCaseString() +{ +} + +HashFunctionCaseString::~HashFunctionCaseString() +{ +} + +unsigned long int HashFunctionCaseString::hash( const void *id ) +{ + const char *str = (const char *)id; + unsigned long int nPos = 0; + for( int j = 0; str[j] != '\0'; j++ ) + { + nPos = tolower(str[j]) + (nPos << 6) + (nPos << 16) - nPos; +// nPos += nPos<<16|(((unsigned long int)tolower(str[j]))<<((j*7)%24)); + } + return nPos; +} + +bool HashFunctionCaseString::cmpIDs( const void *id1, const void *id2 ) +{ + const char *str1 = (const char *)id1; + const char *str2 = (const char *)id2; + + int j; + for( j = 0; str1[j] != '\0' && str2[j] != '\0'; j++ ) + { + if( tolower(str1[j]) != tolower(str2[j]) ) + return false; + } + return (str1[j]==str2[j]); +} + diff --git a/src/old/hashfunctioncasestring.h b/src/old/hashfunctioncasestring.h new file mode 100644 index 0000000..7816a1b --- /dev/null +++ b/src/old/hashfunctioncasestring.h @@ -0,0 +1,28 @@ +#ifndef HASH_FUNCTION_CASE_STRING +#define HASH_FUNCTION_CASE_STRING + +#include "hashfunction.h" + +/** A hash function for string data. This hash function does strings, but is + * actually generalized to handle any binary stream of characters terminated + * by a null character. This is different than HashFunctionString in that + * this does comparisons without regaurd to case. + *@author Mike Buland. + */ +class HashFunctionCaseString : public HashFunction +{ +public: + /** + * Standard Constructor. + */ + HashFunctionCaseString(); + + /** + * Standard Deconstructor. + */ + virtual ~HashFunctionCaseString(); + unsigned long int hash( const void *id ); + bool cmpIDs( const void *id1, const void *id2 ); +}; + +#endif diff --git a/src/old/hashfunctionint.cpp b/src/old/hashfunctionint.cpp new file mode 100644 index 0000000..4bd0feb --- /dev/null +++ b/src/old/hashfunctionint.cpp @@ -0,0 +1,20 @@ +#include "hashfunctionint.h" + +HashFunctionInt::HashFunctionInt() +{ +} + +HashFunctionInt::~HashFunctionInt() +{ +} + +unsigned long int HashFunctionInt::hash( const void *id ) +{ + return (unsigned long)(id); +} + +bool HashFunctionInt::cmpIDs( const void *id1, const void *id2 ) +{ + return (unsigned long)(id1) == (unsigned long)(id2); +} + diff --git a/src/old/hashfunctionint.h b/src/old/hashfunctionint.h new file mode 100644 index 0000000..0fbc764 --- /dev/null +++ b/src/old/hashfunctionint.h @@ -0,0 +1,26 @@ +#ifndef HASH_FUNCTION_INT +#define HASH_FUNCTION_INT + +#include "hashfunction.h" + +/** A hash function for integer data. Really, this does almost nothing except + * ensure we're dealing with positive indicies. + *@author Mike Buland. + */ +class HashFunctionInt : public HashFunction +{ +public: + /** + * Standard Constructor. + */ + HashFunctionInt(); + + /** + * Standard Deconstructor. + */ + virtual ~HashFunctionInt(); + unsigned long int hash( const void *id ); + bool cmpIDs( const void *id1, const void *id2 ); +}; + +#endif diff --git a/src/old/hashfunctionstring.cpp b/src/old/hashfunctionstring.cpp new file mode 100644 index 0000000..bd14643 --- /dev/null +++ b/src/old/hashfunctionstring.cpp @@ -0,0 +1,51 @@ +#include "hashfunctionstring.h" +#ifndef NULL +#define NULL ((void *) 0) +#endif + +HashFunctionString::HashFunctionString() +{ +} + +HashFunctionString::~HashFunctionString() +{ +} + +unsigned long int HashFunctionString::hash( const void *id ) +{ + if (id == NULL) + { + return 0; + } + + unsigned long int nPos = 0; + for( const char *s = (const char *)id; *s; s++ ) + { + nPos = *s + (nPos << 6) + (nPos << 16) - nPos; + } + return nPos; +} + +bool HashFunctionString::cmpIDs( const void *id1, const void *id2 ) +{ + if (id1 == NULL || id2 == NULL) + { + return false; + } + if (id1 == id2) + { + return true; + } + + const char *str1 = (const char *)id1; + const char *str2 = (const char *)id2; + + int j; + for( j = 0; str1[j] != '\0' && str2[j] != '\0'; j++ ) + { + if( str1[j] != str2[j] ) + return false; + } + return (str1[j]==str2[j]); +} + diff --git a/src/old/hashfunctionstring.h b/src/old/hashfunctionstring.h new file mode 100644 index 0000000..7d2a1a6 --- /dev/null +++ b/src/old/hashfunctionstring.h @@ -0,0 +1,27 @@ +#ifndef HASH_FUNCTION_STRING +#define HASH_FUNCTION_STRING + +#include "hashfunction.h" + +/** A hash function for string data. This hash function does strings, but is + * actually generalized to handle any binary stream of characters terminated + * by a null character. + *@author Mike Buland. + */ +class HashFunctionString : public HashFunction +{ +public: + /** + * Standard Constructor. + */ + HashFunctionString(); + + /** + * Standard Deconstructor. + */ + virtual ~HashFunctionString(); + unsigned long int hash( const void *id ); + bool cmpIDs( const void *id1, const void *id2 ); +}; + +#endif diff --git a/src/old/hashtable.cpp b/src/old/hashtable.cpp new file mode 100644 index 0000000..dbcd964 --- /dev/null +++ b/src/old/hashtable.cpp @@ -0,0 +1,424 @@ +#include +#include +#include + +#include "hashtable.h" + +HashTable::HashTable( HashFunction *hNewFunc, unsigned long int nInitSize, bool bAllowDupes ) +{ + hFunc = hNewFunc; + nTableSize = nextPrime( nInitSize ); + aTable = new HashNode[nTableSize]; + //for( int j = 0; j < nTableSize; j++ ) if( aTable[j].id || aTable[j].data || aTable[j].bDeleted ) printf("Unclean entry\n"); + nSize = 0; + nFilled = 0; + this->bAllowDupes = bAllowDupes; +} + +HashTable::~HashTable() +{ + delete[] aTable; + delete hFunc; +} + +void HashTable::set( int j, const void *newID, const void *newData ) +{ + if( newData == NULL ) + { + printf("Inserting NULL data is indestinguishable from uninserted data!\n"); + } + aTable[j].id = newID; + aTable[j].data = newData; +} + +void HashTable::clear() +{ + memset( aTable, 0, sizeof(HashNode) * nTableSize ); +} + +bool HashTable::isFilled( int j ) +{ + return (aTable[j].id != NULL)||(aTable[j].bDeleted); +} + +void HashTable::reHash( unsigned long int nNewSize ) +{ + HashNode *aOldTable = aTable; + unsigned long int oldSize = nTableSize; + + // If the table can still be used if we just get rid of deleted items, don't + // change the size of the table, otherwise, go ahead and use the number + // passed in. + if( nSize > nTableSize>>1 ) + { + nTableSize = nextPrime( nNewSize ); + } + + aTable = newTable( nTableSize ); + //for( int j = 0; j < nTableSize; j++ ) if( aTable[j].id || aTable[j].data || aTable[j].bDeleted ) printf("Unclean entry\n"); + + nSize = 0; + nFilled = 0; + + for( unsigned long int j = 0; j < oldSize; j++ ) + { + if( aOldTable[j].id != NULL && aOldTable[j].bDeleted == false ) + { + insert( aOldTable[j].id, aOldTable[j].data ); + } + } + + delete[] aOldTable; +} + +unsigned long int HashTable::probe( unsigned long int nStart, const void *id ) +{ + int nHash = nStart; + nStart = nStart%nTableSize; + if( bAllowDupes == true ) + { + for( + unsigned long int j=0; + isFilled( nStart ) && j < 32; + nStart = (nStart+(1<cmpIDs( aTable[nStart].id, id ) == true && + aTable[nStart].bDeleted == false ) + { + return nStart; + } + } + } + } + // This is our insurance, if the table is full, then go ahead and rehash, + // then try again. + if( isFilled( nStart ) ) + { + reHash( getCapacity()*2 ); + return probe( nHash, id ); + } + return nStart; +} + +HashTable::HashNode *HashTable::newTable( unsigned long int nNewSize ) +{ + return new HashNode[nNewSize]; +} + +#ifdef HASH_DEBUG_VIS +void HashTable::printDebugLine( const char *exData ) +{ + char *buf = new char[getCapacity()+3]; + int j; + buf[0] = '['; + for( j = 0; j < getCapacity(); j++ ) + { + buf[j+1] = (aTable[j].bDeleted)?('X'):((isFilled( j ))?('#'):('-')); + } + buf[j+1] = ']'; + buf[j+2] = '\0'; + printf("%s %s\n", buf, exData ); + delete[] buf; +} +#endif + +bool HashTable::insert( const void *id, const void *data ) +{ + unsigned long int nPos = probe( hFunc->hash( id ), id )%nTableSize; + + if( bAllowDupes == true ) + { + if( aTable[nPos].id == NULL && aTable[nPos].bDeleted == false ) + { + set( nPos, id, data ); +#ifdef HASH_DEBUG_VIS + printDebugLine( (const char *)id ); +#endif + nSize++; + nFilled++; + return true; + } + else + { + return false; + } + } + else + { + if( aTable[nPos].id == NULL && aTable[nPos].bDeleted == false ) + { + set( nPos, id, data ); +#ifdef HASH_DEBUG_VIS + printDebugLine( (const char *)id ); +#endif + nSize++; + nFilled++; + return true; + } + else if( hFunc->cmpIDs( aTable[nPos].id, id ) == true ) + { + set( nPos, id, data ); +#ifdef HASH_DEBUG_VIS + printDebugLine( (const char *)id ); +#endif + return true; + } + else + { + return false; + } + } +} + +const void *HashTable::get( const void *id, unsigned long int nSkip ) +{ + unsigned long int nPos = hFunc->hash( id )%nTableSize; + + for( unsigned long int j=0; j < 32; nPos = (nPos+(1<cmpIDs( id, aTable[nPos].id ) ) + { + if( nSkip == 0 ) + { + return aTable[nPos].data; + } + else + { + nSkip--; + } + } + } + } + + if( bAllowDupes ) + { + unsigned long int nOldPos = nPos; + for( nPos++; nPos != nOldPos; nPos=(nPos+1)%nTableSize ) + { + if( !isFilled( nPos ) ) return NULL; + if( aTable[nPos].bDeleted == false ) + { + if( hFunc->cmpIDs( id, aTable[nPos].id ) ) + { + if( nSkip == 0 ) + { + return aTable[nPos].data; + } + else + { + nSkip--; + } + } + } + } + } + + return NULL; +} + +const void *HashTable::getKey( const void *id, unsigned long int nSkip ) +{ + unsigned long int nPos = hFunc->hash( id )%nTableSize; + + for( unsigned long int j=0; j < 32; nPos = (nPos+(1<cmpIDs( id, aTable[nPos].id ) ) + { + if( nSkip == 0 ) + { + return aTable[nPos].id; + } + else + { + nSkip--; + } + } + } + } + + if( bAllowDupes ) + { + unsigned long int nOldPos = nPos; + for( nPos++; nPos != nOldPos; nPos=(nPos+1)%nTableSize ) + { + if( !isFilled( nPos ) ) return NULL; + if( aTable[nPos].bDeleted == false ) + { + if( hFunc->cmpIDs( id, aTable[nPos].id ) ) + { + if( nSkip == 0 ) + { + return aTable[nPos].id; + } + else + { + nSkip--; + } + } + } + } + } + + return NULL; +} + +void *HashTable::getFirstItemPos() +{ + HashPos *pos = new HashPos; + return pos; +} + +const void *HashTable::getItemData( void *xPos ) +{ + return aTable[((HashPos *)xPos)->nPos].data; +} + +const void *HashTable::getItemID( void *xPos ) +{ + return aTable[((HashPos *)xPos)->nPos].id; +} + +void *HashTable::getNextItemPos( void *xPos ) +{ + HashPos *pos = (HashPos *)xPos; + if( pos->bStarted == false ) + { + pos->bStarted = true; + pos->nPos = 0; + } + else + { + pos->nPos++; + } + if( pos->nPos < nTableSize ) + { + for( ; pos->nPos < nTableSize; pos->nPos++ ) + { + if( isFilled( pos->nPos ) && + aTable[pos->nPos].bDeleted == false ) + { + return xPos; + } + } + } + + delete pos; + + return NULL; +} + +// Big-O sqrt(n) +// Change this to be erethpothynies table with a storage +// lookup later on. +bool HashTable::isPrime (int num) +{ + if (num == 2) // the only even prime + return true; + else if (num % 2 == 0) // other even numbers are composite + return false; + else + { + //bool prime = true; + int divisor = 3; + int upperLimit = static_cast(sqrt(num) + 1); + while (divisor <= upperLimit) + { + if (num % divisor == 0) + return false; + // prime = false; + divisor +=2; + } + return true; + } +} + +// Big-O n^(3/2) +int HashTable::nextPrime( int base ) +{ + int nPrime; + for( nPrime = base; isPrime( nPrime ) == false; nPrime++ ); + return nPrime; +} + +unsigned long int HashTable::getCapacity() +{ + return nTableSize; +} + +unsigned long int HashTable::getSize() +{ + return nSize; +} + +double HashTable::getLoad() +{ + return (double)(nFilled)/(double)(nTableSize); +} + +const void *HashTable::operator[](const void *id) +{ + return get( id ); +} + +bool HashTable::del( const void *id, int nSkip ) +{ + unsigned long int nPos = hFunc->hash( id )%nTableSize; + + for( unsigned long int j=0; j < 32; nPos = (nPos+(1<cmpIDs( id, aTable[nPos].id ) && + aTable[nPos].bDeleted == false ) + { + if( nSkip == 0 ) + { + aTable[nPos].bDeleted = true; + nSize--; +#ifdef HASH_DEBUG_VIS + printDebugLine( (const char *)id ); +#endif + return true; + } + else + { + nSkip--; + } + } + } + + return false; +} + diff --git a/src/old/hashtable.h b/src/old/hashtable.h new file mode 100644 index 0000000..179b694 --- /dev/null +++ b/src/old/hashtable.h @@ -0,0 +1,308 @@ +/**\hashtable.h + * Describes the HashFunction, HashFunctionString, and HashTable classes. It + * was just easier to put them all in one set of files. + *@author Mike Buland + */ + +#ifndef HASH_TABLE_H +#define HASH_TABLE_H + +//Uncomment this line to see a cool text-mode visualization of what's going on +//#define HASH_DEBUG_VIS 1 + +#include +#include +#include + +#include "hashfunction.h" + +/** + * A simple yet flexable hash-table. This uses several tricks to help ensure + * that the table is always running at maximum efficiency. You no longer have + * to specify a "danger fill level" when more space is needed a rehash is + * automatically trigered. Deleting elements is fully supported, as well as + * duplicate elements. To work with and allow duplicates simple construct your + * HashTable the way you normally would, but when deleting or getting elements + * you can specify a skip value. This effectively allows you to treat elements + * with duplicate ID's as though they were in a zero-based array. The first + * element inserted with a given ID would be at skip zero, the next at skip 1 + * and so on. This allows you to quickly search for elements with duplicate + * names, just stop when you get a null for a skip number, i.e. + *
+ *   for( int j = 0;; j++ )
+ *   {
+ *       void *pData = hash.get( myID, j );
+ *       if( !pData ) break;
+ *       // Do something interesting with pData
+ *   }
+ * 
+ * There are new features in this HashTable that also allow for memory saving + * when dealing with systems where many elements are being deleted from the + * table. In those cases the elements deleted cannot be simply deleted, instead + * they have to be marked as deleted and hidden from the user, but maintained in + * the table so that future hashing operations don't fail. When rehashing + * occurs all elements marked as deleted are quietly removed. In these cases, + * if the number of deleted items would free enough space in the table for the + * table to be used efficiently without resizing, it is left the same size and + * rehashing is performed effectively in place, allowing the deleted items to + * be removed. + *
+ * For info on adding new hashing algorithms, please see the HashFunction class. + *@author Mike Buland + *@todo Fix probing for tables that allow duplicates, and delete an item, then + * insert an item with the same name. + */ +class HashTable +{ +public: + /** Constructs a hash table. + *@param hNewFunc A pointer to a hashfunction class to use. If this is + * null the default general string type will be used. + *@param nInitSize The initial size of the hashtable. + *@param bAllowDupes Setting this value to true allows the system to + * insert more than one copy of any given key. This can be tricky, and + * will require you to use the nSkip parameter on the get function. + */ + HashTable( HashFunction *hNewFunc, unsigned long int nInitSize, bool bAllowDupes=false ); + + /** + * Destroys the hashtable, cleaning up all internal storage, but not stored + * elements. Also deletes the HashFunction passed in in the constructor. + */ + virtual ~HashTable(); + + /** Inserts an item into the hashtable. This function will trigger a + * rehash if adding another item would force the table's load factor over + * the danger level. + *@param id used to find the data later. + *@param data The data item to insert into the table with the identifier + * id + *@returns True if insertion was successfull, and false if it failed. + */ + bool insert( const void *id, const void *data ); + + /** Gets an item in the hashtable based on the id of that item. If there + * is more than one item with the same id you can use the nSkip parameter + * to access all of them. + *@param id The id of the item you're trying to find. + *@param nSkip The number of items with that id to skip before returning + * with the requested item. + *@returns A pointer to the data stored at the given id. + */ + const void *get( const void *id, unsigned long int nSkip=0 ); + + const void *getKey( const void *id, unsigned long int nSkip=0 ); + + /** Gets the total capacity of the hashtable. This is actually the number + * of total positions available inside the hashtable at the moment. This + * will change when the hashtable's load exceeds the danger level. + * Please note that this is NOT the actual amount of space available. + * In reality you can only access about 45-50 percent of that space. + *@returns The total capacity. + */ + unsigned long int getCapacity(); + + /** Gets the number of filled in items in the hash table. This is roughly + * equivelent to the getSize function assosiated with the Lists. + *@returns The number of filled in items in the hash table. + */ + unsigned long int getSize(); + + /** Gets the load (percentage) of filled in items in the table. This is + * technically the size divided by the capacity, but is definately usefull + * since it's required to check if it's time to rehash. + *@returns The table load in the range 0.0 to 1.0 + */ + double getLoad(); + + /** Sets up an xPos object for use indexing the items in the table. Call + * this first and follow the directions for getNextItemPos below to + * iterate through every item in the table, while avoiding the empty + * spaces. + *@returns A pointer to a xPos object telling the hashtable where to find + * the item you're looking at. + */ + void *getFirstItemPos(); + + /** Get the item's data that is being pointed to by xPos. This is only + * valid after xPos was created using getFirstItemPos and getNextItemPos + * was called at least once. + *@param xPos supplied by getFirstItemPos. + *@returns The key value that was used to insert the data into the table. + */ + const void *getItemData( void *xPos ); + + /** Get the item's ID that is being pointed to by xPos. This is only + * valid after xPos was created using getFirstItemPos and getNextItemPos + * was called at least once. + *@param xPos supplied by getFirstItemPos. + *@returns The key value that was used to insert the data into the table. + */ + const void *getItemID( void *xPos ); + + /** Used for iterating through a hash table sequentially. This will + * update the xPos pointer to point to the next time, all ready to + * be accessed with getItemID and getItemData. This must be called at + * least once before xPos is meaningful, and will return a NULL when it + * has reached the last item. + *@param xPos This must be an object created by a call to the function + * getFirstItemPos, and is only meaningful to the internal routines. + * Aborting a call in the middle (not running to the end of the table) + * may result in a memory leak at the moment. + *@returns xPos if still iterating through the list, otherwise it will + * return NULL when the end has been reached and the xPos variable has + * been deleted. + */ + void *getNextItemPos( void *xPos ); + + /** A helpful operator to make accessing items easier. Please note that + * this simply returns a pointer to the data stored internally, and cannot + * be used like the STL operator to store new data, use insert for that. + *@param id The identifier used to store the requested item. + *@returns The data value assosiated with the given id, or NULL if it + * wasn't found in the table. + */ + const void *operator[](const void *id); + + /** + * Delete the specified item from the hashtable. This actually keeps the + * data and marks it deleted. For all intents and purposes to the user it + * is deleted, except that the space is still used until a rehash is forced. + * This means that in hashtables where elements are being inserted and + * deleted frequently you may run into a higher rate of expansion. + *@param id The ID to delete. + *@param nSkip The number of similar id's to skip before deleting in a + * hashtable that allows duplicates. + *@returns True if the element was found and deleted, false otherwise. + */ + bool del( const void *id, int nSkip=0 ); + + /** + * Deletes every entry in the hash table. See the notes on del to see what + * this means, except that since no data is being kept, the entire table is + * just marked as usable space. + */ + void clear(); + +private: + /** + * Contains info related to a position in the hashtable. Used for + * searching through hashtables one item at a time, in order. This class + * should never be created by anything but a HashTable, and should never + * be referenced directly. Instead the hashtable returns a void pointer, + * which is what should be passed back in next time you use a search + * function. Always finish a search, since the object is deleted at the + * end of the search. + *@author Mike Buland + */ + class HashPos + { + public: + /** Create a blank HashPos. */ + HashPos() { bStarted=false; nPos = 0; }; + /** Has the search been started? */ + bool bStarted; + /** The position (index) into the backend storage structure. */ + unsigned long int nPos; + }; + + /** + * All data related to a single element in the hashtable. This should + * really only be used and manipulated by the HashTable itself. + *@author Mike Buland + */ + typedef struct HashNode + { + public: + /** Create a new, empty HashNode. */ + HashNode() { id = NULL; data = NULL; bDeleted = false; }; + /** A pointer to the original ID that was used to key the data. */ + const void *id; + /** A pointer to the data stored along with the above ID. */ + const void *data; + /** Weather or not this data should really...exist */ + bool bDeleted; + } HashNode; + +private: + /** + * Just sets the values in the element to some friendly values. + *@param newID The new ID to store. + *@param newData The new Data to store. + */ + void set( int j, const void *newID, const void *newData ); + /** + * Tells you if the node is filled or not. + *@returns True=an ID has been stored here, False=no ID. + */ + bool isFilled( int j ); + /** + * This actually resizes, but since every resize requires a reHash to go + * along with it, that's the name. This actually creates a new buffer for + * all of the contained data and then pulls every old element that was in + * the old table out and performs the hashing placement calculations again. + * This function skips all data that was marked as deleted, so at this + * point it really will be. + *@param nNewSize The new size to set the table to while re-hashing. + *@returns True if the operation was successful, false otherwise. + */ + void reHash( unsigned long int nNewSize ); + + /** + * Helper function to allocate a new table. Really just does the memory + * allocation. + *@param nNewSize The size of the table to generate. + *@returns A new, blank array of HashNode objects the size you specified. + */ + HashNode *newTable( unsigned long int nNewSize ); + + /** + * This function is used once an actual hash code is obtained. nStart is + * the given hash code, which is then wrapped to the size of the table. If + * there is data at that location, tests are performed to see if it's the + * right one. If it is, then it is returned, otherwise a series of further + * tests based on a 2^n search pattern is performed. The position of the + * requested data in the back-end storage is returned if found, otherwise + * another less useful value is returned... + *@param nStart The initial hashcode of the ID testing for. + *@param id A pointer to the id that is being searched for. + *@returns The real location of the data requested. + */ + unsigned long int probe( unsigned long int nStart, const void *id ); + + /** + * Simple helper function to determine if a number is prime or not. + * This function runs in sqrt(n) time. + *@param num Number to test for prime-hood. + *@returns True if the number is prime, false otherwise. + */ + bool isPrime( int num ); + + /** + * Given any number, this function finds the first number after it that is + * prime. Since this number is a multiple internally it's rare that the + * starting number would be prime. + *@param base The number to start the prime search on. + *@returns The first prime after the number given. + */ + int nextPrime( int base ); + +#ifdef HASH_DEBUG_VIS + void printDebugLine( const char *exData ); +#endif + + /** A pointer to the HashFunction subclass instance to use. */ + HashFunction *hFunc; + /** The complete array of HashNode objects to store data in. */ + HashNode *aTable; + /** The actual size of the table, not how many elements are in it. */ + unsigned long int nTableSize; + /** The number of elements that are in the table. */ + unsigned long int nSize; + /** The number of elements that are unavailable now. */ + unsigned long int nFilled; + /** Allow duplicate ID's in the table. */ + bool bAllowDupes; +}; + +#endif diff --git a/src/old/http.cpp b/src/old/http.cpp new file mode 100644 index 0000000..df7dafe --- /dev/null +++ b/src/old/http.cpp @@ -0,0 +1,377 @@ +#include +#include +#include "http.h" +#include "hashfunctionstring.h" + +Http::Http( Connection *pConnection ) : hReqHeader( new HashFunctionString(), 100 ) +{ + pCon = pConnection; + nParseState = parseInit; +} + +Http::~Http() +{ + for( int j = 0; j < lStrings.getSize(); j++ ) + { + delete (std::string *)lStrings[j]; + } +} + +bool Http::parseRequest() +{ + for(;;) + { + pCon->readInput(); + switch( nParseState ) + { + case parseInit: + { + int nLen = pCon->scanInputFor( CR ); + if( nLen == -1 ) + { + return false; + } + else + { + nReqType = getRequestType( pCon->getInput() ); + pCon->usedInput( pCon->scanInputFor(' ')+1 ); + + nLen = pCon->scanInputFor(' '); + sReqURI.append( pCon->getInput(), nLen ); + pCon->usedInput( nLen+1 ); + + if( !strncmp( pCon->getInput(), "HTTP/", 5 ) ) + { + char mbuf[2]={'\0','\0'}; + unsigned char major, minor; + + pCon->usedInput( 5 ); + mbuf[0] = pCon->getInput()[0]; + major = (unsigned char)atoi(mbuf); + mbuf[0] = pCon->getInput()[2]; + minor = (unsigned char)atoi(mbuf); + setRequestVersion( major, minor ); + if( checkRequestVer() ) + { + nParseState = parseHeader; + } + else + { + setResponseStatus( statusHTTPVersionNotSupported ); + //printf("Verson not supported.\n"); + return true; + } + + pCon->usedInput( 5 ); + } + else + { + setResponseStatus( statusBadRequest ); + } + + //return false; + } + } + break; + + case parseHeader: + { + int nLen = pCon->scanInputFor( CR ); + //printf("nLen = %d: :::%s:::\n", nLen, pCon->getInput() ); + if( nLen == -1 ) + { + pCon->readInput( 1, 0); + } + else if( nLen == 0 ) + { + // We've got our double-newline, time for content. + pCon->usedInput( 2 ); + setResponseStatus( statusOK ); + return true; + } + else + { + nLen = pCon->scanInputFor(':'); + if( nLen == -1 ) + { + //printf("No colon? what are you trying to pull?\n"); + } + else + { + std::string *pName = new std::string( pCon->getInput(), nLen ); + lStrings.append( pName ); + pCon->usedInput( nLen+1 ); + + nLen = pCon->scanInputFor( CR ); + std::string *pValue = convSpaceString( pCon->getInput(), nLen ); + lStrings.append( pValue ); + pCon->usedInput( nLen+2 ); + + hReqHeader.insert( + pName->c_str(), + pValue->c_str() + ); + + //printf("::%s = \"%s\"\n", + // pName->c_str(), + // pValue->c_str() + // ); + } + } + } + break; + + case parseFinished: + break; + } + } +} + +bool Http::buildResponse( short nResponseCode, const char *sResponse ) +{ + if( nResponseCode > 0 ) + { + nResStatus = nResponseCode; + } + + if( sResponse == NULL ) + { + sResStatusStr = "uh yeah"; + } + else + { + sResStatusStr = sResponse; + } + + time_t curTime; + time( &curTime ); + gmtime_r( &curTime, &tResTime ); + + sServerStr = "libbu++ Http/0.0.1"; + bResPersistant = false; + + //char buf[30]; + //strftime( buf, 30, "%a, %d %b %Y %H:%M:%S GMT", &tResponseTime ); + + return true; +} + +bool Http::sendResponse() +{ + char buf[256]; + + sprintf( buf, "HTTP/1.1 %d %s\r\n", nResStatus, sResStatusStr.c_str() ); + pCon->appendOutput( buf ); + + strftime( buf, 256, "Date: %a, %d %b %Y %H:%M:%S GMT\r\n", &tResTime ); + pCon->appendOutput( buf ); + + sprintf( buf, "Server: %s\r\n", sServerStr.c_str() ); + pCon->appendOutput( buf ); + + if( bResPersistant ) + { + } + else + { + pCon->appendOutput("Connection: close\r\n"); + } + + sprintf( buf, "Content-Type: %s\r\n", sResMime.c_str() ); + pCon->appendOutput( buf ); + + sprintf( buf, "Content-Length: %d\r\n", sResContent.size() ); + pCon->appendOutput( buf ); + + pCon->appendOutput("\r\n"); + + pCon->appendOutput( sResContent.c_str(), sResContent.size() ); + + return true; +} + +void Http::setResponsePersistant( bool bPersistant ) +{ + bResPersistant = bPersistant; +} + +void Http::setResponseContent( const char *sMime, const char *sContent, int nLen ) +{ + sResMime = sMime; + sResContent.erase(); + sResContent.append( sContent, nLen ); +} + +std::string *Http::convSpaceString( const char *sStr, int nLen ) +{ + int nNewLen = 0; + bool bStart = true; + bool bSpace = false; + + for( int j = 0; j < nLen; j++ ) + { + if( sStr[j] == ' ' || sStr[j] == '\t' ) + { + if( bStart ) + { + } + else if( bSpace == false ) + { + bSpace = true; + nNewLen++; + } + } + else + { + bStart = false; + bSpace = false; + nNewLen++; + } + } + if( bSpace ) + { + nNewLen--; + } + + std::string *pSStr = new std::string; + //char *pStr = pSStr->c_str(); + nNewLen = 0; + bStart = true; + bSpace = false; + + for( int j = 0; j < nLen; j++ ) + { + if( sStr[j] == ' ' || sStr[j] == '\t' ) + { + if( bStart ) + { + } + else if( bSpace == false ) + { + bSpace = true; + *pSStr += ' '; + //pStr[nNewLen++] = ' '; + } + } + else + { + bStart = false; + bSpace = false; + *pSStr += sStr[j]; + //pStr[nNewLen++] = sStr[j]; + } + } + if( bSpace == true ) + { + nNewLen--; +// pStr[nNewLen] = '\0'; + } + + return pSStr; +} + +const char *Http::getRequestURI() +{ + return sReqURI.c_str(); +} + +short Http::getRequestType( const char *sType ) +{ + if( !strncmp( sType, "OPTIONS", 7 ) ) + { + return reqOptions; + } + else if( !strncmp( sType, "GET", 3 ) ) + { + return reqGet; + } + else if( !strncmp( sType, "HEAD", 4 ) ) + { + return reqHead; + } + else if( !strncmp( sType, "POST", 4 ) ) + { + return reqPost; + } + else if( !strncmp( sType, "PUT", 3 ) ) + { + return reqPut; + } + else if( !strncmp( sType, "DELETE", 6 ) ) + { + return reqDelete; + } + else if( !strncmp( sType, "TRACE", 5 ) ) + { + return reqTrace; + } + else if( !strncmp( sType, "CONNECT", 7 ) ) + { + return reqConnect; + } + else + { + printf(" Uh oh, extension!\n"); + return reqExtension; + } +} + +const char *Http::getRequestType( short nType ) +{ + switch( nType ) + { + case reqOptions: return "OPTIONS"; + case reqGet: return "GET"; + case reqHead: return "HEAD"; + case reqPost: return "POST"; + case reqPut: return "PUT"; + case reqDelete: return "DELETE"; + case reqTrace: return "TRACE"; + case reqConnect: return "CONNECT"; + case reqExtension: return "EXTENSION"; + default: return "INVALID VALUE"; + } +} + +short Http::getRequestType() +{ + return nReqType; +} + +const char *Http::getRequestTypeStr() +{ + return getRequestType( nReqType ); +} + +void Http::setResponseStatus( short nStatus ) +{ + nResStatus = nStatus; +} + +void Http::setRequestVersion( unsigned char nMajor, unsigned char nMinor ) +{ + cReqVersion = (nMajor<<4)|nMinor; +} + +unsigned char Http::getRequestMinorVer() +{ + return cReqVersion&0x0F; +} + +unsigned char Http::getRequestMajorVer() +{ + return cReqVersion>>4; +} + +bool Http::checkRequestVer() +{ + if( cReqVersion == HTTP11 ) + return true; + return false; +} + +const char *Http::getHeader( const char *lpStr ) +{ + return (const char *)hReqHeader[lpStr]; +} + diff --git a/src/old/http.h b/src/old/http.h new file mode 100644 index 0000000..7e9f9a0 --- /dev/null +++ b/src/old/http.h @@ -0,0 +1,273 @@ +/**\file http.h + * Describe a Hyper Text Transfer Protocol processor. This class will allow + * any program to act as either an HTTP server, client, or both. It contains + * a number of additional helpers and subclasses. + *@author Mike Buland + */ + +#ifndef HTTP_H +#define HTTP_H + +#include +#include "connection.h" +#include "linkedlist.h" +#include "hashtable.h" + +#define CR '\r' /**< The ASCII value of a Carrage Return */ +#define LF '\n' /**< The ASCII value of a Line Feed */ +#define CRLF CR LF /**< Combo of CR+LF for use in http */ + +/** + * Macro to create combined http version codes. This just makes processing a + * little bit faster for the most part. + *@param maj Major version number, between 0 and 15 + *@param min Minor version number, between 0 and 15 + *@returns A one byte combined version number suitable for use in switches. + */ +#define HTTPVER( maj, min ) ((maj<<4)|(min)) + +#define HTTP10 HTTPVER( 1, 0 ) /**< Combined version code for http 1.0 */ +#define HTTP11 HTTPVER( 1, 1 ) /**< Combined version code for http 1.1 */ + +/** + * This is the master HTTP processing class. One instance handles one + * transaction, in the future a different mechanism may be thought up, but for + * now this means that you must create multiple objects to handle a single + * connection that contains multiple requests. + * In the constructor the Http class is given a connection object. This object + * should already be initialized and connected to whatever socket it wants to + * be sending and receiving data to and from. Once that's done you can call + * parseRequest if you're acting as a server, or a variety of buildRequest + * functions to create and send a request if you're a client. + * Please note that this class does not provide any HTTP or extended format + * processing systems, but will allow for mime types tables to be registered. + *@author Mike Buland + */ +class Http +{ +public: + /** + * Create an Http object tied to an existing connection object. + *@param pConnection The live connection object to deal with. + */ + Http( Connection *pConnection ); + + /** + * Standard Deconstructor. + */ + virtual ~Http(); + + /** + * Perform all parsing needed to figure out what an HTTP client wants from + * us. This will setup a number of properties in the Http object itself + * and has the possibility of setting one or more response states initially. + * These states should be checked for immediately after parsing to see if + * an appropriate error message should be generated. These errors can + * include issues with protocol, data formats, or unknown versions of the + * protocol. + *@returns True means that all processing is finished, false means that + * the parseRequest function should be called again when more data is + * ready. A return value of true does not indicate success, only that + * processing is finished, the getResponseStatus function should be called + * to see what status was set in the parse routine. A 200 indicates that + * as far as the parser is concerned, everything when smoothly. Otherwise + * it's your responsibility to build the appropriate error response body + * (like an html file) and send it as the response. + */ + bool parseRequest(); + + /** + * Get a request type's internal Http object id based on the string + * representation. These can be any HTTP/1.1 standard request type. + *@param sType The string that should be checked for type. This is in all + * caps, just like if it came from the HTTP client, which is most often + * the case. + *@returns The numerical ID of the given request type. Please note that + * HTTP/1.1 standard specifies that any string is valid here as long as + * the non-basic string is a request type understood by the serving + * software. This means that anything that is non-standard will return + * a type reqExtension and not an error. This is not a mistake. + */ + short getRequestType( const char *sType ); + + /** + * Get the string representation of an Http object request type integer ID. + * This is used mainly for debugging to be sure the system has what we + * think it has. + *@param nType The integer ID of the request type to process. + *@returns The HTTP/1.1 string representation of that Http object ID code. + */ + const char *getRequestType( short nType ); + + /** + * Returns the Http object request type ID code that is stored in the + * object by either the parseRequest function or use of the buildRequest + * functions. + *@returns The ID of the request type stored in the object. + */ + short getRequestType(); + + /** + * Same as getRequestType, only returns the string representation. + *@returns The string representation of the request type ID stored in the + * object. + */ + const char *getRequestTypeStr(); + + /** + * Sets the version of the request used by the system. This will be used + * by parse request, but is also part of the buildRequest tool functions. + *@param nMajor The major version number. + *@param nMinor The minor version number. + */ + void setRequestVersion( unsigned char nMajor, unsigned char nMinor ); + + /** + * Gets the major version number of the protocol used/to be used in this + * request. + *@returns The major version number of the request protocol. + */ + unsigned char getRequestMinorVer(); + + /** + * Gets the minor version number of the protocol used/to be used in this + * request. + *@returns The minor version number of the request protocol. + */ + unsigned char getRequestMajorVer(); + + /** + * Checks the stored request version against an internal table of supported + * protocol versions. + *@returns True if the protocol version is supported, false otherwise. + */ + bool checkRequestVer(); + + /** + * Converts an arbitrary string to a new string object with space saving + * operations performed ala the HTTP/1.1 specs. All leading and trailing + * whitespace is stripped, and all whitespace within the string is reduced + * to a single space char. + *@param sStr A pointer to the string data to process. + *@param nLen The length of the string to process. Since this function is + * often called on stream data, there is no null terminator where we need + * one. This is here for convinience so the data doesn't need to be hacked + * up or moved to an intermediate buffer. + *@returns A new string that may well be shorter than the original but that + * will have the same value as far as the HTTP/1.1 specs are concerned. + */ + std::string *convSpaceString( const char *sStr, int nLen ); + + /** + * Gets a string pointer to the URI that was/is being requested. This can + * be any RFC standard URI, with or without protocol and domain. + *@returns A pointer to the URI that was/is being requested. + */ + const char *getRequestURI(); + + /** + * Set a new response status. This status can be anything that the HTTP + * specs allow. Other values are allowed as well, but beware, not all + * servers/clients will accept values that are not in the tables in this + * class. + *@param nStatus The status to set. + */ + void setResponseStatus( short nStatus ); + + bool buildResponse( short nResponseCode=-1, const char *sResponse=NULL ); + void setResponseContent( const char *sMime, const char *sContent, int nLen ); + void setResponsePersistant( bool bPersistant ); + bool sendResponse(); + + enum + { + reqOptions, + reqGet, + reqHead, + reqPost, + reqPut, + reqDelete, + reqTrace, + reqConnect, + reqExtension + }; + + enum + { + statusContinue = 100, + statusSwitchProto = 101, + + statusOK = 200, + statusCreated = 201, + statusAccepted = 202, + statusNonAuthInfo = 203, + statusNoContent = 204, + statusResetContent = 205, + statusPartialContent = 206, + + statusMultiChoices = 300, + statusMovedPermanently = 301, + statusFound = 302, + statusSeeOther = 303, + statusNotModified = 304, + statusUseProxy = 305, + statusUnused = 306, + statusTempRedirect = 307, + + statusBadRequest = 400, + statusUnauthorized = 401, + statusPaymentRequired = 402, + statusForbidden = 403, + statusNotFound = 404, + statusMethodNotAllowed = 405, + statusNotAcceptable = 406, + statusProxyAuthRequired = 407, + statusRequestTimeout = 408, + statusConflict = 409, + statusGone = 410, + statusLengthRequired = 411, + statusPreconditionFailed = 412, + statusRequestEntityTooLarge = 413, + statusRequestURITooLong = 414, + statusUnsupportedMediaType = 415, + statusRequestedRangeNotSatisfiable = 416, + statusExpectationFailed = 417, + + statusInternalServerError = 500, + statusNotImplemented = 501, + statusBadGateway = 502, + statusServiceUnavailable = 503, + statusGatewayTimeout = 504, + statusHTTPVersionNotSupported = 505 + }; + + const char *getHeader( const char *lpStr ); + +private: + Connection *pCon; + unsigned char nParseState; + + short nReqType; + std::string *pReqStr; + std::string sReqURI; + unsigned char cReqVersion; + HashTable hReqHeader; + LinkedList lStrings; + + std::string sServerStr; + std::string sResMime; + std::string sResContent; + std::string sResStatusStr; + bool bResPersistant; + struct tm tResTime; + short nResStatus; + + enum + { + parseInit, + parseHeader, + parseFinished + }; +}; + +#endif diff --git a/src/old/httpget.cpp b/src/old/httpget.cpp new file mode 100644 index 0000000..ee1f29c --- /dev/null +++ b/src/old/httpget.cpp @@ -0,0 +1,263 @@ +#include "httpget.h" +#include "exceptions.h" +#include "connection.h" +#include + +char HttpGet::hexcode[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + +HttpGet::HttpGet() : + nPort( 80 ), + sUserAgent("libbu++; HttpGet") +{ +} + +HttpGet::HttpGet( const std::string &url ) : + nPort( 80 ) +{ + setURL( url ); +} + +HttpGet::~HttpGet() +{ +} + +void HttpGet::setURL( const std::string &url ) +{ + int len = url.size(); + //printf("Full URL: %s\n", url.c_str() ); + int pos = url.find("://"); + sProto.assign( url, 0, pos ); + //printf("Protocol: %s\n", sProto.c_str() ); + + int pos2 = url.find("/", pos+3 ); + if( pos2 >= 0 ) + { + sHost.assign( url, pos+3, pos2-pos-3 ); + } + else + { + sHost.assign( url, pos+3, std::string::npos ); + } + + int pos3 = sHost.find(":"); + if( pos3 >= 0 ) + { + nPort = strtol( sHost.c_str()+pos3+1, NULL, 10 ); + sHost.erase( pos3 ); + } + //printf("Hostname: %s\n", sHost.c_str() ); + //printf("Port: %d\n", nPort ); + + pos3 = url.find("?", pos2+1 ); + if( pos3 >= 0 ) + { + sPath.assign( url, pos2, pos3-pos2 ); + //printf("Path: %s\n", sPath.c_str() ); + for(;;) + { + int end = pos3+1; + for(; url[end] != '=' && url[end] != '&' && end < len; end++ ); + std::string sKey, sValue; + sKey.assign( url, pos3+1, end-pos3-1 ); + if( url[end] == '=' ) + { + pos3 = end; + for( end++; url[end] != '&' && end < len; end++ ); + sValue.assign( url, pos3+1, end-pos3-1 ); + pos3 = end; + } + else + { + } + lParams.push_back( StringPair( sKey, sValue ) ); + //printf("Param: %s = %s\n", sKey.c_str(), sValue.c_str() ); + if( end+1 >= len ) break; + } + } + else + { + sPath.assign( url, pos2, std::string::npos ); + //printf("Path: %s\n", sPath.c_str() ); + } + + //printf("\n"); +} + +void HttpGet::addParam( const std::string &key, const std::string &value ) +{ + lParams.push_back( StringPair( key, value ) ); +} + +std::string HttpGet::escape( const std::string &src ) +{ + std::string escaped(""); + for( std::string::const_iterator i = src.begin(); i != src.end(); i++ ) + { + unsigned char j = *i; + if( (j >= '0' && j <= '9') || + (j >= 'a' && j <= 'z') || + (j >= 'A' && j <= 'Z') || + j == '$' || + j == '-' || + j == '_' || + j == '.' || + j == '+' || + j == '!' || + j == '*' || + j == '\'' || + j == '(' || + j == ')' ) + { + escaped += j; + } + else + { + escaped += "%"; + escaped += hexcode[j>>4]; + escaped += hexcode[j&0x0F]; + } + } + + return escaped; +} + +SBuffer *HttpGet::get() +{ + std::string sData; + sData = "GET " + sPath; + if( !lParams.empty() ) + { + sData += "?"; + for( std::list::iterator i = lParams.begin(); + i != lParams.end(); i++ ) + { + if( i != lParams.begin() ) + sData += "&"; + + if( (*i).second == "" ) + { + sData += escape( (*i).first ); + } + else + { + sData += escape( (*i).first ); + sData += "="; + sData += escape( (*i).second ); + } + } + } + + sData += " HTTP/1.1\r\n" + "User-Agent: " + sUserAgent + "\r\n" + "Connection: close\r\n" + "Host: " + sHost + "\r\n" + "Content-type: application/x-www-form-urlencoded\r\n\r\n"; + + //printf("Connection content:\n\n%s\n\n", sData.c_str() ); + + Connection con; + //printf("Opening connection...\n"); + con.open( sHost.c_str(), nPort ); + { + int nSocket = con.getSocket(); + fd_set rfds, wfds, efds; + int retval; + + FD_ZERO(&rfds); + FD_SET(nSocket, &rfds); + FD_ZERO(&wfds); + FD_SET(nSocket, &wfds); + FD_ZERO(&efds); + FD_SET(nSocket, &efds); + + struct timeval tv; + tv.tv_sec = 4; + tv.tv_usec = 0; + + //printf("Selecting on socket, can we read, write, etc?\n"); + retval = select( nSocket+1, &rfds, &wfds, &efds, &tv ); + /*printf("About to write: sock=%d, r=%d, w=%d, e=%d, ret=%d\n", + nSocket, + FD_ISSET( nSocket, &rfds ), + FD_ISSET( nSocket, &wfds ), + FD_ISSET( nSocket, &efds ), + retval + );*/ + + if( retval == 0 ) + { + //printf("Timeout on connection.\n"); + con.close(); + throw ExceptionBase("Connection Timeout on open.\n"); + } + + } + con.appendOutput( sData.c_str(), sData.size() ); + //printf("Writing to socket...\n"); + con.writeOutput(); + //printf("Data written...\n"); + int nSec = 5; + int nUSec = 0; + int nLastAmnt = con.getInputAmnt(); + try + { + double dTotTime = 0.0; + //printf("About to read input...\n"); + while( con.readInput( nSec, nUSec, &nSec, &nUSec ) ) + { + if( nLastAmnt == con.getInputAmnt() ) + { + if( nSec <= 0 && nUSec <= 0 ) + { + //printf("out of time, closing up.\n"); + con.close(); + throw ExceptionBase("Connection Timeout.\n"); + } + if( nSec == 5 && nUSec == 0 ) + { + //printf("No new data, breaking.\n"); + break; + } + } + else + { + dTotTime += (5.0-(nSec+nUSec/1000000.0)); + printf("\rRead %db at %.2fkb/sec", + con.getInputAmnt(), + ((double)(con.getInputAmnt())/1024.0) / dTotTime + ); + fflush( stdout ); + nSec = 5; + nUSec = 0; + nLastAmnt = con.getInputAmnt(); + } + } + } + catch( ConnectionException &e ) + { + //con.close(); + if( strcmp( e.what(), "Connection closed" ) ) + printf("\nConnectionException: %s\n", e.what() ); + } + + int total = con.getInputAmnt(); + const char *dat = con.getInput(); + //printf("\n===> Final size %d\n", total ); + for( int i = 0; i < total; i++ ) + { + if( !memcmp( dat+i, "\r\n\r\n", 4 ) ) + { + SBuffer *buf = new SBuffer; + buf->write( dat+i+4, total-i-4 ); + buf->setPos( 0 ); + con.close(); + return buf; + } + } + con.close(); + + //printf("\n\n%s\n\n", dat ); + + throw ExceptionBase("Something went wrong, incomplete response? fix this.\n"); +} + diff --git a/src/old/httpget.h b/src/old/httpget.h new file mode 100644 index 0000000..8272641 --- /dev/null +++ b/src/old/httpget.h @@ -0,0 +1,44 @@ +#ifndef HTTP_GET_H +#define HTTP_GET_H + +#include +#include +#include +#include + +#include "sbuffer.h" + +class HttpGet +{ +public: + HttpGet(); + HttpGet( const std::string &url ); + virtual ~HttpGet(); + + void setURL( const std::string &url ); + void addParam( const std::string &key, const std::string &value ); + void setUserAgent( const std::string &sUserAgent ) + { + this->sUserAgent = sUserAgent; + } + void setHost( const std::string &sHost ) + { + this->sHost = sHost; + } + + std::string escape( const std::string &src ); + SBuffer *get(); + +private: + std::string sProto; + std::string sHost; + std::string sPath; + int nPort; + std::string sUserAgent; + typedef std::pair StringPair; + std::list lParams; + static char hexcode[]; + +}; + +#endif diff --git a/src/old/linkedlist.cpp b/src/old/linkedlist.cpp new file mode 100644 index 0000000..a9902bc --- /dev/null +++ b/src/old/linkedlist.cpp @@ -0,0 +1,210 @@ +#include "linkedlist.h" + +LinkedList::LinkedList( ) +{ + pBase = NULL; + pTop = NULL; + pLast = NULL; + nSize = 0; + nLast = -1; +} + +LinkedList::~LinkedList( ) +{ +/* + Link *pCur = pBase; + while( pCur ) + { + Link *pLast = pCur; + pCur = pCur->pNext; + delete pLast; + } +*/ + empty(); +} + +void *LinkedList::getAt( int index ) +{ + if( index < 0 || index >= nSize ) + return NULL; + + return getPtrTo( index )->pData; +} + +void LinkedList::append( void *data ) +{ + if( pBase == NULL ) + { + pBase = new Link( data ); + pTop = pBase; + nSize++; + } + else + { + pTop->pNext = new Link( data ); + pTop = pTop->pNext; + nSize++; + } +} + +void LinkedList::insertBefore( void *data, int pos ) +{ + if( pos < 0 || pos > nSize ) + return; + + if( pos == 0 ) + { + Link *pTmp = new Link( data, pBase ); + if( pBase == NULL ) + { + pTop = pTmp; + } + pBase = pTmp; + if( nLast >= 0 ) nLast++; + nSize++; + } + else + { + Link *pCur; + if( (pCur = getPtrTo( pos-1 )) == NULL ) + { + return; + } + Link *pNew = new Link( data, pCur->pNext ); + pCur->pNext = pNew; + if( pNew->pNext == NULL ) + { + pTop = pNew; + } + if( nLast >= pos ) nLast++; + nSize++; + } +} + +int LinkedList::getSize( ) +{ + return nSize; +} + +bool LinkedList::isEmpty( ) +{ + if( nSize == 0 ) + return true; + return false; +} + +void LinkedList::deleteAt( int index ) +{ + if( index >= nSize || + pBase == NULL ) + return; + + if( index == 0 ) + { + Link *pTmp = pBase->pNext; + delete pBase; + pBase = pTmp; + if( nLast >= 0 ) nLast--; + nSize--; + if( pBase == NULL ) + { + pTop = NULL; + } + else if( pBase->pNext == NULL ) + { + pTop = pBase; + } + } + else + { + Link *pCur = getPtrTo( index-1 ); + if( pCur->pNext == pTop ) + { + pTop = pCur; + } + Link *pTmp; + if( pCur->pNext == NULL ) + { + pTmp = NULL; + } + else + { + pTmp = pCur->pNext->pNext; + } + delete pCur->pNext; + pCur->pNext = pTmp; + if( nLast == index ) nLast = -1; + else if( index < nLast ) nLast--; + nSize--; + } +} + +void LinkedList::empty() +{ + while( nSize > 0 ) + { + deleteAt( 0 ); + } +} + +void LinkedList::setSize( int newSize ) +{ + if( newSize < nSize ) + { + // Delete items off of the end of the list. + while( nSize > newSize ) + { + deleteAt( nSize-1 ); + } + } + else + { + // Add null items to the end of the list. + while( nSize < newSize ) + { + append( NULL ); + } + } +} + +void LinkedList::setAt( int index, void *data ) +{ + if( index >= nSize || index < 0 ) + return; + + getPtrTo( index )->pData = data; +} + +LinkedList::Link *LinkedList::getPtrTo( int index ) +{ + if( index < 0 || index >= nSize ) + return NULL; + if( index == nLast ) + { + return pLast; + } + if( index == 0 ) + { + pLast = pBase; + nLast = 0; + return pBase; + } + else + { + Link *pCur = pBase; + int nCur = 0; + if( nLast < index && nLast >= 0 ) + { + pCur = pLast; + nCur = nLast; + } + while( nCur != index ) + { + pCur = pCur->pNext; + nCur++; + } + nLast = index; + pLast = pCur; + return pCur; + } +} diff --git a/src/old/linkedlist.h b/src/old/linkedlist.h new file mode 100644 index 0000000..e430108 --- /dev/null +++ b/src/old/linkedlist.h @@ -0,0 +1,87 @@ +/**@file + * Describes the LinkedList implementation of the List ADT. + *@author Mike Buland + */ + +#ifndef LINKEDLIST_H +#define LINKEDLIST_H + +#include +#include "list.h" + +/** A linked-item implementation of the List ADT. Since the data is linked + * sequentially this is a great choice for lists that will grow and shrink + * a lot, but don't require as much random access. This implementation + * includes optomizations that make iterating through data, and appending + * items to the list take O(1) time. + *@author Mike Buland + */ +class LinkedList : public List +{ +public: + /** + * Construct a blank LinkedList. + */ + LinkedList(); + + /** + * Delete all list data, but do not delete any of the contained elements. + */ + virtual ~LinkedList(); + + void *getAt( int nIndex ); + void append( void *pData ); + void insertBefore( void *pData, int nPos = 0 ); + int getSize( ); + bool isEmpty( ); + void deleteAt( int nIndex ); + void empty(); + void setSize( int nNewSize ); + void setAt( int nIndex, void *pData ); + +private: + /** + * A link in the linked list. + */ + class Link + { + public: + /** + * Construct an empty link. + */ + Link() + { + pData = NULL; + pNext = NULL; + } + /** + * Construct a link filled in with useful data. + *@param newData The data this link should hold. + *@param newNext The next link that this link should point to. + */ + Link( void *newData = NULL, Link * newNext = NULL ) + { + pData = newData; + pNext = newNext; + } + void *pData; /**< A pointer to the contained data. */ + Link *pNext; /**< A pointer to the next link in the chain */ + }; + + /** + * Finds a pointer to the link at index index. This is the core function + * called for all seek operations, and has been optimized as heavily as + * possible. + *@param index The zero-based index of the desired element. + *@returns A pointer to the requested Link, or NULL if it isn't found. + */ + Link *getPtrTo( int index ); + Link *pBase; /**< The first link in the list. */ + Link *pTop; /**< The Last link in the list. */ + Link *pLast; /**< The previously requested link. */ + int nSize; /**< The number of contained links. */ + int nLast; /**< The index of the previously requested link. */ +}; + +#endif + diff --git a/src/old/linkmessage.cpp b/src/old/linkmessage.cpp new file mode 100644 index 0000000..cf3df42 --- /dev/null +++ b/src/old/linkmessage.cpp @@ -0,0 +1,44 @@ +#include "linkmessage.h" +#include + +LinkMessage::LinkMessage( int nNewMsg ) +{ + nMsg = nNewMsg; +} + +LinkMessage::~LinkMessage() +{ +} + +/* +void LinkMessage::setBroadcast( bool bOn ) +{ + bBroadcast = bOn; +} + +bool LinkMessage::isBroadcast() +{ + return bBroadcast; +} + + +void LinkMessage::setFromID( int id ) +{ + nFromLinkID = id; +} + +int LinkMessage::getFromID() +{ + return nFromLinkID; +} + +void LinkMessage::setToID( int id ) +{ + nTargetLinkID = id; +} + +int LinkMessage::getToID() +{ + return nTargetLinkID; +} +*/ diff --git a/src/old/linkmessage.h b/src/old/linkmessage.h new file mode 100644 index 0000000..6cdfb2f --- /dev/null +++ b/src/old/linkmessage.h @@ -0,0 +1,39 @@ +/**\file linkmessage.h + */ + +#ifndef LINKMESSAGE_H +#define LINKMESSAGE_H + +/** + * A message to be broadcast accross ProgramLinks in a ProgramChain. Generally + * one would make a subclass of this in order to transmit more useful + * information, but sometimes it isn't necesarry. + *@author Mike Buland + */ +class LinkMessage +{ +public: + /** + * Construct a blank LinkMessage. + */ + LinkMessage() {}; + + /** + * Deconstruct a LinkMessage. + */ + virtual ~LinkMessage(); + + /** + * Create a LinkMessage object with a specific message assosiated with it + * to start with. + *@param nNewMsg The message to use in the Message object. + */ + LinkMessage( int nNewMsg ); + + /** + * The message contained in the Message object. + */ + int nMsg; +}; + +#endif diff --git a/src/old/linkmessenger.cpp b/src/old/linkmessenger.cpp new file mode 100644 index 0000000..3bd401a --- /dev/null +++ b/src/old/linkmessenger.cpp @@ -0,0 +1,41 @@ +#include "linkmessenger.h" + +LinkMessenger::LinkMessenger() : + pFirst( NULL ), + pLast( NULL ) +{ +} + +LinkMessenger::~LinkMessenger() +{ +} + +void LinkMessenger::enqueueMessage( LinkMessage *pMsg ) +{ + if( pLast == NULL ) + { + pFirst = pLast = new Link; + pLast->pMsg = pMsg; + pLast->pNext = NULL; + } + else + { + pLast->pNext = new Link; + pLast = pLast->pNext; + pLast->pMsg = pMsg; + pLast->pNext = NULL; + } +} + +LinkMessage *LinkMessenger::dequeueMessage() +{ + if( pFirst == NULL ) + return NULL; + + Link *pTmp = pFirst; + pFirst = pFirst->pNext; + LinkMessage *pRet = pTmp->pMsg; + delete pTmp; + return pRet; +} + diff --git a/src/old/linkmessenger.h b/src/old/linkmessenger.h new file mode 100644 index 0000000..ed52639 --- /dev/null +++ b/src/old/linkmessenger.h @@ -0,0 +1,32 @@ +#ifndef LINK_MESSENGER_H +#define LINK_MESSENGER_H + +#include +#include +#include "linkmessage.h" + +class LinkMessenger +{ +public: + LinkMessenger(); + virtual ~LinkMessenger(); + + void enqueueMessage( LinkMessage *pMsg ); + LinkMessage *dequeueMessage(); + bool hasMessages() + { + return (pFirst != NULL); + } + +private: + typedef struct Link + { + LinkMessage *pMsg; + Link *pNext; + }; + Link *pFirst; + Link *pLast; + +}; + +#endif diff --git a/src/old/list.cpp b/src/old/list.cpp new file mode 100644 index 0000000..18f1a66 --- /dev/null +++ b/src/old/list.cpp @@ -0,0 +1,10 @@ +#include "list.h" + +List::List( ) +{ +} + +List::~List( ) +{ +} + diff --git a/src/old/list.h b/src/old/list.h new file mode 100644 index 0000000..c71b328 --- /dev/null +++ b/src/old/list.h @@ -0,0 +1,101 @@ +#ifndef LIST_H +#define LIST_H + + +/** The basic List class ADT. This, on it's own, does absolutely nothing, but + * does define all standard interface functions to access a list. + *@author Mike Buland + */ +class List +{ +public: + /** + * Construct a list. + */ + List(); + + /** + * Desconstruct a list. + */ + virtual ~List(); + + /** Gets the value at a specified index. + *@param nIndex The index of the item to return. + *@returns The specified item, or NULL if the index was beyond the range + * of the list. + *@author Mike Buland + */ + virtual void *getAt( int nIndex ) = 0; + + /** Append the given data to the end of the list. This increases the + * size of the list by one. + *@param pData The data to append to the list. + *@author Mike Buland + */ + virtual void append( void *pData ) = 0; + + /** Inserts an item at the specified position in the list. The + * new item takes the index that you specify, and all other items + * are moved up one position. The size of the list is increased by + * one. + *@param pData The value to insert into the list. + *@param nPos Where to insert the data into the list. + *@author Mike Buland + */ + virtual void insertBefore( void *pData, int nPos = 0 ) = 0; + + /** Determines the size of the list, in elements. + *@returns The size of the list. + *@author Mike Buland + */ + virtual int getSize( ) = 0; + + /** Determines if the list is empty or not. + *@returns True if the list is empty, or false if the list has + * data in it (if the size is greater than zero). + *@author Mike Buland + */ + virtual bool isEmpty( ) = 0; + + /** Deletes an item at the specified index and moves all other + * values down one index. The size of the list is decreased by one. + *@param nIndex The index of the item to delete. + *@author Mike Buland + */ + virtual void deleteAt( int nIndex ) = 0; + + /** Completely empties the list, and sets the effective size to + * zero. + *@author Mike Buland + */ + virtual void empty() = 0; + + /** Sets the size of the list. This can be larger or smaller + * than what it was previously. If larger, new blank items will + * be added to the end of the list. If smaller than the old list + * items will be deleted from the end. + *@param nNewSize The new size of the list. + *@author Mike Buland + */ + virtual void setSize( int nNewSize ) = 0; + + /** Sets a member at a specified location to a new value. + * If the member being set is outside of the range of the + * current list it should be expanded. + *@param nIndex The zero-based index of the item to change. + *@param pData The new value for that index. + *@author Mike Buland + */ + virtual void setAt( int nIndex, void *pData ) = 0; + + /** Makes the List work like an array. Just say listObj[2] to get + * the third element. + *@param nIndex The index to access in the list. + *@returns A pointer to the data at element index. + *@author Mike Buland + */ + void *operator[]( int nIndex ) { return getAt( nIndex ); }; +}; + +#endif + diff --git a/src/old/md5.cpp b/src/old/md5.cpp new file mode 100644 index 0000000..c0cacdd --- /dev/null +++ b/src/old/md5.cpp @@ -0,0 +1,190 @@ +#include +#include +#include +#include "md5.h" + +// This is a fun macro that tells us where the length char goes after the data +// section in the padded data segment. It's short for OBfuscation LOCaction. +#define OBLOC(len) ((((len + 64) >> 9) << 4) + 14) +// This performs a wrapping bitwise shift, kinda' fun! + +#define bit_roll( num, cnt ) \ + (((num) << (cnt)) | (((num) >> (32 - (cnt))) & ~(-1<<(cnt)))) + +//#define md5_cmn( q, a, b, x, s, t ) (bit_roll((a + q + x + t), s) + b) + +// The following are handy wrappers for the cmn function +#define md5_ff( a, b, c, d, x, s, t ) \ + (md5_cmn((b & c) | ((~b) & d), a, b, x, s, t)) + +#define md5_gg( a, b, c, d, x, s, t ) \ + (md5_cmn((b & d) | (c & (~d)), a, b, x, s, t)) + +#define md5_hh( a, b, c, d, x, s, t ) \ + (md5_cmn(b ^ c ^ d, a, b, x, s, t)) + +#define md5_ii( a, b, c, d, x, s, t ) \ + (md5_cmn(c ^ (b | (~d)), a, b, x, s, t)) + +inline long md5_cmn( long q, long a, long b, long x, long s, long t ) +{ + return bit_roll((a + q + x + t), s) + b; +} + +md5::md5() +{ +} + +md5::~md5() +{ +} + +/* + * Calculate the MD5 of an array of little-endian words, and a bit length + */ +void md5::core_md5( long *x, long len, md5sum *output ) +{ + long a = 1732584193, olda; + long b = -271733879, oldb; + long c = -1732584194, oldc; + long d = 271733878, oldd; + + for( long i = 0; i < len; i += 16 ) + { + olda = a; + oldb = b; + oldc = c; + oldd = d; + + a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936); + d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586); + c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819); + b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330); + a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897); + d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426); + c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341); + b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983); + a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416); + d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417); + c = md5_ff(c, d, a, b, x[i+10], 17, -42063); + b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162); + a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682); + d = md5_ff(d, a, b, c, x[i+13], 12, -40341101); + c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290); + b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329); + + a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510); + d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632); + c = md5_gg(c, d, a, b, x[i+11], 14, 643717713); + b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302); + a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691); + d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083); + c = md5_gg(c, d, a, b, x[i+15], 14, -660478335); + b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848); + a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438); + d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690); + c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961); + b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501); + a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467); + d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784); + c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473); + b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734); + + a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558); + d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463); + c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562); + b = md5_hh(b, c, d, a, x[i+14], 23, -35309556); + a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060); + d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353); + c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632); + b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640); + a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174); + d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222); + c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979); + b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189); + a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487); + d = md5_hh(d, a, b, c, x[i+12], 11, -421815835); + c = md5_hh(c, d, a, b, x[i+15], 16, 530742520); + b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651); + + a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844); + d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415); + c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905); + b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055); + a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571); + d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606); + c = md5_ii(c, d, a, b, x[i+10], 15, -1051523); + b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799); + a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359); + d = md5_ii(d, a, b, c, x[i+15], 10, -30611744); + c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380); + b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649); + a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070); + d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379); + c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259); + b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551); + + a = a + olda; + b = b + oldb; + c = c + oldc; + d = d + oldd; + } + + output->data[0] = a; + output->data[1] = b; + output->data[2] = c; + output->data[3] = d; + delete[] x; +} + +long *md5::c2l( const char *str, long len, long *nNewLen ) +{ + long len8 = len*8; + long mlen = OBLOC( len8 ); + long flen = (((mlen/16)+((mlen%16)?(1):(0))))*16; + long *aBin = new long[flen]; + memset( aBin, 0, flen*4 ); + + for( long i = 0; i < len8; i+=8 ) + { + aBin[i>>5] |= ((long)str[i/8]) << (i%32); + } + + aBin[len8 >> 5] |= 0x80 << ((len8) % 32); + aBin[OBLOC( len8 )] = len8; + + (*nNewLen) = flen; + + return aBin; +} + +void md5::l2hexstr( long *binarray, char *str ) +{ + static const char hex_tab[] = {"0123456789abcdef"}; + //static char str[33]; + + int k = 0; + for( int i = 0; i < 16; i++) + { + str[k++] = hex_tab[(binarray[i>>2] >> ((i%4)*8+4)) & 0xF]; + str[k++] = hex_tab[(binarray[i>>2] >> ((i%4)*8 )) & 0xF]; + } +} + +void md5::sumString( md5sum *pSum, const char *sStr ) +{ + sumData( pSum, sStr, strlen( sStr ) ); +} + +void md5::sumData( md5sum *pSum, const char *aData, long nLen ) +{ + long nNewLen; + long *aOb = c2l( aData, nLen, &nNewLen ); + core_md5( aOb, nNewLen, pSum ); +} + +void md5::sumToHex( md5sum *pSum, char *sHex ) +{ + l2hexstr( pSum->data, sHex ); +} + diff --git a/src/old/md5.h b/src/old/md5.h new file mode 100644 index 0000000..7f77d83 --- /dev/null +++ b/src/old/md5.h @@ -0,0 +1,81 @@ +#ifndef MD5_H +#define MD5_H + +/** + * Used to store an MD5 sum in a handy container. + */ +typedef struct +{ + /** The actual data-storage for an MD5 sum. */ + long data[4]; +} md5sum; + +/** + * Class for easily calculating MD5 sums of just about any data. + *@author Mike Buland + */ +class md5 +{ +public: + /** Build an MD5 sum builder. */ + md5(); + + /** Deconstruct */ + virtual ~md5(); + + /** + * Create a sum of a standard c string, null terminated. This is probably + * the easiest function to use. + *@param pSum The MD5 sum structure to fill up. + *@param sStr The null-terminated string to turn into an MD5 sum. + */ + void sumString( md5sum *pSum, const char *sStr ); + + /** + * Create a sum of an array of arbitrary data. This is the most handy for + * dealing with files and so on. + *@param pSum The MD5 sum structure to fill up. + *@param aData A pointer to the base of the data to sum. + *@param nLen The number of bytes to use in the sum. + */ + void sumData( md5sum *pSum, const char *aData, long nLen ); + + /** + * Convert an md5sum to standard hex representation. Make sure that sHex + * contains at least 17 characters of space. + *@param pSum The sum structure to convert to hex. + *@param sHex The string to store the hex value in. + */ + void sumToHex( md5sum *pSum, char *sHex ); + +private: + /** + * Do the bulk of the work of the md5 algorithm. + *@param x I'm not sure. I'll need to look it up. + *@param len The length of the data. + *@param output The sum structure to put the output in. + */ + void core_md5( long *x, long len, md5sum *output ); + + /** + * Convert an array of charaters to an array of longs in a very crafty way. + * This also applies standard MD5 obfuscation to the resulting array, and + * makes it fit within MD5 size constraints. + *@param str The data to convert. + *@param len The length of the data. + *@param nNewLen A pointer to a variable that will hold the new length of + * the resulting array of longs. + *@returns The newly obfuscated and resized long array. + */ + long *c2l( const char *str, long len, long *nNewLen ); + + /** + * Backend helper to convert an array of longs into a hex string. + *@param binarray The binary data to convert. + *@param str The string to store the hex string in. + */ + void l2hexstr( long *binarray, char *str ); + +}; + +#endif diff --git a/src/old/multilog.cpp b/src/old/multilog.cpp new file mode 100644 index 0000000..143ee89 --- /dev/null +++ b/src/old/multilog.cpp @@ -0,0 +1,102 @@ +#include "multilog.h" +#include +#include +#include +#include + +#include "multilogchannel.h" + +MultiLog::MultiLog() +{ + lChannel = new LinkedList(); + rEntry = new RingList( 150 ); + nEntriesLost = 0; +} + +MultiLog::~MultiLog() +{ + int nMax = lChannel->getSize(); + for( int j = 0; j < nMax; j++ ) + { + ((MultiLogChannel *)lChannel->getAt(j))->closeLog(); + delete ((MultiLogChannel *)lChannel->getAt(j)); + } + delete lChannel; + + for( int j = 0; j < rEntry->getSize(); j++ ) + { + delete (LogEntry *)rEntry->getAt( j ); + } + delete rEntry; +} +/* +void MultiLog::Log( int nLevel, const char *lpFormat, ...) +{ + switch( nLevel ) + { + default: + break; + } + va_list ap; + va_start(ap, lpFormat); + + vprintf( lpFormat, ap ); + + va_end(ap); +}*/ + +void MultiLog::DetailLog( int nLevel, const char *lpFile, int nLine, const char *lpFunction, const char *lpFormat, ...) +{ + LogEntry *e = new LogEntry(); + + va_list ap; + va_start(ap, lpFormat); + char *text; + vasprintf( &text, lpFormat, ap ); + va_end(ap); + + time( &e->xTime ); + e->nLevel = nLevel; + e->nLine = nLine; + e->lpFile = new char[strlen(lpFile)+1]; + strcpy( e->lpFile, lpFile ); + e->lpText = new char[strlen(text)+1]; + strcpy( e->lpText, text ); + free( text ); + + append( e ); +} + +void MultiLog::append( LogEntry *pEntry ) +{ + rEntry->append( pEntry ); + if( rEntry->getPushBuf() ) + { + delete (LogEntry *)rEntry->getPushBuf(); + nEntriesLost++; + } + + for( int j = 0; j < lChannel->getSize(); j++ ) + { + ((MultiLogChannel *)lChannel->getAt( j ))->append( pEntry ); + } +} + +void MultiLog::addChannel( MultiLogChannel *pChannel ) +{ + lChannel->append( pChannel ); + + pChannel->openLog(); + + for( int j = 0; j < rEntry->getSize(); j++ ) + { + pChannel->append( (LogEntry *)rEntry->getAt( j ) ); + } +} + +MultiLog::LogEntry::~LogEntry() +{ + delete[] lpFile; + delete[] lpText; +} + diff --git a/src/old/multilog.h b/src/old/multilog.h new file mode 100644 index 0000000..692095a --- /dev/null +++ b/src/old/multilog.h @@ -0,0 +1,130 @@ +#ifndef MULTILOG_H +#define MULTILOG_H + +#include +#include +#include + +#include "ringlist.h" +#include "linkedlist.h" +#include "singleton.h" + +/** + * Calls the DetailLog function but includes pre-processor macros to fill in + * most of the fields for you. This makes your life a lot easier, and makes the + * log useful for system debugging as well as just letting people know what's + * going on. + *@param LEVEL The log level, comes from an enum in the MultiLog class. + *@param FORMAT The text to store in the log, using printf style formatting. + *@param ... Parameters to help format the text in the FROMAT param. + */ +#define LineLog( LEVEL, FORMAT, ...) DetailLog( LEVEL, __FILE__, __LINE__, __PRETTY_FUNCTION__, FORMAT, ##__VA_ARGS__ ) + +#define MultiLineLog( LEVEL, FORMAT, ...) MultiLog::getInstance().DetailLog( LEVEL, __FILE__, __LINE__, __PRETTY_FUNCTION__, FORMAT, ##__VA_ARGS__ ) + +/** MultiLog keeps track of logfile info in a myriad of varieties, and is + * easily configurable between them all. It allows output to the standard + * output, error output, files, networks, and streams, which includes memory + * buffers. + * MultiLog uses the singleton pattern to keep only a single instance of + * the log. Instead of instantiating a new copy, call the getLog method. + *@author Mike Buland + */ +class MultiLog : public Singleton +{ + friend class Singleton; +public: + /** + * Keeps track of a single log entry, in a standard format, that can be + * processed by any MultiLogChannel derrived class. + *@author Mike Buland + */ + typedef struct LogEntry + { + /** Safely delete a log entry. */ + virtual ~LogEntry(); + time_t xTime; /**< The time the log entry was made. */ + int nLevel; /**< The log-level of the entry. */ + char *lpFile; /**< The name of the file this entry came from. */ + int nLine; /**< The line number that this log came from. */ + char *lpText; /**< The text content of this log entry. */ + } LogEntry; + +protected: + /** + * Private constructor, this ensures that this is a singleton. + */ + MultiLog(); + + /** + * Destroy the multilog. + */ + virtual ~MultiLog(); + + /** + * Append a new logentry to the log list, possibly pushing an old entry off. + *@param pEntry The new entry to append. + */ + void append( LogEntry *pEntry ); + + /** + * The actual log entry storage mechanism. + */ + RingList *rEntry; + + /** + * The number of entries that have rolled off the end of the RingList. + */ + unsigned long nEntriesLost; + + /** + * A list of all channels that are registered with the MultiLog. + */ + LinkedList *lChannel; + +public: + + /** Sends info to the logfile. + *@param nLevel The type of data being logged (error, info, etc.) + *@param lpFormat The data to send to the log. + *@author Mike Buland + */ + //void Log( int nLevel, const char *lpFormat, ...); + + /** Sends info to the logfile with extra information, including the files + * that it was called from and the line in the code. Besides that, it's + * exactly the same as Log. Please use the LineLog macro to make DetailLog + * really easy to use. It operates exacly like Log, but inserts the + * builtin macros as the lpFile and nLine parameters. + *@param nLevel The type of data being logged (error, info, etc.) + *@param lpFile The name of the file that called the log function. + *@param nLine The line in the file that this log function was called from. + *@param lpFunction The name of the function that called the log function. + *@param lpFormat The data to send to the log. + *@author Mike Buland + */ + void DetailLog( int nLevel, const char *lpFile, int nLine, const char *lpFunction, const char *lpFormat, ...); + + /** + * Adds a logging channel to the MultiLog channel chain. Every added + * channel will automatically receive a complete log of everything that + * happened before the channel was added as well as all future messages. + *@param pChannel A pointer to the pre-contructed channel object to add. + */ + void addChannel( class MultiLogChannel *pChannel ); + + /** The various pre-defined levels available to use when logging. + * The person logging can make up their own, just make sure to remember + * which value is which (all levels are integers). + *@author Mike Buland + */ + enum Levels + { + LError, + LWarning, + LInfo, + LStatus + }; +}; + +#endif diff --git a/src/old/multilogchannel.cpp b/src/old/multilogchannel.cpp new file mode 100644 index 0000000..ee4c9bf --- /dev/null +++ b/src/old/multilogchannel.cpp @@ -0,0 +1,13 @@ +// +// C++ Implementation: multilogchannel +// +// Description: +// +// +// Author: Mike Buland , (C) 2005 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "multilogchannel.h" + diff --git a/src/old/multilogchannel.h b/src/old/multilogchannel.h new file mode 100644 index 0000000..d891a65 --- /dev/null +++ b/src/old/multilogchannel.h @@ -0,0 +1,46 @@ +#ifndef MULTILOGCHANNEL_H +#define MULTILOGCHANNEL_H + +#include "multilog.h" + +/** + * The baseclass for any MultiLog output channel. Any class that implements + * all of these functions can be put in the log chain and will be sent + * messages from active MultiLoggers. + *@author Mike Buland + */ +class MultiLogChannel +{ +public: + /** + * Deconstruct a MultiLogChannel. + */ + virtual ~MultiLogChannel() {}; + + /** + * Should perform any operations that need to take place in order to start + * the output of data into this channel. This will be called once by the + * MultiLog when the MultiLogChannel is registered. + *@returns True means that everything can go as planned. False means that + * the MultiLog should remove this channel from the list and delete it. + */ + virtual bool openLog() = 0; + + /** + * Should append a log entry to the long, by whatever means are necesarry. + *@param pEntry The LogEntry to append. + *@returns True means that everything can go as planned. False means that + * the MultiLog should remove this channel from the list and delete it. + */ + virtual bool append( MultiLog::LogEntry *pEntry ) = 0; + + /** + * Should perform any operations that need to take place in order to safely + * close and cleanup the log. + *@returns True means that everything can go as planned. False means that + * the MultiLog should remove this channel from the list and delete it. + */ + virtual bool closeLog() = 0; +}; + +#endif diff --git a/src/old/multilogtext.cpp b/src/old/multilogtext.cpp new file mode 100644 index 0000000..4337cc9 --- /dev/null +++ b/src/old/multilogtext.cpp @@ -0,0 +1,188 @@ +#include +#include +#include +#include +#include +#include +#include "multilogtext.h" +/* +bool fileexists( const char *sPath ) +{ + int nFileDesc = open( sPath, O_RDONLY ); + if( nFileDesc < 0 ) + { + return false; + } + else + { + close( nFileDesc ); + return true; + } +}*/ + +MultiLogText::MultiLogText( const char *sFileName, const char *lpFormat, bool bRotateLog, int nMaxLogs ) +{ + this->lpFormat = NULL; + /* + if( bRotateLog ) + { + if( fileexists( sFileName ) == false ) + { + return; + } + + int nLen = strlen( sFileName ); + char *buf = new char[nLen+6]; + sprintf( buf, "%s.", sFileName ); + + for( int j = 1; j < nMaxLogs; j++ ) + { + sprintf( &buf[nLen+1], "%d", j ); + if( !fileexists( buf ) ) + { + rename( sFileName, buf ); + break; + } + } + }*/ + + nFD = open( sFileName, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH ); + setLogFormat( lpFormat ); +} + +MultiLogText::MultiLogText( int nFileDesc, const char *lpFormat ) +{ + this->lpFormat = NULL; + nFD = nFileDesc; + setLogFormat( lpFormat ); +} + +MultiLogText::~MultiLogText() +{ + if( nFD != -1 ) + { + close( nFD ); + } + + delete[] lpFormat; +} + +bool MultiLogText::setLogFormat( const char *lpFormat ) +{ + char buf[200]; + int k = 0; + static char fmts[10][4]={ + {'y', 'd', '0', '1'}, + {'m', 'd', '0', '2'}, + {'d', 'd', '0', '3'}, + {'h', 'd', '0', '4'}, + {'M', 'd', '0', '5'}, + {'s', 'd', '0', '6'}, + {'l', 'd', '0', '7'}, + {'f', 's', '0', '8'}, + {'L', 'd', '0', '9'}, + {'t', 's', '1', '0'}, + }; + + for( int j = 0; lpFormat[j] != '\0'; j++ ) + { + if( lpFormat[j] == '%' ) + { + buf[k++] = '%'; + int nPlace = k++; + k++; + buf[k++] = '$'; + bool bDone = false; + for( j++; bDone == false; j++ ) + { + int l; + for( l = 0; l < 10; l++ ) + { + if( lpFormat[j] == fmts[l][0] ) + { + buf[nPlace] = fmts[l][2]; + buf[nPlace+1] = fmts[l][3]; + buf[k++] = fmts[l][1]; + bDone = true; + break; + } + } + if( l == 10 ) + { + buf[k++] = lpFormat[j]; + } + } + j--; + } + else + { + buf[k++] = lpFormat[j]; + } + } + buf[k++] = '\n'; + buf[k] = '\0'; + + if( this->lpFormat != NULL ) + { + delete[] this->lpFormat; + } + this->lpFormat = new char[k+1]; + strcpy( this->lpFormat, buf ); + + return true; +} + +bool MultiLogText::openLog() +{ + if( nFD == -1 ) + { + return false; + } + return true; +} + +bool MultiLogText::append( MultiLog::LogEntry *pEntry ) +{ + if( nFD == -1 ) + { + return false; + } + + char *line = NULL; + struct tm *pTime; + pTime = localtime( &pEntry->xTime ); + asprintf( + &line, + lpFormat, + pTime->tm_year+1900, + pTime->tm_mon+1, + pTime->tm_mday, + pTime->tm_hour, + pTime->tm_min, + pTime->tm_sec, + pEntry->nLevel, + pEntry->lpFile, + pEntry->nLine, + pEntry->lpText + ); + write( nFD, line, strlen(line) ); + free( line ); + + return true; +} + +bool MultiLogText::closeLog() +{ + if( nFD == -1 ) + { + return false; + } + // Don't close it if it's sdtout or errorout + if( nFD > 2 ) + { + close( nFD ); + } + nFD = -1; + return true; +} + diff --git a/src/old/multilogtext.h b/src/old/multilogtext.h new file mode 100644 index 0000000..197aef1 --- /dev/null +++ b/src/old/multilogtext.h @@ -0,0 +1,70 @@ +#ifndef MULTILOGTEXT_H +#define MULTILOGTEXT_H + +#include "multilogchannel.h" + +/** + * Simple MultiLogChannel that takes the logdata, formats it textually, and + * writes it to a text device, either a file or the screen, yay! This takes + * the place of the old standard logging facility. + * The entries in the format follow the standard printf % style, and are as + * follows: + *
    + *
  • %y - current year
  • + *
  • %m - current month
  • + *
  • %d - current day
  • + *
  • %h - current hour (24-hour format)
  • + *
  • %M - current minute
  • + *
  • %s - current seccond
  • + *
  • %l - Loglevel (numerical)
  • + *
  • %f - Filename
  • + *
  • %L - Line number
  • + *
  • %t - Full text of the log entry
  • + *
+ *@author Mike Buland + */ +class MultiLogText : public MultiLogChannel +{ +public: + /** + * Construct a MultiLogText object around a specific filename and format. + * The file named by sFileName will be opened for writting in text+append + * mode. No existing data will be destroyed. + *@param sFileName The file to output log-data to. + *@param lpFormat The format using the above specifications to be used for + * every log entry. + */ + MultiLogText( const char *sFileName, const char *lpFormat, bool bRotateLog=false, int nMaxLogs=0 ); + + /** + * Construct a MultiLogText object around a specific file and format. + * The file descriptor passed in should describe an already opened and set- + * up file or device. This could easily be a socket or stdout or stderr. + *@param nFileDesc The already opened descriptor to send data to. + *@param lpFormat The format using the above specifications to be used for + * every log entry. + */ + MultiLogText( int nFileDesc, const char *lpFormat ); + + /** + * Destroy the object. + */ + virtual ~MultiLogText(); + + bool openLog(); + bool append( MultiLog::LogEntry *pEntry ); + bool closeLog(); + + /** + * Change the log format on the fly. + *@param lpFormat The new format to use for all future log entries. + *@returns True if everything was fine, false for catastrophic failure. + */ + bool setLogFormat( const char *lpFormat ); + +private: + int nFD; /**< The file descriptor we're writing to. */ + char *lpFormat; /**< The format that we're using, converted for printf. */ +}; + +#endif diff --git a/src/old/ordhash.cpp b/src/old/ordhash.cpp new file mode 100644 index 0000000..77cbd61 --- /dev/null +++ b/src/old/ordhash.cpp @@ -0,0 +1 @@ +#include "ordhash.h" diff --git a/src/old/ordhash.h b/src/old/ordhash.h new file mode 100644 index 0000000..e946f95 --- /dev/null +++ b/src/old/ordhash.h @@ -0,0 +1,104 @@ +#ifndef ORD_HASH_H +#define ORD_HASH_H + +#include "hash.h" +#include "tqsort.h" + +template, typename valuealloc = std::allocator, typename challoc = std::allocator > +class OrdHash : public Hash +{ +public: + OrdHash() : + bSorted( false ), + aData( NULL ) + { + } + + virtual ~OrdHash() + { + } + +protected: + virtual void invalidate() + { + bSorted = false; + delete[] aData; + aData = NULL; + } + + virtual void onInsert() + { + invalidate(); + } + + virtual void onUpdate() + { + invalidate(); + } + + virtual void onDelete() + { + invalidate(); + } + + virtual void onReHash() + { + invalidate(); + } + + virtual std::pair getAtPos( uint32_t nPos ) + { + return Hash::getAtPos( aData[nPos].nIndex ); + } + + virtual void buildIndex() + { + aData = new struct ind[this->nFilled]; + uint32_t k = 0; + for( uint32_t j = 0; j < this->nCapacity; j++ ) + { + if( this->isFilled( j ) ) + { + if( !this->isDeleted( j ) ) + { + aData[k].pVal = &(this->aValues[j]); + aData[k].nIndex = j; + k++; + } + } + } + + tqsort::ind, cmpfnc, value **>( aData, this->nFilled ); + + bSorted = true; + } + + virtual uint32_t getFirstPos( bool &bFinished ) + { + if( bSorted == false ) + buildIndex(); + + return 0; + } + + virtual uint32_t getNextPos( uint32_t nPos, bool &bFinished ) + { + if( nPos+1 >= this->nFilled ) + { + bFinished = true; + return 0; + } + return ++nPos; + } +public: + typedef struct ind + { + value *pVal; + uint32_t nIndex; + } ind; +private: + bool bSorted; + ind *aData; +}; + +#endif diff --git a/src/old/paramproc.cpp b/src/old/paramproc.cpp new file mode 100644 index 0000000..a352e66 --- /dev/null +++ b/src/old/paramproc.cpp @@ -0,0 +1,514 @@ +#include "paramproc.h" +#include + +#define ptrtype( iitype, iiname ) \ + ParamProc::ParamPtr::ParamPtr( iitype *iiname ) : \ + type( vt ##iiname ) { val.iiname = iiname; } + +ParamProc::ParamPtr::ParamPtr() +{ + val.str = NULL; + type = vtunset; +} + +ptrtype( std::string, str ); +ptrtype( uint64_t, uint64 ); +ptrtype( uint32_t, uint32 ); +ptrtype( uint16_t, uint16 ); +ptrtype( uint8_t, uint8 ); +ptrtype( int64_t, int64 ); +ptrtype( int32_t, int32 ); +ptrtype( int16_t, int16 ); +ptrtype( int8_t, int8 ); +ptrtype( float, float32 ); +ptrtype( double, float64 ); +ptrtype( long double, float96 ); +ptrtype( bool, bln ); + +ParamProc::ParamPtr &ParamProc::ParamPtr::operator=( ParamProc::ParamPtr &ptr ) +{ + val = ptr.val; + type = ptr.type; + + return *this; +} + +bool ParamProc::ParamPtr::isSet() +{ + return type != vtunset; +} + +ParamProc::ParamPtr &ParamProc::ParamPtr::operator=( const char *str ) +{ + if( !isSet() ) return *this; + switch( type ) + { + case vtstr: + (*val.str) = str; + break; + + case vtuint64: + (*val.uint64) = strtoull( str, NULL, 10 ); + break; + + case vtuint32: + (*val.uint32) = strtoul( str, NULL, 10 ); + break; + + case vtuint16: + (*val.uint16) = (uint16_t)strtoul( str, NULL, 10 ); + break; + + case vtuint8: + (*val.uint8) = (uint8_t)strtoul( str, NULL, 10 ); + break; + + case vtint64: + (*val.int64) = strtoll( str, NULL, 10 ); + break; + + case vtint32: + (*val.int32) = strtol( str, NULL, 10 ); + break; + + case vtint16: + (*val.int16) = (int16_t)strtol( str, NULL, 10 ); + break; + + case vtint8: + (*val.int8) = (int8_t)strtol( str, NULL, 10 ); + break; + + case vtfloat32: + (*val.float32) = strtof( str, NULL ); + break; + + case vtfloat64: + (*val.float64) = strtod( str, NULL ); + break; + + case vtfloat96: + (*val.float96) = strtold( str, NULL ); + break; + + case vtbln: + if( strcasecmp("yes", str ) == 0 || + strcasecmp("true", str ) == 0 ) + { + (*val.bln) = true; + } + else + { + (*val.bln) = false; + } + break; + } + + return *this; +} + +ParamProc::ParamProc() +{ +} + +ParamProc::~ParamProc() +{ + for( std::list::iterator i = lArg.begin(); + i != lArg.end(); i++ ) + { + delete *i; + } + + for( std::list::iterator i = lBan.begin(); + i != lBan.end(); i++ ) + { + delete *i; + } + +} +/* +void ParamProc::addParam( const char *lpWord, char cChar, Proc proc, ParamPtr val ) +{ + printf("Calling callback...\n"); + val = "Hello there, this is set in the ParamProc"; + (this->*proc)(); +}*/ + +void ParamProc::addParam( const char *lpWord, char cChar, Proc proc, + ParamPtr val, const char *lpDesc, const char *lpExtra, + const char *lpValue ) +{ + ArgSpec *as = new ArgSpec; + if( lpWord ) + as->sWord = lpWord; + + as->cChar = cChar; + as->proc = proc; + as->val = val; + if( lpDesc ) + as->sDesc = lpDesc; + if( lpExtra ) + as->sExtra = lpExtra; + if( lpValue ) + as->sValue = lpValue; + + lArg.push_back( as ); + + if( !lBan.empty() ) + { + if( lBan.back()->pBefore == NULL ) + lBan.back()->pBefore = as; + } +} + +void ParamProc::addParam( const char *lpWord, char cChar, Proc proc, + const char *lpDesc, const char *lpExtra, + const char *lpValue ) +{ + addParam( lpWord, cChar, proc, ParamPtr(), lpDesc, lpExtra, lpValue ); +} + +void ParamProc::addParam( const char *lpWord, char cChar, ParamPtr val, + const char *lpDesc, const char *lpExtra, + const char *lpValue ) +{ + addParam( lpWord, cChar, NULL, val, lpDesc, lpExtra, lpValue ); +} + +void ParamProc::addParam( const char *lpWord, Proc proc, ParamPtr val, + const char *lpDesc, const char *lpExtra, + const char *lpValue ) +{ + addParam( lpWord, '\0', proc, val, lpDesc, lpExtra, lpValue ); +} + +void ParamProc::addParam( const char *lpWord, Proc proc, + const char *lpDesc, const char *lpExtra, + const char *lpValue ) +{ + addParam( lpWord, '\0', proc, ParamPtr(), lpDesc, lpExtra, lpValue ); +} + +void ParamProc::addParam( const char *lpWord, ParamPtr val, + const char *lpDesc, const char *lpExtra, + const char *lpValue ) +{ + addParam( lpWord, '\0', NULL, val, lpDesc, lpExtra, lpValue ); +} + +void ParamProc::addParam( char cChar, Proc proc, ParamPtr val, + const char *lpDesc, const char *lpExtra, + const char *lpValue ) +{ + addParam( NULL, cChar, proc, val, lpDesc, lpExtra, lpValue ); +} + +void ParamProc::addParam( char cChar, Proc proc, + const char *lpDesc, const char *lpExtra, + const char *lpValue ) +{ + addParam( NULL, cChar, proc, ParamPtr(), lpDesc, lpExtra, lpValue ); +} + +void ParamProc::addParam( char cChar, ParamPtr val, + const char *lpDesc, const char *lpExtra, + const char *lpValue ) +{ + addParam( NULL, cChar, NULL, val, lpDesc, lpExtra, lpValue ); +} + +void ParamProc::process( int argc, char *argv[] ) +{ + for( int arg = 1; arg < argc; arg++ ) + { + //printf(":::%d:::%s\n", arg, argv[arg] ); + if( argv[arg][0] == '-' ) + { + if( argv[arg][1] == '-' ) + { + ArgSpec *s = checkWord( argv[arg]+2 ); + if( s ) + { + if( argv[arg][s->sWord.getLength()+2] == '=' ) + { + if( s->val.isSet() ) + { + if( s->sValue.getString() == NULL ) + { + s->val = argv[arg]+s->sWord.getLength()+3; + } + else + { + s->val = s->sValue.getString(); + } + } + if( s->proc ) + { + char **tmp = new char*[argc-arg]; + tmp[0] = argv[arg]+s->sWord.getLength()+3; + for( int k = 1; k < argc-arg; k++ ) + tmp[k] = argv[arg+k]; + int ret = (this->*s->proc)( argc-arg, tmp ); + if( ret > 0 ) + { + arg += ret-1; + } + delete tmp; + } + } + else + { + int add = 0; + if( s->val.isSet() ) + { + if( s->sValue.getString() == NULL ) + { + if( arg+1 >= argc ) + { + return; + } + s->val = argv[arg+1]; + add++; + } + else + { + s->val = s->sValue.getString(); + } + } + if( s->proc ) + { + int ret = (this->*s->proc)( + argc-arg-1, argv+arg+1 ); + + if( ret > add ) + add = 0; + else + add -= ret; + arg += ret; + } + arg += add; + } + continue; + } + else + { + unknownParam( argc-arg, argv+arg ); + } + } + else + { + for( int chr = 1; argv[arg][chr]; chr++ ) + { + ArgSpec *s = checkLetr( argv[arg][chr] ); + if( s ) + { + if( argv[arg][chr+1] != '\0' ) + { + bool bUsed = false; + if( s->val.isSet() ) + { + if( s->sValue.getString() == NULL ) + { + s->val = argv[arg]+chr+1; + bUsed = true; + } + else + { + s->val = s->sValue.getString(); + } + } + if( s->proc ) + { + char **tmp = new char*[argc-arg]; + tmp[0] = argv[arg]+chr+1; + for( int k = 1; k < argc-arg; k++ ) + tmp[k] = argv[arg+k]; + int ret = (this->*s->proc)( argc-arg, tmp ); + if( ret > 0 ) + { + arg += ret - 1; + delete tmp; + break; + } + delete tmp; + } + if( bUsed ) + { + break; + } + } + else + { + bool bUsed = false; + if( s->val.isSet() ) + { + if( s->sValue.getString() == NULL ) + { + s->val = argv[arg+1]; + bUsed = true; + } + else + { + s->val = s->sValue.getString(); + } + } + if( s->proc ) + { + int ret = (this->*s->proc)( + argc-arg-1, argv+arg+1 + ); + if( ret > 0 ) + { + arg += ret; + break; + } + } + if( bUsed ) + { + arg++; + break; + } + } + } + else + { + unknownParam( argc-arg, argv+arg ); + } + } + } + } + else + { + cmdParam( argc-arg, argv+arg ); + } + } +} + +ParamProc::ArgSpec *ParamProc::checkWord( const char *arg ) +{ + //printf("Checking \"%s\"...\n", arg ); + std::list::const_iterator i; + for( i = lArg.begin(); i != lArg.end(); i++ ) + { + if( (*i)->sWord.getString() == NULL ) + continue; + + if( !strcmp( (*i)->sWord, arg ) ) + return *i; + + if( (*i)->val.isSet() ) + { + if( !strncmp( (*i)->sWord, arg, (*i)->sWord.getLength() ) && + arg[(*i)->sWord.getLength()] == '=' ) + { + return *i; + } + } + } + + return NULL; +} + +ParamProc::ArgSpec *ParamProc::checkLetr( const char arg ) +{ + //printf("Checking \'%c\'...\n", arg ); + std::list::const_iterator i; + for( i = lArg.begin(); i != lArg.end(); i++ ) + { + if( (*i)->cChar == '\0' ) + continue; + + if( (*i)->cChar == arg ) + { + return *i; + } + } + + return NULL; +} + +int ParamProc::cmdParam( int argc, char *argv[] ) +{ + printf("Unhandled command parameter \"%s\" found!\n", argv[0] ); + return 0; +} + +int ParamProc::unknownParam( int argc, char *argv[] ) +{ + printf("Unknown parameter \"%s\" found!\n", argv[0] ); + return 0; +} + +int ParamProc::help( int argc, char *argv[] ) +{ + std::list::const_iterator b = lBan.begin(); + std::list::const_iterator i; + int len=0; + for( i = lArg.begin(); i != lArg.end(); i++ ) + { + if( len < (*i)->sWord.getLength() + (*i)->sExtra.getLength() ) + len = (*i)->sWord.getLength() + (*i)->sExtra.getLength(); + } + char fmt[10]; + sprintf( fmt, "%%-%ds ", len ); + + for( i = lArg.begin(); i != lArg.end(); i++ ) + { + if( b != lBan.end() ) + { + if( (*b)->pBefore == (*i) ) + { + printf( (*b)->sBanner.getString() ); + b++; + } + } + printf(" "); + if( (*i)->cChar ) + { + if( (*i)->sWord.getString() ) + { + printf("-%c, ", (*i)->cChar ); + } + else + { + printf("-%c ", (*i)->cChar ); + } + } + else + { + printf(" "); + } + if( (*i)->sWord.getString() ) + { + printf("--"); + std::string sTmp = (*i)->sWord.getString(); + if( (*i)->sExtra.getString() ) + sTmp += (*i)->sExtra.getString(); + printf( fmt, sTmp.c_str() ); + } + else + { + printf(" "); + printf(fmt, "" ); + } + printf("%s\n", (*i)->sDesc.getString() ); + } + if( b != lBan.end() ) + { + if( (*b)->pBefore == NULL ) + { + printf( (*b)->sBanner.getString() ); + } + } + + exit( 0 ); +} + +void ParamProc::addHelpBanner( const char *sHelpBanner ) +{ + Banner *pBan = new Banner; + pBan->sBanner = sHelpBanner; + pBan->pBefore = NULL; + lBan.push_back( pBan ); +} + diff --git a/src/old/paramproc.h b/src/old/paramproc.h new file mode 100644 index 0000000..d857193 --- /dev/null +++ b/src/old/paramproc.h @@ -0,0 +1,153 @@ +#ifndef PARAM_PROC_H +#define PARAM_PROC_H + +#include +#include +#include +#include "staticstring.h" + +class ParamProc +{ +public: + class ParamPtr + { + public: + ParamPtr(); + ParamPtr( std::string *str ); + ParamPtr( uint64_t *uint64 ); + ParamPtr( uint32_t *uint32 ); + ParamPtr( uint16_t *uint16 ); + ParamPtr( uint8_t *uint8 ); + ParamPtr( int64_t *int64 ); + ParamPtr( int32_t *int32 ); + ParamPtr( int16_t *int16 ); + ParamPtr( int8_t *int8 ); + ParamPtr( float *float32 ); + ParamPtr( double *float64 ); + ParamPtr( long double *float96 ); + ParamPtr( bool *bln ); + + enum + { + vtunset, + vtstr, + vtuint64, + vtuint32, + vtuint16, + vtuint8, + vtint64, + vtint32, + vtint16, + vtint8, + vtfloat32, + vtfloat64, + vtfloat96, + vtbln, + }; + ParamPtr &operator=( ParamPtr &ptr ); + ParamPtr &operator=( const char *str ); + + bool isSet(); + + private: + int type; + union + { + std::string *str; + uint64_t *uint64; + uint32_t *uint32; + uint16_t *uint16; + uint8_t *uint8; + int64_t *int64; + int32_t *int32; + int16_t *int16; + int8_t *int8; + float *float32; + double *float64; + long double *float96; + bool *bln; + } val; + }; + + typedef int (ParamProc::*Proc)( int, char *[] ); + + typedef struct ArgSpec + { + uint8_t nFlags; + StaticString sWord; + char cChar; + Proc proc; + ParamProc::ParamPtr val; + StaticString sExtra; + StaticString sDesc; + StaticString sValue; + } ArgSpec; + +public: + ParamProc(); + virtual ~ParamProc(); + + void addParam( const char *lpWord, char cChar, Proc proc, ParamPtr val, + const char *lpDesc=NULL, const char *lpExtra=NULL, + const char *lpValue=NULL + ); + void addParam( const char *lpWord, char cChar, Proc proc, + const char *lpDesc=NULL, const char *lpExtra=NULL, + const char *lpValue=NULL + ); + void addParam( const char *lpWord, char cChar, ParamPtr val, + const char *lpDesc=NULL, const char *lpExtra=NULL, + const char *lpValue=NULL + ); + + void addParam( const char *lpWord, Proc proc, ParamPtr val, + const char *lpDesc=NULL, const char *lpExtra=NULL, + const char *lpValue=NULL + ); + void addParam( const char *lpWord, Proc proc, + const char *lpDesc=NULL, const char *lpExtra=NULL, + const char *lpValue=NULL + ); + void addParam( const char *lpWord, ParamPtr val, + const char *lpDesc=NULL, const char *lpExtra=NULL, + const char *lpValue=NULL + ); + + void addParam( char cChar, Proc proc, ParamPtr val, + const char *lpDesc=NULL, const char *lpExtra=NULL, + const char *lpValue=NULL + ); + void addParam( char cChar, Proc proc, + const char *lpDesc=NULL, const char *lpExtra=NULL, + const char *lpValue=NULL + ); + void addParam( char cChar, ParamPtr val, + const char *lpDesc=NULL, const char *lpExtra=NULL, + const char *lpValue=NULL + ); + + void process( int argc, char *argv[] ); + void addHelpBanner( const char *sHelpBanner ); + +private: + ArgSpec *checkWord( const char *arg ); + ArgSpec *checkLetr( const char arg ); + +public: + virtual int cmdParam( int argc, char *argv[] ); + virtual int unknownParam( int argc, char *argv[] ); + virtual int help( int argc, char *argv[] ); + +private: + typedef struct Banner + { + StaticString sBanner; + ArgSpec *pBefore; + } Banner; + std::list lBan; + std::list lArg; +}; + +#define mkproc( cls ) static_cast(&cls) + +#endif diff --git a/src/old/plugger.cpp b/src/old/plugger.cpp new file mode 100644 index 0000000..f3bfa67 --- /dev/null +++ b/src/old/plugger.cpp @@ -0,0 +1 @@ +#include "plugger.h" diff --git a/src/old/plugger.h b/src/old/plugger.h new file mode 100644 index 0000000..d92f194 --- /dev/null +++ b/src/old/plugger.h @@ -0,0 +1,198 @@ +#ifndef PLUGGER_H +#define PLUGGER_H + + +#include "hashtable.h" +#include "list" +#include "hashfunctionstring.h" +#include "hashfunctionint.h" +#include "dlfcn.h" +#include "exceptions.h" + +typedef struct PluginInfo +{ + const char *sID; + const char *sAuthor; + unsigned short nVersion; + unsigned short nRevision; + void *(*createPlugin)(); + void (*destroyPlugin)( void * ); +} PluginInfo; + +typedef struct PluginReg +{ + bool bBuiltin; + void *dlHandle; + PluginInfo *pInfo; +} PluginReg; + +#define PluginInterface( classname, baseclass, name, ver, rev ) \ +extern "C" { \ + baseclass *create ##classname() \ + { \ + return new classname(); \ + } \ + void destroy ##classname( baseclass *pCls ) \ + { \ + delete pCls; \ + } \ + PluginInfo classname = { \ + #classname, name, ver, rev, \ + create ##classname, destroy ##classname }; \ +} + +#define PluginInterface2( pluginname, classname, baseclass, name, ver, rev ) \ +extern "C" { \ + baseclass *create ##classname() \ + { \ + return new classname(); \ + } \ + void destroy ##classname( baseclass *pCls ) \ + { \ + delete pCls; \ + } \ + PluginInfo pluginname = { \ + #pluginname, name, ver, rev, \ + (void *(*)())(create ##classname), \ + (void (*)( void * ))(destroy ##classname) }; \ +} + +#define PluginInterface3( structname, pluginname, classname, baseclass, name, ver, rev ) \ +extern "C" { \ + baseclass *create ##classname() \ + { \ + return new classname(); \ + } \ + void destroy ##classname( baseclass *pCls ) \ + { \ + delete pCls; \ + } \ + PluginInfo structname = { \ + #pluginname, name, ver, rev, \ + (void *(*)())(create ##classname), \ + (void (*)( void * ))(destroy ##classname) }; \ +} + +template +class Plugger +{ +public: + +public: + Plugger() : + hPlugin( new HashFunctionString(), 11 ), + hObj( new HashFunctionInt(), 11 ) + { + } + + virtual ~Plugger() + { + void *pos = hObj.getFirstItemPos(); + while( (pos = hObj.getNextItemPos( pos )) ) + { + T *pPlug = (T *)hObj.getItemID( pos ); + PluginReg *pReg = (PluginReg *)hObj.getItemData( pos ); + pReg->pInfo->destroyPlugin( pPlug ); + } + + std::list::iterator i; + for( i = lPlugin.begin(); i != lPlugin.end(); i++ ) + { + if( (*i)->bBuiltin == false ) + { + dlclose( (*i)->dlHandle ); + } + delete (*i); + } + } + + void registerBuiltinPlugin( PluginInfo *pInfo ) + { + PluginReg *pReg = new PluginReg; + pReg->bBuiltin = true; + pReg->pInfo = pInfo; + lPlugin.insert( lPlugin.end(), pReg ); + hPlugin.insert( pInfo->sID, pReg ); + } + + void registerExternalPlugin( const char *sFName, const char *sPluginName ) + { + PluginReg *pReg = (PluginReg *)hPlugin[sPluginName]; + if( pReg != NULL ) + { + hPlugin.del( sPluginName ); + dlclose( pReg->dlHandle ); + delete pReg; + pReg = NULL; + } + + pReg = new PluginReg; + + pReg->bBuiltin = false; + pReg->dlHandle = dlopen( sFName, RTLD_NOW ); + if( pReg->dlHandle == NULL ) + { + throw PluginException( 1, "Error on %s: %s", sFName, dlerror() ); + } + pReg->pInfo = (PluginInfo *)dlsym( pReg->dlHandle, sPluginName ); + if( pReg->pInfo == NULL ) + { + throw PluginException( 2, "Error on %s: %s", sFName, dlerror() ); + } + hPlugin.insert( pReg->pInfo->sID, pReg ); + lPlugin.insert( lPlugin.end(), pReg ); + } + + T *instantiate( const char *lpName ) + { + PluginReg *pReg = (PluginReg *)hPlugin[lpName]; + if( pReg == NULL ) + return NULL; + + T *p = (T *)pReg->pInfo->createPlugin(); + hObj.insert( p, pReg ); + //printf("pReg: %08X, pPlug: %08X\n", pReg, p ); + + return p; + } + + bool hasPlugin( const char *lpName ) + { + if( hPlugin[lpName] == NULL ) + return false; + return true; + } + + void destroy( T *pPlug ) + { + PluginReg *pReg = (PluginReg *)hObj[pPlug]; + //printf("pReg: %08X, pPlug: %08X\n", pReg, pPlug ); + if( pReg == NULL ) + return; + + pReg->pInfo->destroyPlugin( pPlug ); + + hObj.del( pPlug ); + } + + void unloadAll() + { + std::list::iterator i; + for( i = lPlugin.begin(); i != lPlugin.end(); i++ ) + { + if( (*i)->bBuiltin == false ) + { + dlclose( (*i)->dlHandle ); + } + delete (*i); + } + hPlugin.clear(); + } + +private: + std::list lPlugin; + HashTable hPlugin; + HashTable hObj; +}; + +#endif diff --git a/src/old/pqueue.cpp b/src/old/pqueue.cpp new file mode 100644 index 0000000..1f0b8b5 --- /dev/null +++ b/src/old/pqueue.cpp @@ -0,0 +1,33 @@ +#include "pqueue.h" + +PQueue::PQueue( int nNewNumQueues ) +{ + nNumQueues = nNewNumQueues; + aQueue = new Queue[nNumQueues]; +} + +PQueue::~PQueue() +{ + delete[] aQueue; +} + +void PQueue::enqueue( void *pData, int nQueueLevel ) +{ + if( nQueueLevel < 0 || nQueueLevel >= nNumQueues ) + return; + + aQueue[nQueueLevel].enqueue( pData ); +} + +void *PQueue::dequeue() +{ + for( int j = 0; j < nNumQueues; j++ ) + { + if( aQueue[j].isEmpty() == false ) + { + return aQueue[j].dequeue(); + } + } + + return NULL; +} diff --git a/src/old/pqueue.h b/src/old/pqueue.h new file mode 100644 index 0000000..8307d56 --- /dev/null +++ b/src/old/pqueue.h @@ -0,0 +1,48 @@ +#ifndef PQUEUE_H +#define PQUEUE_H + +#include "queue.h" + +/** Priority queue. This is just like a queue, but something with a higher + * priority will always come off the queue before something with a lower + * priority, even if it's added after. Otherwise works just like a queue. + *@author Mike Buland + */ +class PQueue +{ +public: + /** Create a queue with any number of different priorities. + *@param nNewNumQueues The number of queues, the default is 3 + */ + PQueue( int nNewNumQueues=3 ); + + /** + * Cleanup all contained queues. + */ + virtual ~PQueue(); + + /** Add a new item to the queue at the specified priority. A lower + * number means a higher priority! + *@param pData A pointer to the data to add to the queue + *@param nQueueLevel The priority to set the new data to + */ + void enqueue( void *pData, int nQueueLevel ); + + /** Pull the next item off the queue, high priority first. + *@returns A pointer to the data that was next in the priority queue + */ + void *dequeue(); + +private: + /** + * The queues we use for real data storage. + */ + Queue *aQueue; + + /** + * The number of priorities or queus that we need. + */ + int nNumQueues; +}; + +#endif diff --git a/src/old/programchain.cpp b/src/old/programchain.cpp new file mode 100644 index 0000000..6120d58 --- /dev/null +++ b/src/old/programchain.cpp @@ -0,0 +1,96 @@ +#include +#include "programchain.h" + +ProgramChain::ProgramChain() : + xLog( MultiLog::getInstance() ) +{ + xLog.LineLog( MultiLog::LStatus, "Program Chain Initialized." ); +} + +ProgramChain::~ProgramChain() +{ +} + +bool ProgramChain::addLink( ProgramLink *pLink ) +{ + if( pLink->init() == false ) + { + emergencyShutdown(); + return false; + } + + lLink.append( pLink ); + + pLink->setChain( this ); + + return true; +} + +ProgramLink *ProgramChain::getLink( const char *lpName ) +{ + char a; + a = lpName[0]; + return NULL; +} + +ProgramLink *ProgramChain::getBaseLink() +{ + return NULL; +} + +bool ProgramChain::execChainOnce() +{ + int nLen = lLink.getSize(); + for( int j = 0; j < nLen; j++ ) + { + if( ((ProgramLink *)lLink[j])->timeSlice() == false ) + { + xLog.LineLog( MultiLog::LInfo, "Shutting down due to signal from link #%d", j ); + emergencyShutdown(); + return false; + } + } + + return true; +} + +bool ProgramChain::enterChainLoop() +{ + for(;;) + { + if( execChainOnce() == false ) + { + return false; + } + } + + return true; +} + +void ProgramChain::emergencyShutdown() +{ + int nLen = lLink.getSize(); + for( int j = 0; j < nLen; j++ ) + { + ((ProgramLink *)lLink[j])->deInit(); + delete (ProgramLink *)lLink[j]; + } + lLink.empty(); +} + +LinkMessage *ProgramChain::broadcastIRM( LinkMessage *pMsgOut, ProgramLink *pSender ) +{ + int nLen = lLink.getSize(); + for( int j = 0; j < nLen; j++ ) + { + LinkMessage *pMsg = ((ProgramLink *)lLink[j])->processIRM( pMsgOut ); + if( pMsg != NULL ) + { + delete pMsgOut; + return pMsg; + } + } + + delete pMsgOut; + return NULL; +} diff --git a/src/old/programchain.h b/src/old/programchain.h new file mode 100644 index 0000000..2bdfeee --- /dev/null +++ b/src/old/programchain.h @@ -0,0 +1,95 @@ +#ifndef PROGRAMCHAIN_H +#define PROGRAMCHAIN_H + +#include "linkedlist.h" +#include "multilog.h" +#include "programlink.h" + +/** + * The Program Chain links together program "chunks" to more easily facilitate + * a generalized program loop with modular extensions. + *@author Mike Buland + */ +class ProgramChain +{ +public: + /** + * Construct an empty chain. + */ + ProgramChain(); + + /** + * Destroy your chain. + */ + virtual ~ProgramChain(); + + /** + * Adds a link to the end of the chain. + *@param pLink A pointer to the link to add to the chain. + *@returns True if adding the link was successful, otherwise false + *@author Mike Buland + */ + bool addLink( ProgramLink *pLink ); + + /** + * Gets a link by name. + *@param lpName The name of the link you're looking for. Every link has a + * name, apparently. + *@returns A pointer to the specified ProgramLink, or NULL if none were + * found matching your criteria. + *@author Mike Buland + */ + class ProgramLink *getLink( const char *lpName ); + + /** + * Gets the very first link in the chain. + *@returns A pointer to the first link in the chain. + *@author Mike Buland + */ + class ProgramLink *getBaseLink(); + + /** + * Runs through the chain once. Useful if you want to have more control + * over the operation of the chain. + *@returns true if every link returned true. If at least one link returns + * false, then returns false. + *@author Mike Buland + */ + bool execChainOnce(); + + /** + * Enters the master chain loop, looping over the entire chain and + * executing every link's TimeSlice routine in order, over and over, until + * a link returns a false value. + *@returns False, always. It returns true unless a link returned false, + * but loops until a link does return false. + *@author Mike Buland + **/ + bool enterChainLoop(); + + /** + * Broadcasts an Immediate Response Message to all active links, save the + * sender. Whatever link first responds with a non-null response message + * will have it's messages sent back to the broadcasting link as the returns + * of this function call. Therefore it is very important that all message + * processing code is handled in a fairly timely fasion. + *@param pMsgOut The message to broadcast in hopes of a response. + *@param pSender The message that sent out the message and doesn't want to + * receive it's own message. This should always just be "this". + *@returns The message that was returned by the first link to return a + * non-null response. If all messages return null responses then this also + * returns null. Please note that whoever calls this will be responsible + * for deleting the message returned by it, if non-null. + */ + class LinkMessage *broadcastIRM( LinkMessage *pMsgOut, ProgramLink *pSender ); + +private: + /** + * Shuts down all operation no matter what point in the operation we were. + */ + void emergencyShutdown(); + MultiLog &xLog; /**< A reference to the log. */ + LinkedList lLink; /**< The linked list that contains all of the links. */ +}; + +#endif diff --git a/src/old/programlink.cpp b/src/old/programlink.cpp new file mode 100644 index 0000000..21c6fe4 --- /dev/null +++ b/src/old/programlink.cpp @@ -0,0 +1,54 @@ +#include "programlink.h" +#include "programchain.h" + +ProgramLink::ProgramLink() +{ +} + +ProgramLink::~ProgramLink() +{ +} + +LinkMessage *ProgramLink::sendIRM( LinkMessage *pMsgOut ) +{ + return pChain->broadcastIRM( pMsgOut, this ); +} + +void ProgramLink::setChain( ProgramChain *pNewChain ) +{ + pChain = pNewChain; +} + +/* +void ProgramLink::postMessage( LinkMessage *pMsg, int nLvl ) +{ + if( nLvl == msgToChain ) + { + qMsgToChain.enqueue( pMsg ); + } + else if( nLvl == msgToLink ) + { + qMsgToLink.enqueue( pMsg ); + } + else + { + // ERROR! + } +} + +LinkMessage *ProgramLink::getMessage( int nLvl ) +{ + if( nLvl == msgToChain ) + { + return (LinkMessage *)qMsgToChain.dequeue(); + } + else if( nLvl == msgToLink ) + { + return (LinkMessage *)qMsgToLink.dequeue(); + } + else + { + // ERROR! + } +} +*/ diff --git a/src/old/programlink.h b/src/old/programlink.h new file mode 100644 index 0000000..f93edcc --- /dev/null +++ b/src/old/programlink.h @@ -0,0 +1,99 @@ +#ifndef PROGRAMLINK_H +#define PROGRAMLINK_H + +class ProgramLink; +#include "queue.h" +#include "linkmessage.h" +#include "programchain.h" + +/** + * Program Link is the base class for any object that will be a piece of the + * main program chain loop. + *@author Mike Buland + */ +class ProgramLink +{ +friend class ProgramChain; +public: + /** + * Construct a program link. + */ + ProgramLink(); + + /** + * Deconstruct. + */ + virtual ~ProgramLink(); + + /** + * Initialization code required for a link that wasn't performed in the + * constructor. + *@returns true if initialization was successful. A false value will halt + * the chain. + */ + virtual bool init()=0; + + /** + * DeInitialization code that should happen, but doesn't belong in the + * destructor. + *@returns true means everything worked, false means failure, but is + * meaningless. + */ + virtual bool deInit()=0; + + /** + * Executed once per link per chain pass. Contains the guts of the program. + *@returns true if everything went well. A false value will halt the chain. + */ + virtual bool timeSlice()=0; + + /** + * This must be handled in order to process Instant Response Messages. + * This function should return null on all messages that it doesn't + * understand how to handle, and construct new messages to return to sender + * in the cases where it does understand. + *@param pMsgIn The message that must be processed. + *@returns Either a new message in cases where a response is required, + * or null if nothing needs to be done by this link. + */ + virtual LinkMessage *processIRM( LinkMessage *pMsgIn ) = 0; + + /** + * Broadcast a LinkMessage to all other links in the system. Each other + * link will get a call of their processIRM function. If the message gets + * a response then you will regain control immediately, otherwise the system + * will give all other Links a chance to respond before returning NULL. + *@param pMsgOut The message to broadcast. + *@returns The message response, or NULL if no Link understood your message. + */ + LinkMessage *sendIRM( LinkMessage *pMsgOut ); + +private: + /** + * Set which chain we're assosiated with. This is how IRM messages make + * it out to the rest of the world. + *@param pNewChain A pointer to the containing program chain. + */ + void setChain( class ProgramChain *pNewChain ); + + /** + * The pointer to the containing chain. + */ + class ProgramChain *pChain; +/* + void postMessage( LinkMessage *pMsg, int nLvl ); + LinkMessage *getMessage( int nLvl ); + + enum + { + msgToChain, + msgToLink + }; + +private: + Queue qMsgToChain; + Queue qMsgToLink; +*/ +}; + +#endif diff --git a/src/old/protocol.cpp b/src/old/protocol.cpp new file mode 100644 index 0000000..78b3ee2 --- /dev/null +++ b/src/old/protocol.cpp @@ -0,0 +1,20 @@ +#include "protocol.h" + +Protocol::Protocol() +{ + pConnection = NULL; +} + +Protocol::~Protocol() +{ +} + +void Protocol::setConnection( Connection *pNewConnection ) +{ + pConnection = pNewConnection; +} + +Connection *Protocol::getConnection() +{ + return pConnection; +} diff --git a/src/old/protocol.h b/src/old/protocol.h new file mode 100644 index 0000000..09e1c98 --- /dev/null +++ b/src/old/protocol.h @@ -0,0 +1,62 @@ +#ifndef PROTOCOL_H +#define PROTOCOL_H + +#include "connection.h" + +/** This is the template for a class that handles specialized input and output + * to connections of different types with different protocols. + *@author Mike Buland + */ +class Protocol +{ +public: + /** Constructor */ + Protocol(); + /** Deconstructor */ + virtual ~Protocol(); + + /** + * Function is called every time there is new data on the line. This is + * called directly from the Connection class to process data. This is not + * called whever there is pending data on the input, but every time new data + * is added to the input buffer. + *@returns True if processing went alright, false if something went wrong, + * I suppose. In truth this value is thrown away right now. + *@todo Either make a return value of false mean something, or make these + * void. + */ + virtual bool onNewData()=0; + + /** + * Function is called when there is a new connection. This should only + * happen once per Protocol object, but gives each protocol object a + * chance to perform connection handshaking and initialization at a point + * where they know that they have a handle to an active Connection. + *@returns See onNewData + */ + virtual bool onNewConnection()=0; + + virtual void onNewClientConnection(){}; + + virtual void poll(){}; + + /** + * Sets the Protocol's Connection object. This is rather important, and + * handled usually by the ConnectionManager. + *@param pNewConnection The Connection object that this protocol will use to + * deal with the outside world. + */ + void setConnection( class Connection *pNewConnection ); + + /** + * Get a pointer to this object's Connection object, or NULL if one was + * never set. If used with the ConnectionManager that should never happen. + *@returns A pointer to the active Connection. + */ + Connection *getConnection(); + +private: + class Connection *pConnection; /**< The pointer to the Connection. */ +}; + +#endif diff --git a/src/old/protocoltelnet.cpp b/src/old/protocoltelnet.cpp new file mode 100644 index 0000000..b169a51 --- /dev/null +++ b/src/old/protocoltelnet.cpp @@ -0,0 +1,316 @@ +#include "protocoltelnet.h" +#include + +ProtocolTelnet::ProtocolTelnet() +{ + nTermType = termUnInited; + bEchoOn = true; +} + +ProtocolTelnet::~ProtocolTelnet() +{ +} + +bool ProtocolTelnet::onNewConnection() +{ + Connection *pCon = getConnection(); + + pCon->appendOutput( (char)IAC ); + pCon->appendOutput( (char)WILL ); + pCon->appendOutput( (char)SUPPRESSGA ); + + pCon->appendOutput( (char)IAC ); + pCon->appendOutput( (char)DO ); + pCon->appendOutput( (char)SUPPRESSGA ); + + pCon->appendOutput( (char)IAC ); + pCon->appendOutput( (char)DONT ); + pCon->appendOutput( (char)TERMTYPE ); + +// pCon->appendOutput( IAC ); +// pCon->appendOutput( SB ); +// pCon->appendOutput( TERMTYPE ); +// pCon->appendOutput( 1 ); +// pCon->appendOutput( IAC ); +// pCon->appendOutput( SE ); + + pCon->appendOutput( (char)IAC ); + pCon->appendOutput( (char)DONT ); + pCon->appendOutput( (char)ECHO ); + + pCon->appendOutput( (char)IAC ); + pCon->appendOutput( (char)WILL ); + pCon->appendOutput( (char)ECHO ); + +// 255(IAC),251(WILL),3 + return true; +} + +bool ProtocolTelnet::onNewData() +{ + Connection *pCon = getConnection(); + if( !pCon->hasInput() ) + { + return true; + } + + int nInSize = pCon->getInputAmnt(); + char *lpInStr = (char *)pCon->getInput(); + + // Here we interpret the basic commands and un-encapsulate them, so to + // speak. We'll allow this, even if the terminal is in raw mode, we + // just won't send anything in response... + for( int j = 0; j < nInSize; j++ ) + { + switch( (unsigned char)lpInStr[j] ) + { + case '\r': + fbEdited.appendData('\n'); + if( bEchoOn ) pCon->appendOutput("\n\r"); + break; + + case '\n': + break; + + case '\177': // backspace + if( fbEdited.getLength() > 0 ) + { + fbEdited.usedData( -1 ); // Delete one char from the end + if( bEchoOn ) pCon->appendOutput(ESC "[D"); // Move the cursor back one + if( bEchoOn ) pCon->appendOutput(ESC "[P"); // Delete one character + } + break; + + case '\x1B': // escape sequence + if( (unsigned char)lpInStr[j+1] == '[' ) + { + switch( (unsigned char)lpInStr[j+2] ) + { + case 'A': // Up + break; + + case 'B': // Down + break; + + case 'C': // Right + break; + + case 'D': // Left + break; + } + j+=2; + } + break; + + case 0: // NOP: No operation + break; + + case IAC: // IAC: Interpret as command + switch( lpInStr[j+1] ) + { + case SE: // SE: End of subnegotiation parameters. + break; + + case NOP: // NOP: No operation + break; + + case DM: // DM: Data mark. Indicates the position of a Synch event within the data stream. This should always be accompanied by a TCP urgent notification. + break; + + case BRK: // BRK: Break. Indicates that the "break" or "attention" key was hit. + break; + + case IP: // IP: Suspend, interrupt or abort the process to which the NVT is connected. + break; + + case AO: // AO: Abort output. Allows the current process to run to completion but do not send its output to the user. + break; + + case AYT: // AYT: Are you there. Send back to the NVT some visible evidence that the AYT was received. + break; + + case EC: // EC: Erase character. The receiver should delete the last preceding undeleted character from the data stream. + break; + + case EL: // EL: Erase line. Delete characters from the data stream back to but not including the previous CRLF. + break; + + case GA: // GA: Go ahead. Used, under certain circumstances, to tell the other end that it can transmit. + break; + + case SB: // SB: Subnegotiation of the indicated option follows. + switch( lpInStr[j+2] ) + { + case TERMTYPE: + if( lpInStr[j+3] == 0 ) + { + for( int k = 0; j+4+k < nInSize; k++ ) + { + if( (unsigned char)lpInStr[j+4+k] == IAC && + (unsigned char)lpInStr[j+5+k] == SE ) + { + lpInStr[j+4+k] = 0; + //@TODO: Do something with the term type... + printf("Term type: %s\n", &lpInStr[j+4] ); + j += 5+k; + } + } + } + else + { + } + break; + + default: + //printf("unknown subnegotiation parameters! (%d)\n", lpInStr[j+2] ); + break; + } + break; + + case WILL: // WILL: Indicates the desire to begin performing + switch( lpInStr[j+2] ) + { + case SUPPRESSGA: + j += 2; +// pCon->usedInput( 3 ); + break; + + case TERMTYPE: + j += 2; +// pCon->usedInput( 3 ); + break; + + case ECHO: + j += 2; +// pCon->usedInput( 3 ); + break; + + case NAWS: + default: + pCon->appendOutput( (char)ESC[0] ); + pCon->appendOutput( (char)DONT ); + pCon->appendOutput( lpInStr[j+2] ); + //printf("unknown will command used! (%d)\n", lpInStr[j+2] ); + j += 2; + break; + } + break; + + case WONT: // WONT: Indicates the refusal to perform + switch( lpInStr[j+2] ) + { + case ECHO: + j += 2; +// pCon->usedInput( 3 ); + break; + + default: + //printf("unknown wont command used! (%d)\n", lpInStr[j+2] ); + j += 2; + break; + } + break; + + case DO: // DO: Indicates the request that the other party perform + switch( lpInStr[j+2] ) + { + case ECHO: + j += 2; + break; + + case SUPPRESSGA: + j += 2; + break; + + default: + pCon->appendOutput( (char)ESC[0] ); + pCon->appendOutput( (char)DONT ); + pCon->appendOutput( lpInStr[j+2] ); + //printf("unknown do command used! (%d)\n", lpInStr[j+2] ); + j += 2; + break; + } +// pCon->usedInput( 3 ); + break; + + case DONT: // DONT: Indicates the demand that the other party stop performing + switch( lpInStr[j+2] ) + { + case ECHO: + j += 2; +// pCon->usedInput( 3 ); + break; + + default: + printf("unknown dont command used! (%d)\n", lpInStr[j+2] ); + j += 2; + break; + } + break; + } + break; + + default: + fbEdited.appendData( lpInStr[j] ); + if( bEchoOn ) pCon->appendOutput( lpInStr[j] ); + break; + } + } + + pCon->usedInput( pCon->getInputAmnt() ); + + return true; +} + +char *ProtocolTelnet::getLine( bool bFullOnly ) +{ + int i = fbEdited.findChar('\n'); + + if( i < 0 ) + { + if( bFullOnly == false ) + { + i = fbEdited.getLength(); + } + else + { + return NULL; + } + } + + char *lpStr = new char[i+1]; + strncpy( lpStr, fbEdited.getData(), i ); + lpStr[i] = '\0'; + + fbEdited.usedData( i+1 ); + + return lpStr; +} + +char *ProtocolTelnet::peekLine( bool bFullOnly ) +{ + int i = fbEdited.findChar('\n'); + + if( i < 0 ) + { + if( bFullOnly == false ) + { + i = fbEdited.getLength(); + } + else + { + return NULL; + } + } + + char *lpStr = new char[i+1]; + strncpy( lpStr, fbEdited.getData(), i ); + lpStr[i] = '\0'; + + return lpStr; +} + +void ProtocolTelnet::setEcho( bool bEchoOn ) +{ + this->bEchoOn = bEchoOn; +} diff --git a/src/old/protocoltelnet.h b/src/old/protocoltelnet.h new file mode 100644 index 0000000..a6d2e49 --- /dev/null +++ b/src/old/protocoltelnet.h @@ -0,0 +1,77 @@ +#ifndef PROTOCOLTELNET_H +#define PROTOCOLTELNET_H + +#include "protocol.h" +#include "flexbuf.h" + +#define ESC "\x1B" /**< A telnet escape code. */ + +/** Handles all specialized protocol actions related to the telnet protocol. + * This includes setting modes, non-scrollable regions, and so on. + *@author Mike Buland + */ +class ProtocolTelnet : public Protocol +{ +public: + ProtocolTelnet(); + virtual ~ProtocolTelnet(); + + bool onNewData(); + bool onNewConnection(); + + char *getLine( bool bFullOnly = true ); + char *peekLine( bool bFullOnly = true ); + + void setEcho( bool bEchoOn = true ); + + enum + { + termUnInited, + termRaw, + termUnknown, + termVT220, + termXTerm + }; + + enum + { + SE = 240, // SE: End of subnegotiation parameters. + NOP = 241, // NOP: No operation + DM = 242, // DM: Data mark. Indicates the position of a Synch event within the data stream. This should always be accompanied by a TCP urgent notification. + BRK = 243, // BRK: Break. Indicates that the "break" or "attention" key was hit. + IP = 244, // IP: Suspend, interrupt or abort the process to which the NVT is connected. + AO = 245, // AO: Abort output. Allows the current process to run to completion but do not send its output to the user. + AYT = 246, // AYT: Are you there. Send back to the NVT some visible evidence that the AYT was received. + EC = 247, // EC: Erase character. The receiver should delete the last preceding undeleted character from the data stream. + EL = 248, // EL: Erase line. Delete characters from the data stream back to but not including the previous CRLF. + GA = 249, // GA: Go ahead. Used, under certain circumstances, to tell the other end that it can transmit. + SB = 250, // SB: Subnegotiation of the indicated option follows. + WILL = 251, // WILL: Indicates the desire to begin performing, or confirmation that you are now performing, the indicated option. + WONT = 252, // WONT: Indicates the refusal to perform, or continue performing, the indicated option. + DO = 253, // DO: Indicates the request that the other party perform, or confirmation that you are expecting the other party to perform, the indicated option. + DONT = 254, // DONT: Indicates the demand that the other party stop performing, or confirmation that you are no longer expecting the other party to perform, the indicated option. + IAC = 255 // IAC: Interpret as command + }; + + enum + { + ECHO = 1, // Explain who'll echo + SUPPRESSGA = 3, // Suppress Go Ahead + TERMTYPE = 24, // Terminal Type + NAWS = 31, // Window size + TERMSPEED = 32, // Terminal Speed + LINEMODE = 34 // Linemode + }; + +private: + int nTermType; + + int nTermWidth; + int nTermHeight; + + FlexBuf fbEdited; + + bool bEchoOn; +}; + +#endif diff --git a/src/old/queue.cpp b/src/old/queue.cpp new file mode 100644 index 0000000..42999fe --- /dev/null +++ b/src/old/queue.cpp @@ -0,0 +1,26 @@ +#include "queue.h" + +void Queue::enqueue( void *data ) +{ + lQueueData.append( data ); +} + +void *Queue::dequeue() +{ + void *dat = lQueueData[0]; + if( dat != NULL ) + { + lQueueData.deleteAt( 0 ); + } + return dat; +} + +bool Queue::isEmpty() +{ + return lQueueData.isEmpty(); +} + +void Queue::empty() +{ + lQueueData.empty(); +} diff --git a/src/old/queue.h b/src/old/queue.h new file mode 100644 index 0000000..692f5d8 --- /dev/null +++ b/src/old/queue.h @@ -0,0 +1,45 @@ +#ifndef QUEUE_H +#define QUEUE_H +#include "linkedlist.h" + +/** + * An ultra-simple queue implementation. It just uses a linked list as it's + * container so we don't have to worry about anything! + *@author Mike Buland + */ +class Queue +{ +public: + /** + * Puts a new item at the end of the queue. + *@param data A new value to put at the end of the queue. + */ + void enqueue( void *data ); + + /** + * Gets the begining item of the queue off and returns it. + *@returns The value at the front of the queue. + */ + void *dequeue(); + + /** + * Checks if the queueu is empty. + *@returns True if the queueu is empty, and false if it has things in it. + */ + bool isEmpty(); + + /** + * Empty the queue. + */ + void empty(); + + /** + * Get a pointer to the internal list object. + *@returns A pointer to the internal list object. + */ + LinkedList *getList() { return &lQueueData; }; + +private: + LinkedList lQueueData; /**< Where all of the real data is stored. */ +}; +#endif diff --git a/src/old/ringlist.cpp b/src/old/ringlist.cpp new file mode 100644 index 0000000..9efbbc4 --- /dev/null +++ b/src/old/ringlist.cpp @@ -0,0 +1,106 @@ +// +// C++ Implementation: ringlist +// +// Description: +// +// +// Author: Mike Buland , (C) 2005 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include + +#include "ringlist.h" + +RingList::RingList( int nInitSize ) + : List() +{ + nFirstIndex = 0; + nRealLength = nInitSize; + nDataLength = 0; + apData = new void*[nInitSize]; + pPushBuf = NULL; +} + +RingList::~RingList() +{ + delete[] apData; +} + +void *RingList::getAt( int nIndex ) +{ + if( nIndex < 0 || nIndex >= nDataLength ) + { + return NULL; + } + + return apData[(nFirstIndex+nIndex)%nRealLength]; +} + +void RingList::append( void *pData ) +{ + int nIndex = (nFirstIndex+nDataLength)%nRealLength; + + pPushBuf = apData[nIndex]; + apData[nIndex] = pData; + + if( nDataLength == nRealLength ) + { + nFirstIndex = (nFirstIndex+1)%nRealLength; + } + else + { + nDataLength++; + // We really didn't need it this time... + pPushBuf = NULL; + } +} + +void RingList::insertBefore( void *pData, int nPos ) +{ + // Not implemented right now, don't even try it! +} + +int RingList::getSize() +{ + return nDataLength; +} + +bool RingList::isEmpty() +{ + return nDataLength==0; +} + +void RingList::deleteAt( int nIndex ) +{ + // Also not implemented yet +} + +void RingList::empty() +{ + nFirstIndex = 0; + nDataLength = 0; +} + +void RingList::setSize( int nNewSize ) +{ + if( apData ) + { + delete[] apData; + } + nFirstIndex = 0; + nRealLength = nNewSize; + nDataLength = 0; + apData = new void*[nNewSize]; +} + +void RingList::setAt( int nIndex, void *pData ) +{ + apData[(nIndex+nFirstIndex)%nRealLength] = pData; +} + +void *RingList::getPushBuf() +{ + return pPushBuf; +} diff --git a/src/old/ringlist.h b/src/old/ringlist.h new file mode 100644 index 0000000..bc069f3 --- /dev/null +++ b/src/old/ringlist.h @@ -0,0 +1,112 @@ +#ifndef RINGLIST_H +#define RINGLIST_H + +#include "list.h" + +/** + * A RingList or Ring Buffer implementation. This is a list that never grows in + * size once it is created, but instead once it is full new items added to the + * RingList replace the oldest items and the zero-index is virtually shifted. + * Since no data is actually moved when zero-index moves, this is very + * efficient. + *
+ * The items removed are not actually deleted by the RingList, so instead they + * are first moved into a temporary "Push Buffer" that can be accessed so that + * elements pushed off the edge of the RingList can be accessed for cleanup. + *@author Mike Buland + */ +class RingList : public List +{ +public: + /** + * Construct a RingList with a fixed initial size. This size never changes + * unless setSize is called later during normal operation. + *@param nInitSize The number of elements to allocate. + */ + RingList( int nInitSize ); + + /** + * Clean up the data structures, but not the contained elements. + */ + virtual ~RingList(); + + /** + * Get an element at the specified index. + *@param nIndex The index of the element to retreive. + *@returns A pointer to the requested element, or NULL if the element is + * not found or not initialized yet. + */ + void *getAt( int nIndex ); + + /** + * Append an element to the end of the list, overwriting the begining if + * necesarry. + *@param pData The pointer to append to the RingList. + */ + void append( void *pData ); + + /** + * Insert an element before another in the RingList, pushing all after it + * down the list. + *@param pData The data to insert. + *@param nPos The position that new the element should occupy in the list. + */ + void insertBefore( void *pData, int nPos = 0 ); + + /** + * Get the size of the array. + */ + int getSize(); + + /** + * Is the RingList empty? + *@returns True if it is empty, false otherwise. + */ + bool isEmpty(); + + /** + * Delete an element in the list, moving all later elements down one index. + *@param nIndex The index of the element to delete. + */ + void deleteAt( int nIndex ); + + /** + * Remove all elements from the RingList. + */ + void empty(); + + /** + * Set a new size for the RingList. Be careful with this one, if shrinking + * this may quietly create a memory leak. + *@param nNewSize The new size to set the RingList to. + *@todo Either fix this memory leak somehow or remove this function. + */ + void setSize( int nNewSize ); + + /** + * Set a specific element to a new value. + *@param nIndex The zero-based index to change the value of. + *@param pData The data to put at the location specified by nIndex. + */ + void setAt( int nIndex, void *pData ); + + /** + * Retrieve the contents of the push buffer. This is the data that is + * pushed off the end of the array if you append data and the list is full. + * This should be checked after every append operation to be sure there + * isn't anything that needs deleting. + *@returns The last value pushed off the RingList, or NULL if nothing was + * pushed off. + */ + void *getPushBuf(); + +private: + int nFirstIndex; /**< The index to be translated as zero. */ + int nRealLength; /**< The Amount of storage space available. */ + int nDataLength; /**< The number of elements filled in. */ + void **apData; /**< The actual data storage. */ + void *pPushBuf; /**< The push buffer. */ + +}; + +#endif diff --git a/src/old/sbuffer.cpp b/src/old/sbuffer.cpp new file mode 100644 index 0000000..f84f8a3 --- /dev/null +++ b/src/old/sbuffer.cpp @@ -0,0 +1,67 @@ +#include +#include "sbuffer.h" + +SBuffer::SBuffer() : + nPos( 0 ), + bOpen( true ) +{ +} + +SBuffer::~SBuffer() +{ +} + +void SBuffer::close() +{ + bOpen = false; + fbData.clearData(); +} + +size_t SBuffer::read( char *pBuf, size_t nBytes ) +{ + size_t nLeft = fbData.getLength() - nPos; + if( nBytes > nLeft ) + nBytes = nLeft; + + if( nLeft == 0 ) + return 0; + + memcpy( pBuf, fbData.getData()+nPos, nBytes ); + nPos += nBytes; + + return nBytes; +} + +size_t SBuffer::write( const char *pBuf, size_t nBytes ) +{ + fbData.appendData( pBuf, nBytes ); + nPos += nBytes; + + return nBytes; +} + +long SBuffer::tell() +{ + return nPos; +} + +void SBuffer::seek( long offset ) +{ + nPos += offset; +} + +void SBuffer::setPos( long pos ) +{ + nPos = pos; +} + +void SBuffer::setPosEnd( long pos ) +{ + nPos = fbData.getLength() - pos; +} + +bool SBuffer::isEOS() +{ + return nPos == fbData.getLength(); +} + diff --git a/src/old/sbuffer.h b/src/old/sbuffer.h new file mode 100644 index 0000000..65feb71 --- /dev/null +++ b/src/old/sbuffer.h @@ -0,0 +1,40 @@ +#ifndef S_BUFFER_H +#define S_BUFFER_H + +#include + +#include "stream.h" +#include "flexbuf.h" + +class SBuffer : public Stream +{ +public: + SBuffer(); + virtual ~SBuffer(); + + virtual void close(); + virtual size_t read( char *pBuf, size_t nBytes ); + + /** + *@todo Update this to write at nPos, not just append data. + */ + virtual size_t write( const char *pBuf, size_t nBytes ); + + virtual long tell(); + virtual void seek( long offset ); + virtual void setPos( long pos ); + virtual void setPosEnd( long pos ); + virtual bool isEOS(); + + FlexBuf &getBuffer() + { + return fbData; + } + +private: + long nPos; + bool bOpen; + FlexBuf fbData; +}; + +#endif diff --git a/src/old/serializable.cpp b/src/old/serializable.cpp new file mode 100644 index 0000000..fd50943 --- /dev/null +++ b/src/old/serializable.cpp @@ -0,0 +1,8 @@ +#include "serializable.h" + +Serializable::Serializable() +{ +} +Serializable::~Serializable() +{ +} diff --git a/src/old/serializable.h b/src/old/serializable.h new file mode 100644 index 0000000..06def29 --- /dev/null +++ b/src/old/serializable.h @@ -0,0 +1,34 @@ +#ifndef SERIALIZER_H +#define SERIALIZER_H + +//#include "serializer.h" + +/** + * The base class for any class you want to serialize. Simply include this as + * a base class, implement the purely virtual serialize function and you've got + * an easily serializable class. + */ +class Serializable +{ +public: + /** + * Does nothing, here for completeness. + */ + Serializable(); + + /** + * Here to ensure the deconstructor is virtual. + */ + virtual ~Serializable(); + + /** + * This is the main workhorse of the serialization system, just override and + * you've got a serializable class. A reference to the Serializer archive + * used is passed in as your only parameter, query it to discover if you are + * loading or saving. + * @param ar A reference to the Serializer object to use. + */ + virtual void serialize( class Serializer &ar )=0; +}; + +#endif diff --git a/src/old/serializer.cpp b/src/old/serializer.cpp new file mode 100644 index 0000000..636224e --- /dev/null +++ b/src/old/serializer.cpp @@ -0,0 +1,338 @@ +#include "serializer.h" +#include "serializable.h" +#include + +Serializer::Serializer(bool bLoading): + bLoading(bLoading) +{ +} +Serializer::~Serializer() +{ +} + +bool Serializer::isLoading() +{ + return bLoading; +} +Serializer &Serializer::operator<<(bool p) +{ + write( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator<<(int8_t p) +{ + write( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator<<(int16_t p) +{ + write( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator<<(int32_t p) +{ + write( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator<<(int64_t p) +{ + write( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator<<(uint8_t p) +{ + write( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator<<(uint16_t p) +{ + write( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator<<(uint32_t p) +{ + write( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator<<(uint64_t p) +{ + write( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator<<(long p) +{ + write( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator<<(float p) +{ + write( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator<<(double p) +{ + write( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator<<(long double p) +{ + write( &p, sizeof(p) ); + return *this; +} + +Serializer &Serializer::operator>>(bool &p) +{ + read( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator>>(int8_t &p) +{ + read( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator>>(int16_t &p) +{ + read( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator>>(int32_t &p) +{ + read( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator>>(int64_t &p) +{ + read( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator>>(uint8_t &p) +{ + read( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator>>(uint16_t &p) +{ + read( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator>>(uint32_t &p) +{ + read( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator>>(uint64_t &p) +{ + read( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator>>(long &p) +{ + read( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator>>(float &p) +{ + read( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator>>(double &p) +{ + read( &p, sizeof(p) ); + return *this; +} +Serializer &Serializer::operator>>(long double &p) +{ + read( &p, sizeof(p) ); + return *this; +} + +Serializer &Serializer::operator&&(bool &p) +{ + if (bLoading) + { + return *this >> p; + } + else + { + return *this << p; + } +} + +Serializer &Serializer::operator&&(int8_t &p) +{ + if (bLoading) + { + return *this >> p; + } + else + { + return *this << p; + } +} + +Serializer &Serializer::operator&&(int16_t &p) +{ + if (bLoading) + { + return *this >> p; + } + else + { + return *this << p; + } +} + +Serializer &Serializer::operator&&(int32_t &p) +{ + if (bLoading) + { + return *this >> p; + } + else + { + return *this << p; + } +} + +Serializer &Serializer::operator&&(int64_t &p) +{ + if (bLoading) + { + return *this >> p; + } + else + { + return *this << p; + } +} + +Serializer &Serializer::operator&&(uint8_t &p) +{ + if (bLoading) + { + return *this >> p; + } + else + { + return *this << p; + } +} + +Serializer &Serializer::operator&&(uint16_t &p) +{ + if (bLoading) + { + return *this >> p; + } + else + { + return *this << p; + } +} + +Serializer &Serializer::operator&&(uint32_t &p) +{ + if (bLoading) + { + return *this >> p; + } + else + { + return *this << p; + } +} + +Serializer &Serializer::operator&&(uint64_t &p) +{ + if (bLoading) + { + return *this >> p; + } + else + { + return *this << p; + } +} + +Serializer &Serializer::operator&&(float &p) +{ + if (bLoading) + { + return *this >> p; + } + else + { + return *this << p; + } +} + +Serializer &Serializer::operator&&(double &p) +{ + if (bLoading) + { + return *this >> p; + } + else + { + return *this << p; + } +} + +Serializer &Serializer::operator&&(long double &p) +{ + if (bLoading) + { + return *this >> p; + } + else + { + return *this << p; + } +} + + +Serializer &operator<<(Serializer &s, Serializable &p) +{ + p.serialize( s ); + return s; +} + +Serializer &operator>>(Serializer &s, Serializable &p) +{ + p.serialize( s ); + return s; +} + +Serializer &operator&&(Serializer &s, Serializable &p) +{ + if (s.isLoading()) + { + return s >> p; + } + else + { + return s << p; + } +} + +Serializer &operator<<( Serializer &ar, std::string &s ) +{ + ar << (uint32_t)s.length(); + ar.write( s.c_str(), s.length() ); + + return ar; +} + +Serializer &operator>>( Serializer &ar, std::string &s ) +{ + uint32_t l; + ar >> l; + char *tmp = new char[l+1]; + tmp[l] = '\0'; + ar.read( tmp, l ); + s = tmp; + delete[] tmp; + + return ar; +} + diff --git a/src/old/serializer.h b/src/old/serializer.h new file mode 100644 index 0000000..3af489c --- /dev/null +++ b/src/old/serializer.h @@ -0,0 +1,80 @@ +#ifndef SERIALIZABLE_H +#define SERIALIZABLE_H + +#include +#include +#include +//#include "serializable.h" + +class Serializer +{ +private: + bool bLoading; +public: + bool isLoading(); + + enum + { + load = true, + save = false + }; + + Serializer(bool bLoading); + virtual ~Serializer(); + virtual void close()=0; + + virtual void write(const void *, int32_t)=0; + virtual void read(void *, int32_t)=0; + + virtual Serializer &operator<<(bool); + virtual Serializer &operator<<(int8_t); + virtual Serializer &operator<<(int16_t); + virtual Serializer &operator<<(int32_t); + virtual Serializer &operator<<(int64_t); + virtual Serializer &operator<<(uint8_t); + virtual Serializer &operator<<(uint16_t); + virtual Serializer &operator<<(uint32_t); + virtual Serializer &operator<<(uint64_t); + virtual Serializer &operator<<(long); + virtual Serializer &operator<<(float); + virtual Serializer &operator<<(double); + virtual Serializer &operator<<(long double); + + virtual Serializer &operator>>(bool &); + virtual Serializer &operator>>(int8_t &); + virtual Serializer &operator>>(int16_t &); + virtual Serializer &operator>>(int32_t &); + virtual Serializer &operator>>(int64_t &); + virtual Serializer &operator>>(uint8_t &); + virtual Serializer &operator>>(uint16_t &); + virtual Serializer &operator>>(uint32_t &); + virtual Serializer &operator>>(uint64_t &); + virtual Serializer &operator>>(long &); + virtual Serializer &operator>>(float &); + virtual Serializer &operator>>(double &); + virtual Serializer &operator>>(long double &); + + virtual Serializer &operator&&(bool &); + virtual Serializer &operator&&(int8_t &); + virtual Serializer &operator&&(int16_t &); + virtual Serializer &operator&&(int32_t &); + virtual Serializer &operator&&(int64_t &); + virtual Serializer &operator&&(uint8_t &); + virtual Serializer &operator&&(uint16_t &); + virtual Serializer &operator&&(uint32_t &); + virtual Serializer &operator&&(uint64_t &); + virtual Serializer &operator&&(float &); + virtual Serializer &operator&&(double &); + virtual Serializer &operator&&(long double &); + + //virtual Serializer &operator&(Serializable &); +}; + +Serializer &operator<<(Serializer &, class Serializable &); +Serializer &operator>>(Serializer &, class Serializable &); +Serializer &operator&&(Serializer &s, class Serializable &p); + +Serializer &operator<<(Serializer &, std::string &); +Serializer &operator>>(Serializer &, std::string &); + +#endif diff --git a/src/old/serializerbinary.cpp b/src/old/serializerbinary.cpp new file mode 100644 index 0000000..ea7ed93 --- /dev/null +++ b/src/old/serializerbinary.cpp @@ -0,0 +1,63 @@ +#include "serializerbinary.h" +#include "serializable.h" +#include "exceptions.h" + +SerializerBinary::SerializerBinary(FILE *fhFile, bool bLoading): + Serializer(bLoading), + fhFile(fhFile), + bCloseFile(false) +{ +} + +SerializerBinary::SerializerBinary(const char *sFileName, bool bLoading): + Serializer(bLoading), + bCloseFile(true) +{ + if (bLoading) + { + fhFile = fopen(sFileName, "rb"); + if( fhFile == NULL ) + throw FileException("Unable to open file: %s", sFileName ); + } + else + { + fhFile = fopen(sFileName, "wb"); + if( fhFile == NULL ) + throw FileException("Unable to open file: %s", sFileName ); + } +} + +SerializerBinary::~SerializerBinary() +{ + close(); +} + +void SerializerBinary::close() +{ + if (fhFile != NULL && bCloseFile ) + { + fclose(fhFile); + fhFile = NULL; + } +} + +void SerializerBinary::write(const void * pData, int32_t nSize) +{ + if( nSize == 0 ) + return; + + fwrite(pData, nSize, 1, fhFile); +} + +void SerializerBinary::read(void * pData, int32_t nSize) +{ + if( nSize == 0 ) + return; + + uint32_t nRead = fread(pData, nSize, 1, fhFile); + if( nRead < 1 ) + { + throw FileException( excodeEOF, "End of file read"); + } +} + diff --git a/src/old/serializerbinary.h b/src/old/serializerbinary.h new file mode 100644 index 0000000..8510fcd --- /dev/null +++ b/src/old/serializerbinary.h @@ -0,0 +1,24 @@ +#ifndef SERIALIZER_BINARY_H +#define SERIALIZER_BINARY_H + +#include "serializer.h" +#include + +class SerializerBinary : public Serializer +{ +public: + SerializerBinary(FILE *fhFile, bool bLoading); + SerializerBinary(const char *sFileName, bool bLoading); + virtual ~SerializerBinary(); + + virtual void close(); + + virtual void write(const void *, int32_t); + virtual void read(void *, int32_t); + +private: + FILE *fhFile; + bool bCloseFile; +}; + +#endif diff --git a/src/old/serializerbzip2.cpp b/src/old/serializerbzip2.cpp new file mode 100644 index 0000000..bafabc8 --- /dev/null +++ b/src/old/serializerbzip2.cpp @@ -0,0 +1,88 @@ +#include "serializerbzip2.h" +#include "serializable.h" + +#include + +SerializerBZip2::SerializerBZip2(FILE *fhFile, bool bLoading): + Serializer(bLoading), + fhFile(fhFile), + bCloseFile(false) +{ + if( bLoading ) + { + bzFile = BZ2_bzReadOpen( &bzerror, fhFile, 0, 0, NULL, 0 ); + checkBZError(); + } + else + { + bzFile = BZ2_bzWriteOpen( &bzerror, fhFile, 9, 0, 0 ); + checkBZError(); + } +} + +SerializerBZip2::SerializerBZip2(char *sFileName, bool bLoading): + Serializer(bLoading), + bCloseFile(true) +{ + if (bLoading) + { + fhFile = fopen(sFileName, "rb"); + bzFile = BZ2_bzReadOpen( &bzerror, fhFile, 0, 0, NULL, 0 ); + checkBZError(); + } + else + { + fhFile = fopen(sFileName, "wb"); + bzFile = BZ2_bzWriteOpen( &bzerror, fhFile, 9, 0, 0 ); + checkBZError(); + } +} + +SerializerBZip2::~SerializerBZip2() +{ + close(); +} + +void SerializerBZip2::checkBZError() +{ +} + +void SerializerBZip2::close() +{ + if( bzFile != NULL ) + { + if( isLoading() ) + { + void *unused; + int nUnused; + BZ2_bzReadGetUnused( &bzerror, bzFile, &unused, &nUnused ); + BZ2_bzReadClose( &bzerror, bzFile ); + if( nUnused ) + fseek( fhFile, -nUnused, SEEK_CUR ); + } + else + { + BZ2_bzWriteClose( &bzerror, bzFile, 0, 0, 0 ); + } + checkBZError(); + bzFile = NULL; + } + if( fhFile != NULL && bCloseFile ) + { + fclose(fhFile); + fhFile = NULL; + } +} + +void SerializerBZip2::write(const void * pData, int32_t nSize) +{ + BZ2_bzWrite( &bzerror, bzFile, (void *)pData, nSize ); + checkBZError(); +} + +void SerializerBZip2::read(void * pData, int32_t nSize) +{ + BZ2_bzRead( &bzerror, bzFile, pData, nSize ); + checkBZError(); +} + diff --git a/src/old/serializerbzip2.h b/src/old/serializerbzip2.h new file mode 100644 index 0000000..4aeb22e --- /dev/null +++ b/src/old/serializerbzip2.h @@ -0,0 +1,27 @@ +#ifndef SERIALIZER_BINARY_H +#define SERIALIZER_BINARY_H + +#include "serializer.h" +#include + +class SerializerBZip2 : public Serializer +{ +public: + SerializerBZip2(FILE *fhFile, bool bLoading); + SerializerBZip2(char *sFileName, bool bLoading); + virtual ~SerializerBZip2(); + + virtual void close(); + + virtual void write(const void *, int32_t); + virtual void read(void *, int32_t); + +private: + void checkBZError(); + FILE *fhFile; + void *bzFile; + bool bCloseFile; + int bzerror; +}; + +#endif diff --git a/src/old/serializerconnection.cpp b/src/old/serializerconnection.cpp new file mode 100644 index 0000000..2934c8b --- /dev/null +++ b/src/old/serializerconnection.cpp @@ -0,0 +1,15 @@ +#include "serializerconnection.h" + +SerializerConnection::SerializerConnection( + Connection *pCon, bool bIO, int nTimeSec, int nTimeUSec ) : + Serializer( bIO ), + pCon( pCon ), + nTimeSec( nTimeSec ), + nTimeUSec( nTimeUSec ) +{ +} + +SerializerConnection::~SerializerConnection() +{ +} + diff --git a/src/old/serializerconnection.h b/src/old/serializerconnection.h new file mode 100644 index 0000000..e8d85c6 --- /dev/null +++ b/src/old/serializerconnection.h @@ -0,0 +1,24 @@ +#ifndef SERIALIZER_CONNECTION_H +#define SERIALIZER_CONNECTION_H + +#include + +#include "serializer.h" +#include "connection.h" + +/** + * + */ +class SerializerConnection : public Serializer +{ +public: + SerializerConnection( Connection *pCon, bool bIO, int nTimeSec, int nTimeUSec ); + virtual ~SerializerConnection(); + +private: + Connection *pCon; + int nTimeSec; + int nTimeUSec; +}; + +#endif diff --git a/src/old/serializertext.cpp b/src/old/serializertext.cpp new file mode 100644 index 0000000..9cf4394 --- /dev/null +++ b/src/old/serializertext.cpp @@ -0,0 +1,170 @@ +#include "serializertext.h" + +SerializerText::SerializerText(FILE *fhFile, bool bLoading): + Serializer(bLoading), + fhFile(fhFile), + bCloseFile(false) +{ +} + +SerializerText::SerializerText(const char *sFileName, bool bLoading): + Serializer(bLoading), + bCloseFile(true) +{ + if (bLoading) + { + fhFile = fopen(sFileName, "rt"); + } + else + { + fhFile = fopen(sFileName, "wt"); + } +} + +SerializerText::~SerializerText() +{ + close(); +} + +void SerializerText::close() +{ + if (fhFile != NULL) + { + fclose(fhFile); + fhFile = NULL; + } +} + +void SerializerText::write(const void * pData, int32_t nSize) +{ + fwrite(pData, nSize, 1, fhFile); + fprintf(fhFile, "\n"); +} + +void SerializerText::read(void * pData, int32_t nSize) +{ + fread(pData, nSize, 1, fhFile); + fgetc(fhFile); +} + +Serializer &SerializerText::operator<<(bool p) +{ + fprintf(fhFile, "%hhd\n", p); + return *this; +} +Serializer &SerializerText::operator<<(int8_t p) +{ + fprintf(fhFile, "%hhd\n", p); + return *this; +} +Serializer &SerializerText::operator<<(int16_t p) +{ + fprintf(fhFile, "%hd\n", p); + return *this; +} +Serializer &SerializerText::operator<<(int32_t p) +{ + fprintf(fhFile, "%d\n", p); + return *this; +} +Serializer &SerializerText::operator<<(int64_t p) +{ + fprintf(fhFile, "%lld\n", p); + return *this; +} +Serializer &SerializerText::operator<<(uint8_t p) +{ + fprintf(fhFile, "%hhu\n", p); + return *this; +} +Serializer &SerializerText::operator<<(uint16_t p) +{ + fprintf(fhFile, "%hu\n", p); + return *this; +} +Serializer &SerializerText::operator<<(uint32_t p) +{ + fprintf(fhFile, "%u\n", p); + return *this; +} +Serializer &SerializerText::operator<<(uint64_t p) +{ + fprintf(fhFile, "%llu\n", p); + return *this; +} +Serializer &SerializerText::operator<<(float p) +{ + fprintf(fhFile, "%f\n", p); + return *this; +} +Serializer &SerializerText::operator<<(double p) +{ + fprintf(fhFile, "%f\n", p); + return *this; +} +Serializer &SerializerText::operator<<(long double p) +{ + fprintf(fhFile, "%Lf\n", p); + return *this; +} + +Serializer &SerializerText::operator>>(bool &p) +{ + fscanf(fhFile, "%hhd\n", ((signed char *)&p)); + return *this; +} +Serializer &SerializerText::operator>>(int8_t &p) +{ + fscanf(fhFile, "%hhd\n", &p); + return *this; +} +Serializer &SerializerText::operator>>(int16_t &p) +{ + fscanf(fhFile, "%hd\n", &p); + return *this; +} +Serializer &SerializerText::operator>>(int32_t &p) +{ + fscanf(fhFile, "%d\n", &p); + return *this; +} +Serializer &SerializerText::operator>>(int64_t &p) +{ + fscanf(fhFile, "%lld\n", &p); + return *this; +} +Serializer &SerializerText::operator>>(uint8_t &p) +{ + fscanf(fhFile, "%hhu\n", &p); + return *this; +} +Serializer &SerializerText::operator>>(uint16_t &p) +{ + fscanf(fhFile, "%hu\n", &p); + return *this; +} +Serializer &SerializerText::operator>>(uint32_t &p) +{ + fscanf(fhFile, "%u\n", &p); + return *this; +} +Serializer &SerializerText::operator>>(uint64_t &p) +{ + fscanf(fhFile, "%llu\n", &p); + return *this; +} +Serializer &SerializerText::operator>>(float &p) +{ + fscanf(fhFile, "%f\n", &p); + return *this; +} +Serializer &SerializerText::operator>>(double &p) +{ + fscanf(fhFile, "%lf\n", &p); + return *this; +} +Serializer &SerializerText::operator>>(long double &p) +{ + fscanf(fhFile, "%Lf\n", &p); + return *this; +} diff --git a/src/old/serializertext.h b/src/old/serializertext.h new file mode 100644 index 0000000..01b7f7b --- /dev/null +++ b/src/old/serializertext.h @@ -0,0 +1,49 @@ +#ifndef SERIALIZER_TEXT_H +#define SERIALIZER_TEXT_H + +#include "serializer.h" +#include + +class SerializerText : public Serializer +{ +public: + SerializerText(FILE *fhFile, bool bLoading); + SerializerText(const char *sFileName, bool bLoading); + virtual ~SerializerText(); + + virtual void close(); + + virtual void write(const void *, int32_t); + virtual void read(void *, int32_t); + + virtual Serializer &operator<<(bool); + virtual Serializer &operator<<(int8_t); + virtual Serializer &operator<<(int16_t); + virtual Serializer &operator<<(int32_t); + virtual Serializer &operator<<(int64_t); + virtual Serializer &operator<<(uint8_t); + virtual Serializer &operator<<(uint16_t); + virtual Serializer &operator<<(uint32_t); + virtual Serializer &operator<<(uint64_t); + virtual Serializer &operator<<(float); + virtual Serializer &operator<<(double); + virtual Serializer &operator<<(long double); + + virtual Serializer &operator>>(bool &); + virtual Serializer &operator>>(int8_t &); + virtual Serializer &operator>>(int16_t &); + virtual Serializer &operator>>(int32_t &); + virtual Serializer &operator>>(int64_t &); + virtual Serializer &operator>>(uint8_t &); + virtual Serializer &operator>>(uint16_t &); + virtual Serializer &operator>>(uint32_t &); + virtual Serializer &operator>>(uint64_t &); + virtual Serializer &operator>>(float &); + virtual Serializer &operator>>(double &); + virtual Serializer &operator>>(long double &); +private: + FILE *fhFile; + bool bCloseFile; +}; + +#endif diff --git a/src/old/sfile.cpp b/src/old/sfile.cpp new file mode 100644 index 0000000..f1de03c --- /dev/null +++ b/src/old/sfile.cpp @@ -0,0 +1,74 @@ +#include "sfile.h" +#include "exceptions.h" + +SFile::SFile( const char *sName, const char *sFlags ) +{ + fh = fopen( sName, sFlags ); +} + +SFile::~SFile() +{ +} + +void SFile::close() +{ + if( fh ) + { + fclose( fh ); + fh = NULL; + } +} + +size_t SFile::read( char *pBuf, size_t nBytes ) +{ + if( !fh ) + throw FileException("File not open."); + + return fread( pBuf, 1, nBytes, fh ); +} + +size_t SFile::write( const char *pBuf, size_t nBytes ) +{ + if( !fh ) + throw FileException("File not open."); + + return fwrite( pBuf, 1, nBytes, fh ); +} + +long SFile::tell() +{ + if( !fh ) + throw FileException("File not open."); + + return ftell( fh ); +} + +void SFile::seek( long offset ) +{ + if( !fh ) + throw FileException("File not open."); + + fseek( fh, offset, SEEK_CUR ); +} + +void SFile::setPos( long pos ) +{ + if( !fh ) + throw FileException("File not open."); + + fseek( fh, pos, SEEK_SET ); +} + +void SFile::setPosEnd( long pos ) +{ + if( !fh ) + throw FileException("File not open."); + + fseek( fh, pos, SEEK_END ); +} + +bool SFile::isEOS() +{ + return feof( fh ); +} + diff --git a/src/old/sfile.h b/src/old/sfile.h new file mode 100644 index 0000000..b51e5bc --- /dev/null +++ b/src/old/sfile.h @@ -0,0 +1,29 @@ +#ifndef SFILE_H +#define SFILE_H + +#include + +#include "stream.h" + +class SFile : public Stream +{ +public: + SFile( const char *sName, const char *sFlags ); + virtual ~SFile(); + + virtual void close(); + virtual size_t read( char *pBuf, size_t nBytes ); + virtual size_t write( const char *pBuf, size_t nBytes ); + + virtual long tell(); + virtual void seek( long offset ); + virtual void setPos( long pos ); + virtual void setPosEnd( long pos ); + virtual bool isEOS(); + +private: + FILE *fh; + +}; + +#endif diff --git a/src/old/sha1.cpp b/src/old/sha1.cpp new file mode 100644 index 0000000..8270c3b --- /dev/null +++ b/src/old/sha1.cpp @@ -0,0 +1,161 @@ +#include +#include +#include + +#include "sha1.h" + +Sha1::Sha1() : + H0( 0x67452301 ), + H1( 0xefcdab89 ), + H2( 0x98badcfe ), + H3( 0x10325476 ), + H4( 0xc3d2e1f0 ), + unprocessedBytes( 0 ), + size( 0 ) +{ +} + +Sha1::~Sha1() +{ +} + +void Sha1::process() +{ + int t; + uint32_t a, b, c, d, e, K, f, W[80]; + + // starting values + a = H0; + b = H1; + c = H2; + d = H3; + e = H4; + + // copy and expand the message block + for( t = 0; t < 16; t++ ) W[t] = (bytes[t*4] << 24) + +(bytes[t*4 + 1] << 16) + +(bytes[t*4 + 2] << 8) + + bytes[t*4 + 3]; + for(; t< 80; t++ ) W[t] = lrot( W[t-3]^W[t-8]^W[t-14]^W[t-16], 1 ); + + /* main loop */ + uint32_t temp; + for( t = 0; t < 80; t++ ) + { + if( t < 20 ) { + K = 0x5a827999; + f = (b & c) | ((~b) & d); + } else if( t < 40 ) { + K = 0x6ed9eba1; + f = b ^ c ^ d; + } else if( t < 60 ) { + K = 0x8f1bbcdc; + f = (b & c) | (b & d) | (c & d); + } else { + K = 0xca62c1d6; + f = b ^ c ^ d; + } + temp = lrot(a,5) + f + e + W[t] + K; + e = d; + d = c; + c = lrot(b,30); + b = a; + a = temp; + //printf( "t=%d %08x %08x %08x %08x %08x\n",t,a,b,c,d,e ); + } + + /* add variables */ + H0 += a; + H1 += b; + H2 += c; + H3 += d; + H4 += e; + + //printf( "Current: %08x %08x %08x %08x %08x\n",H0,H1,H2,H3,H4 ); + /* all bytes have been processed */ + unprocessedBytes = 0; +} + +void Sha1::update( const char* data, int num ) +{ + // add these bytes to the running total + size += num; + + // repeat until all data is processed + while( num > 0 ) + { + // number of bytes required to complete block + int needed = 64 - unprocessedBytes; + + // number of bytes to copy (use smaller of two) + int toCopy = (num < needed) ? num : needed; + + // Copy the bytes + memcpy( bytes + unprocessedBytes, data, toCopy ); + + // Bytes have been copied + num -= toCopy; + data += toCopy; + unprocessedBytes += toCopy; + + // there is a full block + if( unprocessedBytes == 64 ) process(); + } +} + +unsigned char* Sha1::getDigest() +{ + // save the message size + uint32_t totalBitsL = size << 3; + uint32_t totalBitsH = size >> 29; + + // add 0x80 to the message + update( "\x80", 1 ); + + unsigned char footer[64] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + // block has no room for 8-byte filesize, so finish it + if( unprocessedBytes > 56 ) + update( (char*)footer, 64 - unprocessedBytes); + + // how many zeros do we need + int neededZeros = 56 - unprocessedBytes; + + // store file size (in bits) in big-endian format + toBigEndian( totalBitsH, footer + neededZeros ); + toBigEndian( totalBitsL, footer + neededZeros + 4 ); + + // finish the final block + update( (char*)footer, neededZeros + 8 ); + + // allocate memory for the digest bytes + unsigned char* digest = new unsigned char[20]; + + // copy the digest bytes + toBigEndian( H0, digest ); + toBigEndian( H1, digest + 4 ); + toBigEndian( H2, digest + 8 ); + toBigEndian( H3, digest + 12 ); + toBigEndian( H4, digest + 16 ); + + // return the digest + return digest; +} + +uint32_t Sha1::lrot( uint32_t x, int bits ) +{ + return (x<>(32 - bits)); +}; + +void Sha1::toBigEndian( uint32_t num, unsigned char* byte ) +{ + byte[0] = (unsigned char)(num>>24); + byte[1] = (unsigned char)(num>>16); + byte[2] = (unsigned char)(num>>8); + byte[3] = (unsigned char)num; +} + diff --git a/src/old/sha1.h b/src/old/sha1.h new file mode 100644 index 0000000..ab6081d --- /dev/null +++ b/src/old/sha1.h @@ -0,0 +1,42 @@ +/* sha1.h + +Copyright (c) 2005 Michael D. Leonhard + +http://tamale.net/ + +This file is licensed under the terms described in the +accompanying LICENSE file. +*/ + +#ifndef SHA1_H +#define SHA1_H + +#include + +/** + * Calculates SHA-1 sums. This is based strongly on code from Michael D. + * Leonhard who released his code under the terms of the MIT license, thank you! + */ +class Sha1 +{ +public: + Sha1(); + ~Sha1(); + + void update( const char* data, int num ); + unsigned char* getDigest(); + + // utility methods + static uint32_t lrot( uint32_t x, int bits ); + static void toBigEndian( uint32_t in, unsigned char* out ); + +private: + // fields + uint32_t H0, H1, H2, H3, H4; + unsigned char bytes[64]; + int unprocessedBytes; + uint32_t size; + void process(); +}; + +#endif diff --git a/src/old/singleton.h b/src/old/singleton.h new file mode 100644 index 0000000..47adbd5 --- /dev/null +++ b/src/old/singleton.h @@ -0,0 +1,59 @@ +#ifndef SINGLETON_H +#define SINGLETON_H + +#include + +/** + * Provides singleton functionality in a modular sort of way. Make this the + * base class of any other class and you immediately gain singleton + * functionality. Be sure to make your constructor and various functions use + * intellegent scoping. Cleanup and instantiation are performed automatically + * for you at first use and program exit. There are two things that you must + * do when using this template, first is to inherit from it with the name of + * your class filling in for T and then make this class a friend of your class. + *@code + * // Making the Single Singleton: + * class Single : public Singleton + * { + * friend class Singleton; + * protected: + * Single(); + * ... + * }; + @endcode + * You can still add public functions and variables to your new Singleton child + * class, but your constructor should be protected (hence the need for the + * friend decleration). + *@author Mike Buland + */ +template +class Singleton +{ +protected: + /** + * Private constructor. This constructor is empty but has a body so that + * you can make your own override of it. Be sure that you're override is + * also protected. + */ + Singleton() {}; + +private: + /** + * Copy constructor, defined so that you could write your own as well. + */ + Singleton( const Singleton& ); + +public: + /** + * Get a handle to the contained instance of the contained class. It is + * a reference. + *@returns A reference to the contained object. + */ + static T &getInstance() + { + static T i; + return i; + } +}; + +#endif diff --git a/src/old/sptr.cpp b/src/old/sptr.cpp new file mode 100644 index 0000000..7f5e894 --- /dev/null +++ b/src/old/sptr.cpp @@ -0,0 +1 @@ +#include "sptr.h" diff --git a/src/old/sptr.h b/src/old/sptr.h new file mode 100644 index 0000000..deae94d --- /dev/null +++ b/src/old/sptr.h @@ -0,0 +1,99 @@ +#ifndef SPTR_H +#define SPTR_H + +#include +#include + +template +class SPtr +{ +public: + SPtr() : + pRefCnt( NULL ), + pData( NULL ) + { + } + + ~SPtr() + { + decCount(); + } + + SPtr( const SPtr &src ) : + pRefCnt( src.pRefCnt ), + pData( src.pData ) + { + if( pRefCnt ) + (*pRefCnt) += 1; + } + + SPtr( T *pSrc ) : + pRefCnt( NULL ), + pData( pSrc ) + { + pRefCnt = new int32_t; + (*pRefCnt) = 1; + } + + int32_t count() + { + return *pRefCnt; + } + + T *operator->() const + { + return pData; + } + + T *operator*() const + { + return pData; + } + + SPtr operator=( const SPtr &src ) + { + decCount(); + pRefCnt = src.pRefCnt; + pData = src.pData; + (*pRefCnt) += 1; + + return *this; + } + + bool operator==( const SPtr &src ) + { + return pData == src.pData; + } + + operator bool() + { + return pRefCnt != NULL; + } + + bool isSet() + { + return pRefCnt != NULL; + } + +private: + void decCount() + { + if( pRefCnt ) + { + (*pRefCnt) -= 1; + //printf("Decrementing ref-count to %d\n", *pRefCnt ); + if( (*pRefCnt) == 0 ) + { + delete pRefCnt; + delete pData; + pRefCnt = NULL; + pData = NULL; + } + } + } + + int32_t *pRefCnt; + T *pData; +}; + +#endif diff --git a/src/old/stack.cpp b/src/old/stack.cpp new file mode 100644 index 0000000..8d9565c --- /dev/null +++ b/src/old/stack.cpp @@ -0,0 +1,33 @@ +#include "stack.h" + +void Stack::push( void *data ) +{ + lStackData.append( data ); +} + +void *Stack::top() +{ + return lStackData.getAt( lStackData.getSize()-1 ); +} + +void Stack::pop() +{ + lStackData.deleteAt( lStackData.getSize()-1 ); +} + +void *Stack::poptop() +{ + void *dat = top(); + pop(); + return dat; +} + +bool Stack::isEmpty() +{ + return lStackData.isEmpty(); +} + +void Stack::empty() +{ + lStackData.empty(); +} diff --git a/src/old/stack.h b/src/old/stack.h new file mode 100644 index 0000000..30e2a19 --- /dev/null +++ b/src/old/stack.h @@ -0,0 +1,50 @@ +#ifndef STACK_H +#define STACK_H +#include "linkedlist.h" + +/** An ultra-simple stack implementation that just uses a linked list. + *@author Mike Buland + */ +class Stack +{ +public: + /** Pushes a new value onto the top of the stack. + *@param data A new value for the stack. + *@author Mike Buland + */ + void push( void *data ); + + /** Returns the top value off of the stack, but doesn't remove it from the + * stack. + *@returns The value at the top of the stack. + *@author Mike Buland + */ + void *top(); + + /** Pops the top item off of the stack. + *@author Mike Buland + */ + void pop(); + + /** Gets the top item off of the stack, pops it off the stack, and returns + * it. + *@returns The value at the top of the stack. + *@author Mike Buland + */ + void *poptop(); + + /** Checks if the stack is empty. + *@returns True if the stack is empty, and false if it has things in it. + *@author Mike Buland + */ + bool isEmpty(); + + /** Empty the stack. + *@author Mike Buland + */ + void empty(); + +private: + LinkedList lStackData; /**< The actual stack data. */ +}; +#endif diff --git a/src/old/staticstring.cpp b/src/old/staticstring.cpp new file mode 100644 index 0000000..60f130f --- /dev/null +++ b/src/old/staticstring.cpp @@ -0,0 +1,282 @@ +#include "staticstring.h" +#include "serializer.h" + +#include +#include +#include + +StaticString::StaticString() +{ + lpStr = NULL; + nLen = 0; +} + +StaticString::StaticString( int nLength ) +{ + lpStr = new char[nLength+1]; + nLen = nLength; + memset( lpStr, 0, nLength+1 ); +} + +StaticString::StaticString( const char *lpNewStr, int nNewLen ) +{ + lpStr = NULL; + nLen = 0; + setString( lpNewStr, nNewLen ); +} + +StaticString::StaticString( const char *lpNewStr ) +{ + lpStr = NULL; + nLen = 0; + setString( lpNewStr, -1 ); +} + +StaticString::StaticString( StaticString &xSrcStr, int nNewLen ) +{ + lpStr = NULL; + nLen = 0; + setString( xSrcStr, nNewLen ); +} + +StaticString::StaticString( StaticString &xSrcStr ) +{ + lpStr = NULL; + nLen = 0; + setString( xSrcStr, -1 ); +} + +StaticString::StaticString( const StaticString &xSrcStr ) +{ + nLen = xSrcStr.getLength(); + lpStr = new char[nLen]; + memcpy( lpStr, xSrcStr.getString(), nLen ); +} + +StaticString::~StaticString() +{ + if( lpStr != NULL ) delete[] lpStr; +} + +char *StaticString::getString() +{ + return lpStr; +} + +const char *StaticString::getString() const +{ + return lpStr; +} + +int StaticString::getLength() const +{ + return nLen; +} + +void StaticString::setLength( int nNewLength ) +{ + char *lpNewStr = new char[nNewLength+1]; + if( lpStr != NULL ) + { + strncpy( lpNewStr, lpStr, nNewLength ); + } + lpNewStr[nNewLength] = '\0'; + if( lpStr ) + { + delete[] lpStr; + } + lpStr = lpNewStr; + nLen = nNewLength; +} + +void StaticString::setString( const char *lpNewStr, int nNewLen ) +{ + if( lpStr ) + { + delete[] lpStr; + lpStr = NULL; + nLen = 0; + } + if( nNewLen < 0 ) + { + if( lpNewStr == NULL ) return; + nLen = strlen( lpNewStr ); + lpStr = new char[nLen+1]; + strcpy( lpStr, lpNewStr ); + } + else + { + nLen = nNewLen; + lpStr = new char[nLen+1]; + memset( lpStr, 0, nLen+1 ); + if( lpNewStr ) + strncpy( lpStr, lpNewStr, nNewLen ); + } +} + +void StaticString::setString( StaticString &sNewStr, int nNewLen ) +{ + if( lpStr ) + { + delete[] lpStr; + lpStr = NULL; + nLen = 0; + } + if( nNewLen < 0 ) + { + if( sNewStr.lpStr == NULL ) return; + nLen = sNewStr.nLen; + lpStr = new char[nLen+1]; + strcpy( lpStr, sNewStr.lpStr ); + } + else + { + nLen = nNewLen; + lpStr = new char[nLen+1]; + memset( lpStr, 0, nLen+1 ); + if( sNewStr.lpStr ) + strncpy( lpStr, sNewStr.lpStr, nNewLen ); + } +} + +StaticString &StaticString::operator=( StaticString &lpOtherStr ) +{ + setString( lpOtherStr ); + + return *this; +} + +StaticString &StaticString::operator=( std::string &lpOtherStr ) +{ + setString( lpOtherStr.c_str() ); + + return *this; +} + +StaticString &StaticString::operator=( const char *lpNewStr ) +{ + setString( lpNewStr ); + + return *this; +} + +StaticString::operator const char *() +{ + return lpStr; +} + +char StaticString::getAt( unsigned int nIndex ) +{ + if( nIndex < 0 || nIndex >= nLen ) + return '\0'; + + return lpStr[nIndex]; +} + +void StaticString::setAt( unsigned int nIndex, char cVal ) +{ + if( nIndex < 0 || nIndex >= nLen ) + return; + + lpStr[nIndex] = cVal; +} + +char &StaticString::operator[]( unsigned int nIndex ) +{ + if( nIndex < 0 || nIndex >= nLen ) + return lpStr[0]; + + return lpStr[nIndex]; +} + +StaticString::operator int() +{ + return nLen; +} + +char *StaticString::operator+( int nAmnt ) +{ + return lpStr + nAmnt; +} + +char *StaticString::operator-( int nAmnt ) +{ + return lpStr - nAmnt; +} + +void StaticString::clear() +{ + memset( lpStr, 0, nLen+1 ); +} + +void StaticString::serialize( Serializer &ar ) +{ + if( ar.isLoading() ) + { + ar >> nLen; + setLength( nLen ); + ar.read( lpStr, nLen ); + } + else + { + ar << nLen; + ar.write( lpStr, nLen ); + } +} + +bool StaticString::operator==( const char *str ) +{ + const char *a = str, *b = lpStr; + for(; *a == *b; a++, b++ ) if( *a == '\0' && *b == '\0' ) return true; + return false; +} + +bool StaticString::operator==( StaticString &str ) +{ + const char *a = str.lpStr, *b = lpStr; + for(; *a == *b; a++, b++ ) if( *a == '\0' && *b == '\0' ) return true; + return false; +} + +bool StaticString::operator!=( const char *str ) +{ + const char *a = str, *b = lpStr; + for(; *a == *b; a++, b++ ) if( *a == '\0' && *b == '\0' ) return false; + return true; +} + +bool StaticString::operator!=( StaticString &str ) +{ + const char *a = str.lpStr, *b = lpStr; + for(; *a == *b; a++, b++ ) if( *a == '\0' && *b == '\0' ) return false; + return true; +} +/* +unsigned long int StaticString::getHashCode() +{ + unsigned long int nPos = nLen; + unsigned long int j = 0; + for( const char *s = (const char *)lpStr; j< nLen; s++, j++ ) + { + nPos = *s + (nPos << 6) + (nPos << 16) - nPos; + } + return nPos; +} + +bool StaticString::compareForHash( Hashable &other ) +{ + if( ((StaticString &)other).nLen != nLen ) + return false; + + const char *a = ((StaticString &)other).lpStr; + const char *b = lpStr; + if( a == b ) + return true; + + for( unsigned long j = 0; j < nLen; j++, a++, b++ ) + if( *a != *b ) + return false; + + return true; +} +*/ diff --git a/src/old/staticstring.h b/src/old/staticstring.h new file mode 100644 index 0000000..4c3f7b8 --- /dev/null +++ b/src/old/staticstring.h @@ -0,0 +1,63 @@ +#ifndef STATIC_STRING_H +#define STATIC_STRING_H + +#include +#include "serializable.h" +//#include "hashable.h" + +/** + * Simple string managing class. Allows for dynamically allocated string data + * along with some minor caching to speed things up when accessing your + * string data. Really designed for making copies of strings easy and safe, + * and making accessing meta-info like length fast and reliable as well. + *@author Mike Buland + */ +class StaticString : public Serializable/*, public Hashable*/ +{ +public: + StaticString(); + StaticString( const char *lpNewStr, int nNewLen ); + StaticString( const char *lpNewStr ); + StaticString( StaticString &xSrcStr, int nNewLen ); + StaticString( StaticString &xSrcStr ); + StaticString( const StaticString &xSrcStr ); + StaticString( int nLength ); + virtual ~StaticString(); + + char *getString(); + const char *getString() const; + int getLength() const; + void setLength( int nNewLength ); + + void setString( const char *lpNewStr, int nNewLen=-1 ); + void setString( StaticString &sNewStr, int nNewLen=-1 ); + + char getAt( unsigned int nIndex ); + void setAt( unsigned int nIndex, char cVal ); + + class StaticString &operator=( class StaticString &lpOtherStr ); + class StaticString &operator=( std::string &lpOtherStr ); + class StaticString &operator=( const char *lpNewStr ); + operator const char *(); + char &operator[]( unsigned int nIndex ); + operator int(); + char *operator+( int nAmnt ); + char *operator-( int nAmnt ); + bool operator==( const char *str ); + bool operator==( StaticString &str ); + bool operator!=( const char *str ); + bool operator!=( StaticString &str ); + + void clear(); + + virtual void serialize( class Serializer &ar ); + + //virtual unsigned long int getHashCode(); + //virtual bool compareForHash( Hashable &other ); + +private: + char *lpStr; + uint32_t nLen; +}; + +#endif diff --git a/src/old/stream.cpp b/src/old/stream.cpp new file mode 100644 index 0000000..856a58d --- /dev/null +++ b/src/old/stream.cpp @@ -0,0 +1,10 @@ +#include "stream.h" + +Stream::Stream() +{ +} + +Stream::~Stream() +{ +} + diff --git a/src/old/stream.h b/src/old/stream.h new file mode 100644 index 0000000..e086e28 --- /dev/null +++ b/src/old/stream.h @@ -0,0 +1,27 @@ +#ifndef STREAM_H +#define STREAM_H + +#include +#include + +class Stream +{ +public: + Stream(); + virtual ~Stream(); + + virtual void close() = 0; + virtual size_t read( char *pBuf, size_t nBytes ) = 0; + virtual size_t write( const char *pBuf, size_t nBytes ) = 0; + + virtual long tell() = 0; + virtual void seek( long offset ) = 0; + virtual void setPos( long pos ) = 0; + virtual void setPosEnd( long pos ) = 0; + virtual bool isEOS() = 0; + +private: + +}; + +#endif diff --git a/src/old/stringrep.cpp b/src/old/stringrep.cpp new file mode 100644 index 0000000..a505f8a --- /dev/null +++ b/src/old/stringrep.cpp @@ -0,0 +1,19 @@ +#include "stringrep.h" + +int32_t stringlen( const char *s ) +{ + for( int32_t i = 0;; i++ ) + if( !s[i] ) + return i; +} + +char *stringdup( const char *s ) +{ + int len = stringlen( s ); + char *r = new char[len+1]; + for( int j = 0; j <= len; j++ ) + r[j] = s[j]; + + return r; +} + diff --git a/src/old/stringrep.h b/src/old/stringrep.h new file mode 100644 index 0000000..eaa4a12 --- /dev/null +++ b/src/old/stringrep.h @@ -0,0 +1,17 @@ +#ifndef STRING_REP_H +#define STRING_REP_H + +#include + +/** + * Calculates the length of a string. + */ +int32_t stringlen( const char *s ); + +/** + * Identicle to strdup, which isn't available everywhere, but uses c++ memory + * facilities. Remember to use delete[] when freeing the returned string. + */ +char *stringdup( const char *s ); + +#endif diff --git a/src/old/tests/clistress.cpp b/src/old/tests/clistress.cpp new file mode 100644 index 0000000..6b0ac66 --- /dev/null +++ b/src/old/tests/clistress.cpp @@ -0,0 +1,20 @@ +#include "connection.h" + +int main() +{ + Connection c; + + c.open("localhost", 4001 ); + + c.appendOutput("w"); + c.writeOutput(); + + c.waitForInput( 6, 5, 0 ); + + printf("read: %s\n", c.getInput() ); + + c.close(); + + return 0; +} + diff --git a/src/old/tests/confpair.cpp b/src/old/tests/confpair.cpp new file mode 100644 index 0000000..fb1b0d3 --- /dev/null +++ b/src/old/tests/confpair.cpp @@ -0,0 +1,19 @@ +#include "confpair.h" +#include + +using namespace std; + +int main() +{ + ConfPair p1("DebugMode"); + p1.value() = 12; + cout << p1.value() << "\n"; + p1.value() = 55; + cout << p1.value() << "\n"; + + ConfPairBase &p = p1; + + p = "33.12"; + cout << p.getAsString(); +} + diff --git a/src/old/tests/connect.cpp b/src/old/tests/connect.cpp new file mode 100644 index 0000000..a9fca64 --- /dev/null +++ b/src/old/tests/connect.cpp @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include "connection.h" + +int main() +{ + Connection c; + c.open("127.0.0.1", 12457 ); + + { + int newSocket = c.getSocket(); + int flags; + + flags = fcntl(newSocket, F_GETFL, 0); + flags |= O_NONBLOCK; + if (fcntl(newSocket, F_SETFL, flags) < 0) + { + return false; + } + } + + for( int i = 0; i < 50; i++ ) + { + usleep( 100000 ); + int nbytes = c.readInput(); + if( nbytes == 0 ) + printf("0 bytes, EOF?\n"); + else + printf("Got %d bytes, whacky...\n", nbytes ); + } + + c.close(); + + return 0; +} + diff --git a/src/old/tests/exception.cpp b/src/old/tests/exception.cpp new file mode 100644 index 0000000..6417692 --- /dev/null +++ b/src/old/tests/exception.cpp @@ -0,0 +1,16 @@ +#include +#include "exceptions.h" + +int main() +{ + try + { + throw ExceptionBase( 42, "There was an error on line: %d", __LINE__ ); + } + catch( ExceptionBase &e ) + { + std::cout << "Error "<< e.getErrorCode() << ": " << e.what() << "\n"; + } + + throw ExceptionBase( 112, "This exception wasn't caught!"); +} diff --git a/src/old/tests/formula.cpp b/src/old/tests/formula.cpp new file mode 100644 index 0000000..976b039 --- /dev/null +++ b/src/old/tests/formula.cpp @@ -0,0 +1,13 @@ +#include "formula.h" + +int main( int argc, char *argv[] ) +{ + if( argc < 2 ) return 0; + + Formula f; + double dOut = f.run( argv[1] ); + printf("%s = %f\n", argv[1], dOut ); + + return 0; +} + diff --git a/src/old/tests/fstring.cpp b/src/old/tests/fstring.cpp new file mode 100644 index 0000000..271738c --- /dev/null +++ b/src/old/tests/fstring.cpp @@ -0,0 +1,48 @@ +#include "hash.h" +#include "fstring.h" + +FString genThing() +{ + FString bob; + bob.append("ab "); + bob += "cd "; + bob += "efg"; + + printf("---bob------\n%08X: %s\n", (unsigned int)bob.c_str(), bob.c_str() ); + return bob; +} + +void thing( FString str ) +{ + printf("Hey: %s\n", str.c_str() ); +} + +#define pem printf("---------\n%08X: %s\n%08X: %s\n", (unsigned int)str.c_str(), str.c_str(), (unsigned int)str2.c_str(), str2.c_str() ); +int main( int argc, char *argv ) +{ + FString str("th"); + + str.prepend("Hello "); + str.append("ere."); + + FString str2( str ); + pem; + str += " What's up?"; + pem; + str2 += " How are you?"; + pem; + str = str2; + pem; + + str2 = genThing(); + pem; + + str = str2; + pem; + + thing( str2 ); + thing("test."); + + printf("%d == %d\n", __calcHashCode( str ), __calcHashCode( str.c_str() ) ); +} + diff --git a/src/old/tests/hash.cpp b/src/old/tests/hash.cpp new file mode 100644 index 0000000..2fc6968 --- /dev/null +++ b/src/old/tests/hash.cpp @@ -0,0 +1,116 @@ +#include "hash.h" +#include "staticstring.h" + +int main() +{ + const char *names[]={ + "Homer the Great", + "And Maggie Makes Three", + "Bart's Comet", + "Homie The Clown", + "Bart Vs Australia", + "Homer vs Patty and Selma", + "A star is burns", + "Lisa's Wedding", + "Two Dozen and One Greyhounds", + "The PTA Disbands", + "Round Springfield", + "The Springfield connection", + "Lemon of Troy", + "Who Shot Mr. Burns (Pt. 1)", + "Who Shot Mr. Burns (pt. 2)", + "Radioactive Man", + "Home Sweet Homediddly-dum-doodly", + "Bart Sells His Soul", + "Lisa the Vegetarian", + "Treehouse of horror VI", + "King Size Homer", + "Mother Simpson", + "Sideshow Bob's Last Gleaming", + "The Simpson's 138th Show Spectacular", + "Marge Be Not Proud", + "Team Homer", + "Two Bad Neighbors", + "Scenes From the Class Struggle in Springfield", + "Bart the Fink", + "Lisa the Iconoclast", + "Homer the Smithers", + "The Day the Violence Died", + "A Fish Called Selma", + "Bart on the road", + "22 Short Films about Springfield", + "The Curse of the Flying Hellfish", + "Much Apu about Nothing", + "Homerpalooza", + "The Summer of 4 Ft 2", + "Treehouse of Horror VII", + "You Only Move Twice", + "The Homer They Fall", + "Burns Baby Burns", + "Bart After Dark", + "A Millhouse Divided", + "Lisas Date With Destiny", + "Hurricane Neddy", + "The Mysterious Voyage of Our Homer", + "The Springfield Files", + "The Twisted World of Marge Simpson", + "Mountain of Madness", + NULL + }; + + Hash sTest; + + printf("Inserting\n-------------------\n\n"); + for( int j = 0; j < 33; j++ ) + { + sTest[names[j]] = j; + } + + printf("Test1: %d, Test2: %d\n", sTest.has("Lemon of Troy"), sTest.has(std::string("Lemon of Troy").c_str() ) ); + + sTest.has(std::string("Lemon of Troy").c_str() ); + + printf("Getting\n-------------------\n\n"); + + sTest.erase("Homer the Great"); + sTest["Bart's Comet"].erase(); + + for( Hash::iterator i = sTest.begin(); + i != sTest.end(); i++ ) + { + Hash::iterator j = i; + printf("%d: %s\n", (*j).second, (*j).first ); + } + + printf("Testing\n-------------------\n\n"); + for( int j = 0; j < 33; j++ ) + { + if( sTest.has(names[j]) ) + { + if( sTest[names[j]] != j ) + { + printf("'%s' should be %d, is %d\n", + names[j], j, + sTest[names[j]].value() + ); + } + } + else + { + printf("Missing element %d, '%s'\n", j, names[j] ); + } + } + + printf("Clearing\n-------------------\n\n"); + + sTest.clear(); + + for( Hash::iterator i = sTest.begin(); + i != sTest.end(); i++ ) + { + Hash::iterator j = i; + printf("%d: %s\n", (*j).second, (*j).first ); + } + +} + diff --git a/src/old/tests/hashtest.cpp b/src/old/tests/hashtest.cpp new file mode 100644 index 0000000..eaa84a0 --- /dev/null +++ b/src/old/tests/hashtest.cpp @@ -0,0 +1,107 @@ +#include +#include +#include "hashtable.h" +#include "hashfunctioncasestring.h" + +int main() +{ + const char *names[]={ + "Homer the Great", + "And Maggie Makes Three", + "Bart's Comet", + "Homie The Clown", + "Bart Vs Australia", + "Homer vs Patty and Selma", + "A star is burns", + "Lisa's Wedding", + "Two Dozen and One Greyhounds", + "The PTA Disbands", + "Round Springfield", + "The Springfield connection", + "Lemon of Troy", + "Who Shot Mr. Burns (Pt. 1)", + "Who Shot Mr. Burns (pt. 2)", + "Radioactive Man", + "Home Sweet Homediddly-dum-doodly", + "Bart Sells His Soul", + "Lisa the Vegetarian", + "Treehouse of horror VI", + "King Size Homer", + "Mother Simpson", + "Sideshow Bob's Last Gleaming", + "The Simpson's 138th Show Spectacular", + "Marge Be Not Proud", + "Team Homer", + "Two Bad Neighbors", + "Scenes From the Class Struggle in Springfield", + "Bart the Fink", + "Lisa the Iconoclast", + "Homer the Smithers", + "The Day the Violence Died", + "A Fish Called Selma", + "Bart on the road", + "22 Short Films about Springfield", + "The Curse of the Flying Hellfish", + "Much Apu about Nothing", + "Homerpalooza", + "The Summer of 4 Ft 2", + "Treehouse of Horror VII", + "You Only Move Twice", + "The Homer They Fall", + "Burns Baby Burns", + "Bart After Dark", + "A Millhouse Divided", + "Lisas Date With Destiny", + "Hurricane Neddy", + "The Mysterious Voyage of Our Homer", + "The Springfield Files", + "The Twisted World of Marge Simpson", + "Mountain of Madness", + NULL + }; + + HashTable h( new HashFunctionCaseString(), 5, false ); + + int j; + printf("Inserting...\n"); + for( j = 0; j < 10; j++ ) + { + h.insert( names[j], (void *)(j+1) ); + h.insert( names[j], (void *)(j+1) ); + printf("Capacity: %lu, Size: %lu, Load: %f\n", + h.getCapacity(), + h.getSize(), + h.getLoad() + ); + } + + for( j = 0; j < 10; j++ ) + { + printf("\"%s\" = %d\n", names[j], (int)h[names[j]] ); + } + + printf("\nDeleting some...\n"); + + for( int k = 0; k < 7; k++ ) + { + h.del( names[k] ); + //h.insert( names[j], (void *)(j+1) ); + printf("Capacity: %lu, Size: %lu, Load: %f\n", + h.getCapacity(), + h.getSize(), + h.getLoad() + ); + } + + printf("\nInserting more...\n"); + + for( ; names[j] != NULL; j++ ) + { + h.insert( names[j], (void *)(j+1) ); + printf("Capacity: %lu, Size: %lu, Load: %f\n", + h.getCapacity(), + h.getSize(), + h.getLoad() + ); + } +} diff --git a/src/old/tests/hashtest2.cpp b/src/old/tests/hashtest2.cpp new file mode 100644 index 0000000..74700fd --- /dev/null +++ b/src/old/tests/hashtest2.cpp @@ -0,0 +1,15 @@ +#include "hash.h" +#include + +int main() +{ + char *a, *b; + a = new char[10]; + b = new char[10]; + strcpy( a, "Hey there"); + strcpy( b, "Hey there"); + printf("Same: %s\n", __cmpHashKeys( a, b )?"yes":"no"); + + return 0; +} + diff --git a/src/old/tests/httpsrv/httpconnectionmonitor.cpp b/src/old/tests/httpsrv/httpconnectionmonitor.cpp new file mode 100644 index 0000000..51d82f3 --- /dev/null +++ b/src/old/tests/httpsrv/httpconnectionmonitor.cpp @@ -0,0 +1,88 @@ +#include "httpconnectionmonitor.h" +#include "http.h" +#include "exceptions.h" +#include + +HttpConnectionMonitor::HttpConnectionMonitor() +{ +} + +HttpConnectionMonitor::~HttpConnectionMonitor() +{ +} + +bool HttpConnectionMonitor::onNewConnection( Connection *pCon, int nPort ) +{ + printf("Got connection on port %d\n", nPort ); + + try + { + pCon->readInput( 60, 0 ); + printf("#######################\n%s\n#######################\n", pCon->getInput() ); + + Http hp( pCon ); + while( hp.parseRequest() == false ); + printf("Done parsing.\n\n"); + + if( hp.getRequestType() == Http::reqGet ) + { + printf("\"\"\"%s\"\"\"\n", hp.getRequestURI() ); + if( !strcmp( hp.getRequestURI(), "/" ) ) + { + std::string content("Server Test</test></head><body>This is a test of a new system where all the pages will be more or less dynamic...<br>If you want to try to login, you can do that here:<br><form method=\"post\" action=\"showvars\" enctype=\"multipart/form-data\">Name: <input type=\"text\" name=\"name\"><br>Password: <input type=\"password\" name=\"pass\"><br><input type=\"submit\" name=\"action\" value=\"login\"></form></body></html>"); + hp.buildResponse(); + hp.setResponseContent( + "text/html", + content.c_str(), + content.size() + ); + hp.sendResponse(); + } + else + { + std::string content("<html><head><title>URL Not Found</test></head><body>There is no content mapped to the URL you requested. Please try another one.</body></html>"); + hp.buildResponse( 404, "File not found."); + hp.setResponseContent( + "text/html", + content.c_str(), + content.size() + ); + hp.sendResponse(); + } + } + else + { + printf("Non get: %s\n", hp.getRequestTypeStr() ); + pCon->appendOutput("HTTP/1.1 100 Continue\r\n\r\n"); + } + pCon->writeOutput(); + //for( int j = 0; j < 50; j++ ) + { + pCon->readInput( 1, 0 ); + //printf("Size so far: %d\n", pCon->getInputAmnt() ); + } + + if( pCon->hasInput() ) + { + std::string s( pCon->getInput(), pCon->getInputAmnt() ); + + pCon->printInputDebug(); + //printf("Reamining data\n==============\n%s\n==============\n", + // s.c_str() ); + } + + pCon->disconnect(); + } + catch( ConnectionException &e ) + { + printf("Connection: %s\n", e.what() ); + } + + return true; +} + +bool HttpConnectionMonitor::onClosedConnection( Connection *pCon ) +{ + return true; +} + diff --git a/src/old/tests/httpsrv/httpconnectionmonitor.h b/src/old/tests/httpsrv/httpconnectionmonitor.h new file mode 100644 index 0000000..30c0afd --- /dev/null +++ b/src/old/tests/httpsrv/httpconnectionmonitor.h @@ -0,0 +1,16 @@ +#ifndef HTTPCONNECTIONMONITOR_H +#define HTTPCONNECTIONMONITOR_H + +#include "connectionmonitor.h" + +class HttpConnectionMonitor : public ConnectionMonitor +{ +public: + HttpConnectionMonitor(); + ~HttpConnectionMonitor(); + + bool onNewConnection( Connection *pCon, int nPort ); + bool onClosedConnection( Connection *pCon ); +}; + +#endif diff --git a/src/old/tests/httpsrv/main.cpp b/src/old/tests/httpsrv/main.cpp new file mode 100644 index 0000000..2f1563c --- /dev/null +++ b/src/old/tests/httpsrv/main.cpp @@ -0,0 +1,22 @@ +#include "connectionmanager.h" +#include "httpconnectionmonitor.h" + +int main() +{ + printf("Starting server...\n"); + + ConnectionManager srv; + HttpConnectionMonitor http; + + srv.setConnectionMonitor( &http ); + + printf("Listening on port 7331\n"); + srv.startServer( 7331 ); + + for(;;) + { + srv.scanConnections( 5000, false ); + } + + return 0; +} diff --git a/src/old/tests/log.cpp b/src/old/tests/log.cpp new file mode 100644 index 0000000..d7cfa0b --- /dev/null +++ b/src/old/tests/log.cpp @@ -0,0 +1,29 @@ +#include <stdio.h> +#include <stdlib.h> +#include <iostream> +#include "multilog.h" +#include "multilogtext.h" + +class Test +{ +public: + Test() + { + MultiLineLog( 4, "Test init'd\n"); + } +}; + +int main() +{ + MultiLog &xLog = MultiLog::getInstance(); + + xLog.LineLog( 2, "Hello again"); + + MultiLog::getInstance().addChannel( + new MultiLogText( STDOUT_FILENO, "%02y-%02m-%02d %02h:%02M:%02s: %t" ) + ); + + MultiLineLog( MultiLog::LError, "Hi there!"); + Test t; +} + diff --git a/src/old/tests/md5test.cpp b/src/old/tests/md5test.cpp new file mode 100644 index 0000000..6f832df --- /dev/null +++ b/src/old/tests/md5test.cpp @@ -0,0 +1,19 @@ +#include <stdio.h> +#include <string.h> +#include "md5.h" + +int main() +{ + md5 mproc; + md5sum sum; + char hexstr[33]; + + memset( hexstr, 0, 33 ); + + mproc.sumString( &sum, "qwertyuiopasdfgh" ); + mproc.sumToHex( &sum, hexstr ); + printf("sum: %s\n", hexstr ); + printf("chk: 1ebfc043d8880b758b13ddc8aa1638ef\n"); + + return 0; +} diff --git a/src/old/tests/ordhash.cpp b/src/old/tests/ordhash.cpp new file mode 100644 index 0000000..f1d96ec --- /dev/null +++ b/src/old/tests/ordhash.cpp @@ -0,0 +1,48 @@ +#include "ordhash.h" +#include <string> + +typedef struct eldef +{ + eldef( int a, int b, const std::string &c ) : + id( a ), nSequence( b ), sName( c ) {} + int id; + int nSequence; + std::string sName; +} eldef; + +struct seqcmp +{ + bool operator()( eldef **a, eldef **b ) + { + return (*a)->nSequence < (*b)->nSequence; + } +}; + +struct namcmp +{ + bool operator()( eldef **a, eldef **b ) + { + return (*a)->sName < (*b)->sName; + } +}; + +typedef OrdHash<int, eldef, seqcmp> AHash; +//typedef OrdHash<int, eldef, namcmp> AHash; + +int main() +{ + AHash hsh; + hsh[1] = eldef( 0, 43, "Bob"); + hsh[4] = eldef( 1, 443, "Abby"); + hsh[2] = eldef( 2, 1, "Name"); + hsh[5] = eldef( 3, 0, "Catagory"); + hsh[32] = eldef( 4, 12, "Epilogue"); + + for( AHash::iterator i = hsh.begin(); i != hsh.end(); i++ ) + { + eldef e = (*i).second; + printf("%d, %d, %s\n", e.id, e.nSequence, e.sName.c_str() ); + } + +} + diff --git a/src/old/tests/param.cpp b/src/old/tests/param.cpp new file mode 100644 index 0000000..a4d2824 --- /dev/null +++ b/src/old/tests/param.cpp @@ -0,0 +1,46 @@ +#include "param.h" +#include <stdio.h> + +Param::Param() +{ + addHelpBanner("param - A test of the libbu++ parameter systems\n" + "Enjoy with care and caution\n\nTest stuff:\n"); + addParam( "name", 's', mkproc( Param::printStuff ), &str, "Test a param param" ); + //addParam( "name", &str ); + addParam( "job", 'U', mkproc( Param::printStuff ), "Test a paramless param" ); + + addHelpBanner("\nInformational:\n"); + addParam( "help", mkproc( ParamProc::help ), "Help!" ); + + addHelpBanner("\nThanks for trying my test!\n\n"); +} + +Param::~Param() +{ +} + +int Param::printStuff( int argc, char *argv[] ) +{ + printf("------------%02d-------------\n", argc ); + for( int j = 0; j < argc; j++ ) + { + printf("%d: %s\n", j, argv[j] ); + } + printf("---------------------------\n" ); + printf("SETVAR===\"%s\"\n", str.c_str() ); + + return 1; +} + +int main( int argc, char *argv[] ) +{ + if( argc == 1 ) + { + printf("You have to enter some parameter, try '--help'\n\n"); + return 0; + } + + Param p; + p.process( argc, argv ); +} + diff --git a/src/old/tests/param.h b/src/old/tests/param.h new file mode 100644 index 0000000..2756b69 --- /dev/null +++ b/src/old/tests/param.h @@ -0,0 +1,21 @@ +#ifndef PARAM_H +#define PARAM_H + +#include <stdint.h> + +#include "paramproc.h" + +class Param : public ParamProc +{ +public: + Param(); + virtual ~Param(); + +private: + int printStuff( int argc, char *argv[] ); + + std::string str; + uint32_t uint32; +}; + +#endif diff --git a/src/old/tests/plugin/main.cpp b/src/old/tests/plugin/main.cpp new file mode 100644 index 0000000..51c8390 --- /dev/null +++ b/src/old/tests/plugin/main.cpp @@ -0,0 +1,14 @@ +#include "plugger.h" +#include "plugin.h" + +int main() +{ + Plugger<Plugin> p; + + p.registerExternalPlugin( "./guy.so", "Guy" ); + + Plugin *t = p.instantiate( "Guy" ); + + p.destroy( t ); +} + diff --git a/src/old/tests/plugin/plugin.cpp b/src/old/tests/plugin/plugin.cpp new file mode 100644 index 0000000..ea558fd --- /dev/null +++ b/src/old/tests/plugin/plugin.cpp @@ -0,0 +1,10 @@ +#include "plugin.h" + +Plugin::Plugin() +{ +} + +Plugin::~Plugin() +{ +} + diff --git a/src/old/tests/plugin/plugin.h b/src/old/tests/plugin/plugin.h new file mode 100644 index 0000000..f726867 --- /dev/null +++ b/src/old/tests/plugin/plugin.h @@ -0,0 +1,14 @@ +#ifndef PLUGIN_H +#define PLUGIN_H + +class Plugin +{ +public: + Plugin(); + virtual ~Plugin(); + +private: + +}; + +#endif diff --git a/src/old/tests/qsort.cpp b/src/old/tests/qsort.cpp new file mode 100644 index 0000000..28c6f03 --- /dev/null +++ b/src/old/tests/qsort.cpp @@ -0,0 +1,228 @@ +#define _QSORT_SWAP(a, b, t) ((void)((t = *a), (*a = *b), (*b = t))) + +/* Discontinue quicksort algorithm when partition gets below this size. + This particular magic number was chosen to work best on a Sun 4/260. */ +#define _QSORT_MAX_THRESH 4 + +/* Stack node declarations used to store unfulfilled partition obligations + * (inlined in QSORT). +typedef struct { + QSORT_TYPE *_lo, *_hi; +} qsort_stack_node; + */ + +/* The next 4 #defines implement a very fast in-line stack abstraction. */ +/* The stack needs log (total_elements) entries (we could even subtract + log(MAX_THRESH)). Since total_elements has type unsigned, we get as + upper bound for log (total_elements): + bits per byte (CHAR_BIT) * sizeof(unsigned). */ +#define _QSORT_STACK_SIZE (8 * sizeof(unsigned)) +#define _QSORT_PUSH(top, low, high) \ + (((top->_lo = (low)), (top->_hi = (high)), ++top)) +#define _QSORT_POP(low, high, top) \ + ((--top, (low = top->_lo), (high = top->_hi))) +#define _QSORT_STACK_NOT_EMPTY (_stack < _top) + + +/* Order size using quicksort. This implementation incorporates + four optimizations discussed in Sedgewick: + + 1. Non-recursive, using an explicit stack of pointer that store the + next array partition to sort. To save time, this maximum amount + of space required to store an array of SIZE_MAX is allocated on the + stack. Assuming a 32-bit (64 bit) integer for size_t, this needs + only 32 * sizeof(stack_node) == 256 bytes (for 64 bit: 1024 bytes). + Pretty cheap, actually. + + 2. Chose the pivot element using a median-of-three decision tree. + This reduces the probability of selecting a bad pivot value and + eliminates certain extraneous comparisons. + + 3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving + insertion sort to order the MAX_THRESH items within each partition. + This is a big win, since insertion sort is faster for small, mostly + sorted array segments. + + 4. The larger of the two sub-partitions is always pushed onto the + stack first, with the algorithm then concentrating on the + smaller partition. This *guarantees* no more than log (total_elems) + stack size is needed (actually O(1) in this case)! */ + +/* The main code starts here... */ + +template<typename QSORT_TYPE, typename QSORT_LTT> +void qsrt( QSORT_TYPE *QSORT_BASE, int QSORT_NELT ) +{ + QSORT_LTT QSORT_LT; + QSORT_TYPE *const _base = (QSORT_BASE); + const unsigned _elems = (QSORT_NELT); + QSORT_TYPE _hold; + + /* Don't declare two variables of type QSORT_TYPE in a single + * statement: eg `TYPE a, b;', in case if TYPE is a pointer, + * expands to `type* a, b;' wich isn't what we want. + */ + + if (_elems > _QSORT_MAX_THRESH) { + QSORT_TYPE *_lo = _base; + QSORT_TYPE *_hi = _lo + _elems - 1; + struct { + QSORT_TYPE *_hi; QSORT_TYPE *_lo; + } _stack[_QSORT_STACK_SIZE], *_top = _stack + 1; + + while (_QSORT_STACK_NOT_EMPTY) { + QSORT_TYPE *_left_ptr; QSORT_TYPE *_right_ptr; + + /* Select median value from among LO, MID, and HI. Rearrange + LO and HI so the three values are sorted. This lowers the + probability of picking a pathological pivot value and + skips a comparison for both the LEFT_PTR and RIGHT_PTR in + the while loops. */ + + QSORT_TYPE *_mid = _lo + ((_hi - _lo) >> 1); + + if (QSORT_LT (_mid, _lo)) + _QSORT_SWAP (_mid, _lo, _hold); + if (QSORT_LT (_hi, _mid)) + _QSORT_SWAP (_mid, _hi, _hold); + else + goto _jump_over; + if (QSORT_LT (_mid, _lo)) + _QSORT_SWAP (_mid, _lo, _hold); + _jump_over:; + + _left_ptr = _lo + 1; + _right_ptr = _hi - 1; + + /* Here's the famous ``collapse the walls'' section of quicksort. + Gotta like those tight inner loops! They are the main reason + that this algorithm runs much faster than others. */ + do { + while (QSORT_LT (_left_ptr, _mid)) + ++_left_ptr; + + while (QSORT_LT (_mid, _right_ptr)) + --_right_ptr; + + if (_left_ptr < _right_ptr) { + _QSORT_SWAP (_left_ptr, _right_ptr, _hold); + if (_mid == _left_ptr) + _mid = _right_ptr; + else if (_mid == _right_ptr) + _mid = _left_ptr; + ++_left_ptr; + --_right_ptr; + } + else if (_left_ptr == _right_ptr) { + ++_left_ptr; + --_right_ptr; + break; + } + } while (_left_ptr <= _right_ptr); + + /* Set up pointers for next iteration. First determine whether + left and right partitions are below the threshold size. If so, + ignore one or both. Otherwise, push the larger partition's + bounds on the stack and continue sorting the smaller one. */ + + if (_right_ptr - _lo <= _QSORT_MAX_THRESH) { + if (_hi - _left_ptr <= _QSORT_MAX_THRESH) + /* Ignore both small partitions. */ + _QSORT_POP (_lo, _hi, _top); + else + /* Ignore small left partition. */ + _lo = _left_ptr; + } + else if (_hi - _left_ptr <= _QSORT_MAX_THRESH) + /* Ignore small right partition. */ + _hi = _right_ptr; + else if (_right_ptr - _lo > _hi - _left_ptr) { + /* Push larger left partition indices. */ + _QSORT_PUSH (_top, _lo, _right_ptr); + _lo = _left_ptr; + } + else { + /* Push larger right partition indices. */ + _QSORT_PUSH (_top, _left_ptr, _hi); + _hi = _right_ptr; + } + } + } + + /* Once the BASE array is partially sorted by quicksort the rest + is completely sorted using insertion sort, since this is efficient + for partitions below MAX_THRESH size. BASE points to the + beginning of the array to sort, and END_PTR points at the very + last element in the array (*not* one beyond it!). */ + + { + QSORT_TYPE *const _end_ptr = _base + _elems - 1; + QSORT_TYPE *_tmp_ptr = _base; + register QSORT_TYPE *_run_ptr; + QSORT_TYPE *_thresh; + + _thresh = _base + _QSORT_MAX_THRESH; + if (_thresh > _end_ptr) + _thresh = _end_ptr; + + /* Find smallest element in first threshold and place it at the + array's beginning. This is the smallest array element, + and the operation speeds up insertion sort's inner loop. */ + + for (_run_ptr = _tmp_ptr + 1; _run_ptr <= _thresh; ++_run_ptr) + if (QSORT_LT (_run_ptr, _tmp_ptr)) + _tmp_ptr = _run_ptr; + + if (_tmp_ptr != _base) + _QSORT_SWAP (_tmp_ptr, _base, _hold); + + /* Insertion sort, running from left-hand-side + * up to right-hand-side. */ + + _run_ptr = _base + 1; + while (++_run_ptr <= _end_ptr) { + _tmp_ptr = _run_ptr - 1; + while (QSORT_LT (_run_ptr, _tmp_ptr)) + --_tmp_ptr; + + ++_tmp_ptr; + if (_tmp_ptr != _run_ptr) { + QSORT_TYPE *_trav = _run_ptr + 1; + while (--_trav >= _run_ptr) { + QSORT_TYPE *_hi; QSORT_TYPE *_lo; + _hold = *_trav; + + for (_hi = _lo = _trav; --_lo >= _tmp_ptr; _hi = _lo) + *_hi = *_lo; + *_hi = _hold; + } + } + } + } + +} + + +struct cc +{ + bool operator()( int *a, int *b ) + { + return *a < *b; + } +}; + +#include <stdio.h> + +int main() +{ + int lst[] = { 43, 1, 342, 12, 491, 32, 12321, 32, 3, -3 }; + + for( int j = 0; j < 10; j++ ) + printf("%s%d", (j>0)?", ":"", lst[j] ); + printf("\n"); + qsrt<int, cc>( lst, 10 ); + for( int j = 0; j < 10; j++ ) + printf("%s%d", (j>0)?", ":"", lst[j] ); + printf("\n"); +} + diff --git a/src/old/tests/sbuffer.cpp b/src/old/tests/sbuffer.cpp new file mode 100644 index 0000000..02798cb --- /dev/null +++ b/src/old/tests/sbuffer.cpp @@ -0,0 +1,27 @@ +#include "sbuffer.h" + +int main() +{ + SBuffer buf; + + buf.write("abcdefg", 7 ); + + printf("tell: %ld\n", buf.tell() ); + + char abuf[6]; + int nRead; + nRead = buf.read( abuf, 5 ); + abuf[nRead] = '\0'; + printf("Read %d bytes \"%s\"\n", nRead, abuf ); + + buf.setPos( 0 ); + nRead = buf.read( abuf, 5 ); + abuf[nRead] = '\0'; + printf("Read %d bytes \"%s\"\n", nRead, abuf ); + + nRead = buf.read( abuf, 5 ); + abuf[nRead] = '\0'; + printf("Read %d bytes \"%s\"\n", nRead, abuf ); + +} + diff --git a/src/old/tests/serialize.cpp b/src/old/tests/serialize.cpp new file mode 100644 index 0000000..e233704 --- /dev/null +++ b/src/old/tests/serialize.cpp @@ -0,0 +1,30 @@ +#include "serializerbinary.h" +#include "staticstring.h" +#include <stdio.h> +#include <string> + +int main() +{ + int32_t one; + double two; + bool three; + StaticString s("Test string!"); + std::string ss("Another test string"); + SerializerBinary ar("hello.dat", false); + ar << (int)85; + ar << (double)2.63434; + ar << false; + ar << ss; + ar.close(); + + one = 0; two = 0; three = true; s = "die"; + + SerializerBinary ar2("hello.dat", true); + ar2 >> one; + ar2 >> two; + ar2 >> three; + ar2 >> s; + + printf("we got %d - %f - %s - \"%s\"\n", one, two, (three ? "true":"false"), s.getString() ); + return 0; +} diff --git a/src/old/tests/serializetext.cpp b/src/old/tests/serializetext.cpp new file mode 100644 index 0000000..f6be7d3 --- /dev/null +++ b/src/old/tests/serializetext.cpp @@ -0,0 +1,28 @@ +#include "serializertext.h" +#include "staticstring.h" +#include <iostream> + +int main() +{ + StaticString s("You're a dog!!"); + SerializerText ar("hello.dat", false); + + ar << 4 << 3.993 << true << s; + + ar.close(); + + int one=0;float two=0.0;bool three=false; s = ""; + + SerializerText ar2("hello.dat", true); + + ar2 >> one; + ar2 >> two; + ar2 >> three; + ar2 >> s; + + //printf("out: %d, %f, %s, \"%s\"\n", one, two, (three ? "true" : "false"), s.getString()); + std::cout << one << ", " << two << ", " << three << ", " << s.getString() << "\n"; + + return 0; +} + diff --git a/src/old/tests/sha1.cpp b/src/old/tests/sha1.cpp new file mode 100644 index 0000000..df3113c --- /dev/null +++ b/src/old/tests/sha1.cpp @@ -0,0 +1,44 @@ +#include "sha1.h" +#include "sfile.h" + +#define BS 1024 + +int main( int argc, char *argv[] ) +{ + argc--; argv++; + + if( argc == 0 ) + { + printf("Provide a filename.\n"); + return 0; + } + + char buf[BS]; + + Sha1 s; + SFile fin( *argv, "rb" ); + for(;;) + { + int nRead = fin.read( buf, BS ); + if( nRead == 0 ) + break; + + s.update( buf, nRead ); + if( nRead < BS ) + break; + } + + unsigned char *dig = s.getDigest(); + + char val[]={"0123456789ABCDEF"}; + + for( int j = 0; j < 20; j++ ) + { + putchar( val[dig[j]>>4] ); + putchar( val[dig[j]&0x0F] ); + } + putchar('\n'); + + delete[] dig; +} + diff --git a/src/old/tests/sptr.cpp b/src/old/tests/sptr.cpp new file mode 100644 index 0000000..38d3675 --- /dev/null +++ b/src/old/tests/sptr.cpp @@ -0,0 +1,55 @@ +#include <stdio.h> +#include "sptr.h" + +class Annoy +{ +public: + Annoy() : nCnt( 0 ) + { + printf("Created.\n"); + } + + ~Annoy() + { + printf("Destroyed.\n"); + } + + void go() + { + printf("%d: I'm annoying.\n", ++nCnt); + } + + int nCnt; +}; + +void beAnnoying( SPtr<Annoy> bob ) +{ + printf("bob-Count: %d\n", bob.count() ); + bob->go(); +} + +int main() +{ + SPtr<Annoy> pt( new Annoy ); + printf("Count: %d\n", pt.count() ); + pt->go(); + + { + SPtr<Annoy> pt2 = pt; + printf("Count: %d\n", pt2.count() ); + + pt2->go(); + + { + SPtr<Annoy> pt3( pt2 ); + printf("Count: %d\n", pt3.count() ); + + pt3->go(); + + beAnnoying( pt3 ); + } + printf("Count: %d\n", pt.count() ); + } + printf("Count: %d\n", pt.count() ); +} + diff --git a/src/old/tests/srvstress.cpp b/src/old/tests/srvstress.cpp new file mode 100644 index 0000000..d9a9a1c --- /dev/null +++ b/src/old/tests/srvstress.cpp @@ -0,0 +1,91 @@ +#include "connectionmanager.h" +#include "programlink.h" +#include "linkedlist.h" +#include "protocol.h" + +class StressProtocol : public Protocol +{ +public: + bool onNewData() + { + switch( getConnection()->getInput()[0] ) + { + case 'd': + throw "Hello"; + break; + + case 'w': + getConnection()->appendOutput("Hello"); + break; + }; + + return true; + } + + bool onNewConnection() + { + return true; + } +}; + +class StressMonitor : public ConnectionMonitor, public ProgramLink +{ +public: + bool init() + { + return true; + } + + bool deInit() + { + return true; + } + + bool timeSlice() + { + return true; + } + + bool onNewConnection( Connection *pCon, int nPort ) + { + StressProtocol *sp = new StressProtocol(); + pCon->setProtocol( sp ); + + printf(" sys: New connection: socket(%d), port(%d)\n", + pCon->getSocket(), nPort ); + + return true; + } + + bool onClosedConnection( Connection *pCon ) + { + printf(" sys: Closed connection: socket(%d)\n", + pCon->getSocket() ); + + return true; + } + + LinkMessage *processIRM( LinkMessage *pMsg ) + { + return NULL; + } +}; + +int main() +{ + printf("Starting server...\n"); + + ConnectionManager srv; + StressMonitor telnet; + + srv.setConnectionMonitor( &telnet ); + + srv.startServer( 4001 ); + + for(;;) + { + srv.scanConnections( 5000, false ); + } + + return 0; +} diff --git a/src/old/tests/strhash.cpp b/src/old/tests/strhash.cpp new file mode 100644 index 0000000..f6528ca --- /dev/null +++ b/src/old/tests/strhash.cpp @@ -0,0 +1,12 @@ +#include <stdio.h> +#include "hashfunctionstring.h" + +int main( int argc, char *argv[] ) +{ + HashFunctionString h; + + printf("\"%s\": %lu\n", argv[1], h.hash( argv[1] ) ); + + return 0; +} + diff --git a/src/old/tests/teltest/main.cpp b/src/old/tests/teltest/main.cpp new file mode 100644 index 0000000..5d3ec26 --- /dev/null +++ b/src/old/tests/teltest/main.cpp @@ -0,0 +1,21 @@ +#include "connectionmanager.h" +#include "telnetmonitor.h" + +int main() +{ + printf("Starting server...\n"); + + ConnectionManager srv; + TelnetMonitor telnet; + + srv.setConnectionMonitor( &telnet ); + + srv.startServer( 4001 ); + + for(;;) + { + srv.scanConnections( 5000, false ); + } + + return 0; +} diff --git a/src/old/tests/teltest/telnetmonitor.cpp b/src/old/tests/teltest/telnetmonitor.cpp new file mode 100644 index 0000000..65954eb --- /dev/null +++ b/src/old/tests/teltest/telnetmonitor.cpp @@ -0,0 +1,54 @@ +#include "telnetmonitor.h" +#include "protocoltelnet.h" +#include <sys/stat.h> + +TelnetMonitor::TelnetMonitor() +{ +} + +TelnetMonitor::~TelnetMonitor() +{ +} + +bool TelnetMonitor::init() +{ + return true; +} + +bool TelnetMonitor::deInit() +{ + return true; +} + +bool TelnetMonitor::timeSlice() +{ + for( int j = 0; j < lCon.getSize(); j++ ) + { + if( ((Connection *)lCon[j])->hasInput() ) + { + printf("%s\n", ((Connection *)lCon[j])->getInput() ); + } + } + return true; +} + +LinkMessage* TelnetMonitor::processIRM( LinkMessage *pMsg ) +{ + return NULL; +} + +bool TelnetMonitor::onNewConnection( Connection *pCon, int nPort ) +{ + ProtocolTelnet *pt = new ProtocolTelnet(); + pCon->setProtocol( pt ); + + lCon.append( pt ); + + return true; +} + +bool TelnetMonitor::onClosedConnection( Connection *pCon ) +{ + return true; +} + diff --git a/src/old/tests/teltest/telnetmonitor.h b/src/old/tests/teltest/telnetmonitor.h new file mode 100644 index 0000000..ba5761e --- /dev/null +++ b/src/old/tests/teltest/telnetmonitor.h @@ -0,0 +1,26 @@ +#ifndef HTTPCONNECTIONMONITOR_H +#define HTTPCONNECTIONMONITOR_H + +#include "connectionmonitor.h" +#include "programlink.h" +#include "linkedlist.h" + +class TelnetMonitor : public ConnectionMonitor, public ProgramLink +{ +public: + TelnetMonitor(); + ~TelnetMonitor(); + + bool init(); + bool deInit(); + bool timeSlice(); + LinkMessage* processIRM( LinkMessage *pMsgIn ); + + bool onNewConnection( Connection *pCon, int nPort ); + bool onClosedConnection( Connection *pCon ); + +private: + LinkedList lCon; +}; + +#endif diff --git a/src/old/tests/xmlreadtest.cpp b/src/old/tests/xmlreadtest.cpp new file mode 100644 index 0000000..d061810 --- /dev/null +++ b/src/old/tests/xmlreadtest.cpp @@ -0,0 +1,29 @@ +#include "xmlfilereader.h" +#include "xmlstringreader.h" +#include "xmlfilewriter.h" + +int main( int argc, char *argv[] ) +{ + if( argc < 4 ) + { + printf("Usage: %s f <file in> <file out>\n", argv[0] ); + printf(" %s s <xml string> <file out>\n\n", argv[0] ); + return 0; + } + + if( argv[1][0] == 'f' ) + { + XmlFileReader r( argv[2], true ); +// XmlFileWriter w( argv[3], "\t", r.detatchRoot() ); +// w.write(); + } + else if( argv[1][0] == 's' ) + { + XmlStringReader r( argv[2], true ); + XmlFileWriter w(stdout, "\t", r.detatchRoot() ); + w.write(); + } + + return 0; +} + diff --git a/src/old/tests/xmlrepltest.cpp b/src/old/tests/xmlrepltest.cpp new file mode 100644 index 0000000..9667705 --- /dev/null +++ b/src/old/tests/xmlrepltest.cpp @@ -0,0 +1,31 @@ +#include "xmlwriter.h" + +int main() +{ + printf("Testing Xml Replacement...\n"); + XmlDocument w; + + w.addNode("text"); + w.setContent("this text is before the node. "); + w.addNode("keepme", "This one we keep...", true ); + w.setContent("this text is after."); + w.addNode("deleteme", "This one we don't...", true ); + w.setContent("this is last..." ); + w.closeNode(); + + //XmlWriter::writeNode( stdout, w.getRoot(), 0, NULL ); + + printf("\n\n"); + + //XmlNode *xNode = w.getRoot()->detatchNode( 1 ); + + //XmlWriter::writeNode( stdout, w.getRoot(), 0, NULL ); + + printf("\n\n"); + + //XmlWriter::writeNode( stdout, xNode, 0, NULL ); + + printf("\n\n"); + + return 0; +} diff --git a/src/old/tests/xmlwritetest.cpp b/src/old/tests/xmlwritetest.cpp new file mode 100644 index 0000000..a22d19d --- /dev/null +++ b/src/old/tests/xmlwritetest.cpp @@ -0,0 +1,48 @@ +#include "xmlfilewriter.h" +#include "xmlstringwriter.h" +#include "xmlstringreader.h" + +void fillItIn( XmlWriter &w ) +{ + w.addNode("thinglist"); + + w.addNode("thing"); + w.addProperty("type", " ±î´<M-F6><M-F6>³¸®°êòì¯"); + + w.addNode("id", "Klophin²³±¹¸·µ´äêíëã Staff", true ); + w.addNode("name", "Klophin Staff", true ); + w.addNode("durability", "0.01", true ); + w.addNode("size", "0.1", true ); + + w.addNode("config"); + w.addNode("damage", "3d6+4", true ); + w.addNode("class", "melee", true ); + w.addNode("type", "bludgeon", true ); + w.addNode("damagedesc", "club/clubs", true ); + w.closeNode(); + + w.closeNode(); + + w.closeNode(); +} + +int main() +{ + printf("Testing XmlWriter...\n"); + + //XmlStringReader *xsr = new XmlStringReader("<stuff/>"); + + //printf("%08X\n%08X\n%08X\n", xsr, (XmlReader *)xsr, (XmlDocument *)xsr ); + + //delete (XmlDocument *)xsr; + XmlFileWriter wf("test.xml", "\t"); + + fillItIn( wf ); + + XmlStringWriter ws("\t"); + fillItIn( ws ); + + printf("Now the string version:\n\n%s\n", ws.getString().c_str() ); + + return 0; +} diff --git a/src/old/tokenstring.cpp b/src/old/tokenstring.cpp new file mode 100644 index 0000000..e57ba69 --- /dev/null +++ b/src/old/tokenstring.cpp @@ -0,0 +1,163 @@ +#include "tokenstring.h" +#include <string.h> + +TokenString::TokenString( const char *lpNewTokenString ) +{ + lpTokenString = NULL; + if( lpNewTokenString ) + { + parseLine( lpNewTokenString ); + } +} + +TokenString::~TokenString() +{ + delete[] lpTokenString; + for( int j = 0; j < lToken.getSize(); j++ ) + { + delete[] (((Token *)lToken[j])->lpToken); + delete ((Token *)lToken[j]); + } +} + +void TokenString::parseLine( const char *lpNewTokenString ) +{ + if( lpTokenString != NULL ) + { + delete[] lpTokenString; + lpTokenString = NULL; + for( int j = 0; j < lToken.getSize(); j++ ) + { + delete[] (((Token *)lToken[j])->lpToken); + delete ((Token *)lToken[j]); + } + lToken.empty(); + } + if( lpNewTokenString == NULL ) + { + lpTokenString = new char[1]; + lpTokenString[0] = '\0'; + lToken.setSize(0); + return; + } + // First order of business, make an internal copy so someone can get it + // if they want to. + int nLen = strlen(lpNewTokenString); + lpTokenString = new char[nLen+1]; + strcpy( lpTokenString, lpNewTokenString ); + + // Now we do a preliminary parse. This could be effected by later + // editing and aliasing, but we'll see... + int nTkStart, nTkEnd; + int mode=0; // 0 = startSearch, 1=endSearch + for( int j = 0; j <= nLen; j++ ) + { + if( mode == 0 ) + { + if( lpTokenString[j] != ' ' && + lpTokenString[j] != '\t' ) + { + nTkStart = j; + mode = 1; + } + } + else + { + if( lpTokenString[j] == ' ' || + lpTokenString[j] == '\t' || + lpTokenString[j] == '\0' ) + { + nTkEnd = j-1; + mode = 0; + + appendToken( nTkStart, nTkEnd ); + } + } + } +} + +void TokenString::appendToken( int nStart, int nEnd ) +{ + Token *pToken = new Token; + pToken->lpOrig = &lpTokenString[nStart]; + + // nStart and nEnd are inclusive, we must add two for the end, and the null + pToken->lpToken = new char[nEnd-nStart+2]; + memcpy( pToken->lpToken, &lpTokenString[nStart], nEnd-nStart+1 ); + pToken->lpToken[nEnd-nStart+1] = '\0'; + +// printf("%s\n", pToken->lpToken ); + lToken.append( pToken ); +} + +void TokenString::insertToken( int nStart, int nEnd, char *lpOldOrig, const char *lpNewToken, int nIndex ) +{ + Token *pToken = new Token; + pToken->lpOrig = lpOldOrig; + + // nStart and nEnd are inclusive, we must add two for the end, and the null + pToken->lpToken = new char[nEnd-nStart+2]; + memcpy( pToken->lpToken, &lpNewToken[nStart], nEnd-nStart+1 ); + pToken->lpToken[nEnd-nStart+1] = '\0'; + + lToken.insertBefore( pToken, nIndex ); +} + +int TokenString::getNumTokens() +{ + return lToken.getSize(); +} + +char *TokenString::getToken( int nIndex ) +{ + if( nIndex >= lToken.getSize() ) return NULL; + return (char *)(((Token *)lToken[nIndex])->lpToken); +} + +char *TokenString::getTokenString( int nIndex ) +{ + if( nIndex >= lToken.getSize() ) return NULL; + return (char *)(((Token *)lToken[nIndex])->lpOrig); +} + +void TokenString::expandTokenTo( int nIndex, char *lpNewToken ) +{ + // First, we delete the token at nIndex, then we keep inserting + // at that position... + // We also have to remember the index to the original string, + // since most of what we're expanding to won't be in the origingal + // we need to keep these indexes updated in order to make other parts + // of the system happy. + char *lpOldOrig = ((Token *)lToken[nIndex])->lpOrig; + delete[] ((Token *)lToken[nIndex])->lpToken; + delete ((Token *)lToken[nIndex]); + lToken.deleteAt( nIndex ); + + // We'll do this just like we did above, but instead we'll + // do tricky things when we find tokens... + int nLen = strlen(lpNewToken); + int nTkStart, nTkEnd, nNewIndex=nIndex; + int mode=0; // 0 = startSearch, 1=endSearch + for( int j = 0; j <= nLen; j++ ) + { + if( mode == 0 ) + { + if( lpNewToken[j] != ' ' && lpNewToken[j] != '\t' ) + { + nTkStart = j; + mode = 1; + } + } + else + { + if( lpNewToken[j] == ' ' || lpNewToken[j] == '\t' || lpNewToken[j] == '\0' ) + { + nTkEnd = j-1; + mode = 0; + + insertToken( nTkStart, nTkEnd, lpOldOrig, lpNewToken, nNewIndex ); + nNewIndex++; + } + } + } +} diff --git a/src/old/tokenstring.h b/src/old/tokenstring.h new file mode 100644 index 0000000..42f7309 --- /dev/null +++ b/src/old/tokenstring.h @@ -0,0 +1,114 @@ +#ifndef TOKENSTRING_H +#define TOKENSTRING_H + +#include "linkedlist.h" + +/** A single tokenized command line. Contains all information necesarry to + * nicely access a stand-alone command line and to perform alias expansion + * inside of that command line. + * When expanding a token, the original command line is left intact, so any + * command usng a command line verbatum (getTokenString not getToken) will get + * the original, and not the expanded version. + * Since indexing into the original command line is also done by token, it + * means that using getTokenString( 0 ) will not always get you the first + * character of the command line, it will get you the first non-whitespace + * character. + * Furthermore, when expanding the expantion string is tokenized as well, + * but since the original string is unchanged, all tokens that expand any + * given index will all retain the same index into the original command line. + *@todo Update this to allow it to break on different types of token + * delimiters. + *@author Mike Buland + */ +class TokenString{ +public: + /** Automatically call parseLine when created. + *@param lpNewTokenString The command line to tokenize + *@author Mike Buland + */ + TokenString( const char *lpNewTokenString=NULL ); + virtual ~TokenString(); + + /** Performs a tokenizing parse on the given command line, setting it as + * the internal command line for all future tokenizing (excluding + * expansion) + *@param lpNewTokenString The new command line to set to this object. + *@author Mike Buland + */ + void parseLine( const char *lpNewTokenString ); + + /** Appends a token to the list of available tokens. This references the + * internal pointer to the command line, so no token string must be + * specified. + *@param nStart The first character of the token to insert. + *@param nEnd The last character of the token to insert. + *@author Mike Buland + */ + void appendToken( int nStart, int nEnd ); + + /** Gets the number of tokens. This is particularly useful post-aliasing + * since the number of tokens may not match what is percieved from the + * original command line. + *@returns The number of available tokens. + *@author Mike Buland + */ + int getNumTokens(); + + /** Gets a processed token specified by index. + *@param nIndex The index of the token to retrieve. + *@returns A pointer to the requested token. Please note that these tokens + * may not match the original command line. + *@author Mike Buland + */ + char *getToken( int nIndex ); + + /** Gets the original command line based on tokens. Use this if you want + * to perform your own processing on parts of the command line, without + * resorting to tokens. + * The first character in the returned string will always be + * non-whitespace. + *@param nIndex The index of the token to start at, zero gets you the whole + * command line. + *@returns A pointer to the internal original command line string, starting + * at the position of the first non-whitespace character of the token + * specified. + *@author Mike Buland + */ + char *getTokenString( int nIndex=0 ); + + /** Expands a token, replacing it with the string lpNewToken, but + * processing the new string for tokens before performing the replacement + *@param nIndex Which token should be replaced. + *@param lpNewToken The string to replace the token with. + *@author Mike Buland + */ + void expandTokenTo( int nIndex, char *lpNewToken ); + + /** Inserts a token at any position in the command line. This does not + * effect the original command line. + *@param nStart The start of the token in the string lpNewToken. (inclusive) + *@param nEnd The end of the token in the string lpToken. (inclusive) + *@param lpOldOrig The pointer to the position in the orginal command + * line where this new token should point. + *@param lpNewToken The string containing the new token. May contain more + * than just one token. + *@param nIndex The position to insert the token to. + *@author Mike Buland + */ + void insertToken( int nStart, int nEnd, char *lpOldOrig, const char *lpNewToken, int nIndex ); + +private: + char *lpTokenString; /**< The original text that this string came from */ + LinkedList lToken; /**< The list of tokens. */ + + /** + * A single token within the token string. + */ + typedef struct Token + { + char *lpOrig; /**< This is just a pointer back to lpTokenString */ + char *lpToken; /**< This is really a whole token */ + } Token; +}; + +#endif diff --git a/src/old/tqsort.h b/src/old/tqsort.h new file mode 100644 index 0000000..c836b4f --- /dev/null +++ b/src/old/tqsort.h @@ -0,0 +1,207 @@ +#ifndef T_QSORT_H +#define T_QSORT_H + +#define _QSORT_SWAP(a, b, t) ((void)((t = *a), (*a = *b), (*b = t))) + +/* Discontinue quicksort algorithm when partition gets below this size. + This particular magic number was chosen to work best on a Sun 4/260. */ +#define _QSORT_MAX_THRESH 4 + +/* Stack node declarations used to store unfulfilled partition obligations + * (inlined in QSORT). +typedef struct { + QSORT_TYPE *_lo, *_hi; +} qsort_stack_node; + */ + +/* The next 4 #defines implement a very fast in-line stack abstraction. */ +/* The stack needs log (total_elements) entries (we could even subtract + log(MAX_THRESH)). Since total_elements has type unsigned, we get as + upper bound for log (total_elements): + bits per byte (CHAR_BIT) * sizeof(unsigned). */ +#define _QSORT_STACK_SIZE (8 * sizeof(unsigned)) +#define _QSORT_PUSH(top, low, high) \ + (((top->_lo = (low)), (top->_hi = (high)), ++top)) +#define _QSORT_POP(low, high, top) \ + ((--top, (low = top->_lo), (high = top->_hi))) +#define _QSORT_STACK_NOT_EMPTY (_stack < _top) + + +/* Order size using quicksort. This implementation incorporates + four optimizations discussed in Sedgewick: + + 1. Non-recursive, using an explicit stack of pointer that store the + next array partition to sort. To save time, this maximum amount + of space required to store an array of SIZE_MAX is allocated on the + stack. Assuming a 32-bit (64 bit) integer for size_t, this needs + only 32 * sizeof(stack_node) == 256 bytes (for 64 bit: 1024 bytes). + Pretty cheap, actually. + + 2. Chose the pivot element using a median-of-three decision tree. + This reduces the probability of selecting a bad pivot value and + eliminates certain extraneous comparisons. + + 3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving + insertion sort to order the MAX_THRESH items within each partition. + This is a big win, since insertion sort is faster for small, mostly + sorted array segments. + + 4. The larger of the two sub-partitions is always pushed onto the + stack first, with the algorithm then concentrating on the + smaller partition. This *guarantees* no more than log (total_elems) + stack size is needed (actually O(1) in this case)! */ + +/* The main code starts here... */ + +template<typename QSORT_TYPE, typename QSORT_LTT, typename CST> +void tqsort( QSORT_TYPE *QSORT_BASE, int QSORT_NELT ) +{ + QSORT_LTT QSORT_LT; + QSORT_TYPE *const _base = (QSORT_BASE); + const unsigned _elems = (QSORT_NELT); + QSORT_TYPE _hold; + + /* Don't declare two variables of type QSORT_TYPE in a single + * statement: eg `TYPE a, b;', in case if TYPE is a pointer, + * expands to `type* a, b;' wich isn't what we want. + */ + + if (_elems > _QSORT_MAX_THRESH) { + QSORT_TYPE *_lo = _base; + QSORT_TYPE *_hi = _lo + _elems - 1; + struct { + QSORT_TYPE *_hi; QSORT_TYPE *_lo; + } _stack[_QSORT_STACK_SIZE], *_top = _stack + 1; + + while (_QSORT_STACK_NOT_EMPTY) { + QSORT_TYPE *_left_ptr; QSORT_TYPE *_right_ptr; + + /* Select median value from among LO, MID, and HI. Rearrange + LO and HI so the three values are sorted. This lowers the + probability of picking a pathological pivot value and + skips a comparison for both the LEFT_PTR and RIGHT_PTR in + the while loops. */ + + QSORT_TYPE *_mid = _lo + ((_hi - _lo) >> 1); + + if (QSORT_LT ((CST)(_mid), (CST)(_lo))) + _QSORT_SWAP (_mid, _lo, _hold); + if (QSORT_LT ((CST)(_hi), (CST)(_mid))) + _QSORT_SWAP (_mid, _hi, _hold); + else + goto _jump_over; + if (QSORT_LT ((CST)(_mid), (CST)(_lo))) + _QSORT_SWAP (_mid, _lo, _hold); + _jump_over:; + + _left_ptr = _lo + 1; + _right_ptr = _hi - 1; + + /* Here's the famous ``collapse the walls'' section of quicksort. + Gotta like those tight inner loops! They are the main reason + that this algorithm runs much faster than others. */ + do { + while (QSORT_LT ((CST)(_left_ptr), (CST)(_mid))) + ++_left_ptr; + + while (QSORT_LT ((CST)(_mid), (CST)(_right_ptr))) + --_right_ptr; + + if (_left_ptr < _right_ptr) { + _QSORT_SWAP (_left_ptr, _right_ptr, _hold); + if (_mid == _left_ptr) + _mid = _right_ptr; + else if (_mid == _right_ptr) + _mid = _left_ptr; + ++_left_ptr; + --_right_ptr; + } + else if (_left_ptr == _right_ptr) { + ++_left_ptr; + --_right_ptr; + break; + } + } while (_left_ptr <= _right_ptr); + + /* Set up pointers for next iteration. First determine whether + left and right partitions are below the threshold size. If so, + ignore one or both. Otherwise, push the larger partition's + bounds on the stack and continue sorting the smaller one. */ + + if (_right_ptr - _lo <= _QSORT_MAX_THRESH) { + if (_hi - _left_ptr <= _QSORT_MAX_THRESH) + /* Ignore both small partitions. */ + _QSORT_POP (_lo, _hi, _top); + else + /* Ignore small left partition. */ + _lo = _left_ptr; + } + else if (_hi - _left_ptr <= _QSORT_MAX_THRESH) + /* Ignore small right partition. */ + _hi = _right_ptr; + else if (_right_ptr - _lo > _hi - _left_ptr) { + /* Push larger left partition indices. */ + _QSORT_PUSH (_top, _lo, _right_ptr); + _lo = _left_ptr; + } + else { + /* Push larger right partition indices. */ + _QSORT_PUSH (_top, _left_ptr, _hi); + _hi = _right_ptr; + } + } + } + + /* Once the BASE array is partially sorted by quicksort the rest + is completely sorted using insertion sort, since this is efficient + for partitions below MAX_THRESH size. BASE points to the + beginning of the array to sort, and END_PTR points at the very + last element in the array (*not* one beyond it!). */ + + { + QSORT_TYPE *const _end_ptr = _base + _elems - 1; + QSORT_TYPE *_tmp_ptr = _base; + register QSORT_TYPE *_run_ptr; + QSORT_TYPE *_thresh; + + _thresh = _base + _QSORT_MAX_THRESH; + if (_thresh > _end_ptr) + _thresh = _end_ptr; + + /* Find smallest element in first threshold and place it at the + array's beginning. This is the smallest array element, + and the operation speeds up insertion sort's inner loop. */ + + for (_run_ptr = _tmp_ptr + 1; _run_ptr <= _thresh; ++_run_ptr) + if (QSORT_LT ((CST)(_run_ptr), (CST)(_tmp_ptr))) + _tmp_ptr = _run_ptr; + + if (_tmp_ptr != _base) + _QSORT_SWAP (_tmp_ptr, _base, _hold); + + /* Insertion sort, running from left-hand-side + * up to right-hand-side. */ + + _run_ptr = _base + 1; + while (++_run_ptr <= _end_ptr) { + _tmp_ptr = _run_ptr - 1; + while (QSORT_LT ((CST)(_run_ptr), (CST)(_tmp_ptr))) + --_tmp_ptr; + + ++_tmp_ptr; + if (_tmp_ptr != _run_ptr) { + QSORT_TYPE *_trav = _run_ptr + 1; + while (--_trav >= _run_ptr) { + QSORT_TYPE *_hi; QSORT_TYPE *_lo; + _hold = *_trav; + + for (_hi = _lo = _trav; --_lo >= _tmp_ptr; _hi = _lo) + *_hi = *_lo; + *_hi = _hold; + } + } + } + } +} + +#endif diff --git a/src/old/unit/hashtable/hashtable.cpp b/src/old/unit/hashtable/hashtable.cpp new file mode 100644 index 0000000..b2e1cf5 --- /dev/null +++ b/src/old/unit/hashtable/hashtable.cpp @@ -0,0 +1,107 @@ +#include <cstdlib> +#include <cstring> +#include <iostream> +#include <cpptest.h> +#include <string.h> +#include <set> +#include <map> + +#include "hashfunctionstring.h" +#include "hashfunctioncasestring.h" +#include "hashfunctionint.h" + +class HashFunctionSuite : public Test::Suite +{ +public: + HashFunctionSuite() + { + TEST_ADD( HashFunctionSuite::functionString ) + TEST_ADD( HashFunctionSuite::functionCaseString ) + TEST_ADD( HashFunctionSuite::functionInt ) + } + +private: + void functionStringWorker( HashFunction &hf, std::set<unsigned long> &sCodes, char *str, int nLevel, int nMax ) + { + for( char let = 'A'; let <= 'z'; let += 3 ) + { + str[nLevel+1] = '\0'; + str[nLevel] = let; + unsigned long x = hf.hash( str ); + TEST_ASSERT( sCodes.find( x ) == sCodes.end() ); + TEST_ASSERT( hf.cmpIDs( str, str ) ); + sCodes.insert( x ); + + if( nLevel < nMax ) + functionStringWorker( hf, sCodes, str, nLevel+1, nMax ); + } + } + + void functionString() + { + HashFunctionString hf; + char str[10]; + + std::set<unsigned long> sCodes; + + functionStringWorker( hf, sCodes, str, 0, 3 ); + } + + void functionCaseStringWorker( HashFunction &hf, std::map<unsigned long, char *> &sCodes, char *str, int nLevel, int nMax ) + { + for( char let = 'A'; let <= 'z'; let += 3 ) + { + str[nLevel+1] = '\0'; + str[nLevel] = let; + unsigned long x = hf.hash( str ); + std::map<unsigned long, char *>::iterator i = sCodes.find( x ); + if( i == sCodes.end() ) + { + sCodes[x] = strdup( str ); + } + else + { + TEST_ASSERT( strcasecmp( (*i).second, str ) == 0 ); + TEST_ASSERT( hf.cmpIDs( (*i).second, str ) == true ); + } + + if( nLevel < nMax ) + functionCaseStringWorker( hf, sCodes, str, nLevel+1, nMax ); + } + } + + void functionCaseString() + { + HashFunctionCaseString hf; + char str[10]; + + std::map<unsigned long, char *> sCodes; + + functionCaseStringWorker( hf, sCodes, str, 0, 3 ); + + std::map<unsigned long, char *>::iterator i; + for( i = sCodes.begin(); i != sCodes.end(); i++ ) + { + free( (*i).second ); + } + } + + void functionInt() + { + HashFunctionInt hf; + + for( long i = -100000; i <= 100000; i += 100 ) + { + TEST_ASSERT( ((long)hf.hash( (void *)i )) == i ); + TEST_ASSERT( ((long)hf.cmpIDs( (void *)i, (void *)i )) ); + } + } +}; + +int main( int argc, char *argv[] ) +{ + Test::TextOutput output( Test::TextOutput::Verbose ); + HashFunctionSuite ts; + return ts.run( output ) ? EXIT_SUCCESS : EXIT_FAILURE; +} + diff --git a/src/old/unit/xml/xml.cpp b/src/old/unit/xml/xml.cpp new file mode 100644 index 0000000..e4d779c --- /dev/null +++ b/src/old/unit/xml/xml.cpp @@ -0,0 +1,59 @@ +#include <cstdlib> +#include <cstring> +#include <iostream> +#include <cpptest.h> +#include <string.h> + +#include "xmlstringreader.h" +#include "xmlexception.h" + +class XmlCoreTestSuite : public Test::Suite +{ +public: + XmlCoreTestSuite() + { + TEST_ADD( XmlCoreTestSuite::badXml01 ) + TEST_ADD( XmlCoreTestSuite::badXml02 ) + TEST_ADD( XmlCoreTestSuite::badXml03 ) + + TEST_ADD( XmlCoreTestSuite::entityBuiltin01 ) + + TEST_ADD( XmlCoreTestSuite::entityDoc01 ) + } + +private: + void badXml01() + { + TEST_THROWS( XmlStringReader r("<hello></bye>"), XmlException & ); + } + + void badXml02() + { + TEST_THROWS( XmlStringReader r("<hello>"), XmlException & ); + } + + void badXml03() + { + TEST_THROWS( XmlStringReader r("<hello param=\"stuff?"), XmlException & ); + } + + void entityBuiltin01() + { + XmlStringReader r("<hello>><&'"</hello>"); + TEST_ASSERT( strcmp( r.getRoot()->getContent(), "><&\'\"" ) == 0 ); + } + + void entityDoc01() + { + XmlStringReader r("<!ENTITY name \"bob the man\"><hello>"&name;"</hello>"); + TEST_ASSERT( strcmp( r.getRoot()->getContent(), "\"bob the man\"" ) == 0 ); + } +}; + +int main( int argc, char *argv[] ) +{ + Test::TextOutput output( Test::TextOutput::Verbose ); + XmlCoreTestSuite ts; + return ts.run( output ) ? EXIT_SUCCESS : EXIT_FAILURE; +} + diff --git a/src/old/xmldocument.cpp b/src/old/xmldocument.cpp new file mode 100644 index 0000000..d7867d5 --- /dev/null +++ b/src/old/xmldocument.cpp @@ -0,0 +1,149 @@ +#include <stdio.h> +#include <stdlib.h> +#include "xmlwriter.h" + +XmlDocument::XmlDocument( XmlNode *pRoot ) +{ + this->pRoot = pRoot; + pCurrent = NULL; + bCompleted = (pRoot!=NULL); +} + +XmlDocument::~XmlDocument() +{ + if( pRoot ) + { + delete pRoot; + } +} + +void XmlDocument::addNode( const char *sName, const char *sContent, bool bClose ) +{ + if( pRoot == NULL ) + { + // This is the first node, so ignore position and just insert it. + pCurrent = pRoot = new XmlNode( sName, NULL, sContent ); + } + else + { + pCurrent = pCurrent->addChild( sName, sContent ); + } + + if( bClose ) + { + closeNode(); + } +} + +void XmlDocument::setName( const char *sName ) +{ + pCurrent->setName( sName ); +} + +bool XmlDocument::isCompleted() +{ + return bCompleted; +} + +XmlNode *XmlDocument::getRoot() +{ + return pRoot; +} + +XmlNode *XmlDocument::detatchRoot() +{ + XmlNode *pTemp = pRoot; + pRoot = NULL; + return pTemp; +} + +XmlNode *XmlDocument::getCurrent() +{ + return pCurrent; +} + +void XmlDocument::closeNode() +{ + if( pCurrent != NULL ) + { + pCurrent = pCurrent->getParent(); + + if( pCurrent == NULL ) + { + bCompleted = true; + } + } +} + +void XmlDocument::addProperty( const char *sName, const char *sValue ) +{ + if( pCurrent ) + { + pCurrent->addProperty( sName, sValue ); + } +} + +void XmlDocument::addProperty( const char *sName, const unsigned char nValue ) +{ + char buf[12]; + sprintf( buf, "%hhi", nValue ); + addProperty( sName, buf ); +} + +void XmlDocument::addProperty( const char *sName, const char nValue ) +{ + char buf[12]; + sprintf( buf, "%hhi", nValue ); + addProperty( sName, buf ); +} + +void XmlDocument::addProperty( const char *sName, const unsigned short nValue ) +{ + char buf[12]; + sprintf( buf, "%hi", nValue ); + addProperty( sName, buf ); +} + +void XmlDocument::addProperty( const char *sName, const short nValue ) +{ + char buf[12]; + sprintf( buf, "%hi", nValue ); + addProperty( sName, buf ); +} + +void XmlDocument::addProperty( const char *sName, const int nValue ) +{ + char buf[12]; + sprintf( buf, "%d", nValue ); + addProperty( sName, buf ); +} + +void XmlDocument::addProperty( const char *sName, const unsigned long nValue ) +{ + char buf[12]; + sprintf( buf, "%li", nValue ); + addProperty( sName, buf ); +} + +void XmlDocument::addProperty( const char *sName, const long nValue ) +{ + char buf[12]; + sprintf( buf, "%li", nValue ); + addProperty( sName, buf ); +} + +void XmlDocument::addProperty( const char *sName, const double dValue ) +{ + char buf[40]; + sprintf( buf, "%f", dValue ); + addProperty( sName, buf ); +} + +void XmlDocument::setContent( const char *sContent ) +{ + if( pCurrent ) + { + pCurrent->setContent( sContent ); + } +} + diff --git a/src/old/xmldocument.h b/src/old/xmldocument.h new file mode 100644 index 0000000..6671c41 --- /dev/null +++ b/src/old/xmldocument.h @@ -0,0 +1,171 @@ +#ifndef XMLDOCUMENT +#define XMLDOCUMENT + +#include "xmlnode.h" + +/** + * Keeps track of an easily managed set of XmlNode information. Allows simple + * operations for logical writing to and reading from XML structures. Using + * already formed structures is simply done through the XmlNode structures, + * and the getRoot function here. Creation is performed through a simple set + * of operations that creates the data in a stream type format. + *@author Mike Buland + */ +class XmlDocument +{ +public: + /** + * Construct either a blank XmlDocuemnt or construct a document around an + * existing XmlNode. Be careful, once an XmlNode is passed into a document + * the document takes over ownership and will delete it when the XmlDocument + * is deleted. + *@param pRoot The XmlNode to use as the root of this document, or NULL if + * you want to start a new document. + */ + XmlDocument( XmlNode *pRoot=NULL ); + + /** + * Destroy all contained nodes. + */ + virtual ~XmlDocument(); + + /** + * Add a new node to the document. The new node is appended to the end of + * the current context, i.e. XmlNode, and the new node, provided it isn't + * close as part of this operation, will become the current context. + *@param sName The name of the new node to add. + *@param sContent A content string to be placed inside of the new node. + *@param bClose Set this to true to close the node immediately after adding + * the node and setting the content and name. If this is set to true the + * node is appended, but the context node doesn't change. + */ + void addNode( const char *sName=NULL, const char *sContent=NULL, bool bClose=false ); + + /** + * Set the name of the current node context. + *@param sName The new name of the node. + */ + void setName( const char *sName ); + + /** + * Close the current node context. This will move the current context to + * the parent node of the former current node. If the current node was the + * root then the "completed" flag is set and no more operations are allowed. + */ + void closeNode(); + + /** + * Change the content of the current node at the current position between + * nodes. + *@param sContent The new content of the current node. + */ + void setContent( const char *sContent ); + + /** + * Add a named property to the current context node. + *@param sName The name of the property to add. + *@param sValue The string value of the property. + */ + void addProperty( const char *sName, const char *sValue ); + + /** + * Add a named property to the current context node, converting the + * numerical parameter to text using standrd printf style conversion. + *@param sName The name of the property to add. + *@param nValue The numerical value to add. + */ + void addProperty( const char *sName, const unsigned char nValue ); + + /** + * Add a named property to the current context node, converting the + * numerical parameter to text using standrd printf style conversion. + *@param sName The name of the property to add. + *@param nValue The numerical value to add. + */ + void addProperty( const char *sName, const char nValue ); + + /** + * Add a named property to the current context node, converting the + * numerical parameter to text using standrd printf style conversion. + *@param sName The name of the property to add. + *@param nValue The numerical value to add. + */ + void addProperty( const char *sName, const unsigned short nValue ); + + /** + * Add a named property to the current context node, converting the + * numerical parameter to text using standrd printf style conversion. + *@param sName The name of the property to add. + *@param nValue The numerical value to add. + */ + void addProperty( const char *sName, const short nValue ); + + /** + * Add a named property to the current context node, converting the + * numerical parameter to text using standrd printf style conversion. + *@param sName The name of the property to add. + *@param nValue The numerical value to add. + */ + void addProperty( const char *sName, const unsigned long nValue ); + + /** + * Add a named property to the current context node, converting the + * numerical parameter to text using standrd printf style conversion. + *@param sName The name of the property to add. + *@param nValue The numerical value to add. + */ + void addProperty( const char *sName, const long nValue ); + + /** + * Add a named property to the current context node, converting the + * numerical parameter to text using standrd printf style conversion. + *@param sName The name of the property to add. + *@param nValue The numerical value to add. + */ + void addProperty( const char *sName, const int nValue ); + + /** + * Add a named property to the current context node, converting the + * numerical parameter to text using standrd printf style conversion. + *@param sName The name of the property to add. + *@param dValue The numerical value to add. + */ + void addProperty( const char *sName, const double dValue ); + + /** + * The XmlDocuemnt is considered completed if the root node has been closed. + * Once an XmlDocument has been completed, you can no longer perform + * operations on it. + *@return True if completed, false if still in progress. + */ + bool isCompleted(); + + /** + * Get a pointer to the root object of this XmlDocument. + *@returns A pointer to an internally owned XmlNode. Do not delete this + * XmlNode. + */ + XmlNode *getRoot(); + + /** + * Get a pointer to the root object of this XmlDocument, and remove the + * ownership from this object. + *@returns A pointer to an internally owned XmlNode. Do not delete this + * XmlNode. + */ + XmlNode *detatchRoot(); + + /** + * Get the current context node, which could be the same as the root node. + *@returns A pointer to an internally owned XmlNode. Do not delete this + * XmlNode. + */ + XmlNode *getCurrent(); + +private: + XmlNode *pRoot; /**< The root node. */ + XmlNode *pCurrent; /**< The current node. */ + bool bCompleted; /**< Is it completed? */ +}; + +#endif diff --git a/src/old/xmlfilereader.cpp b/src/old/xmlfilereader.cpp new file mode 100644 index 0000000..ed674a8 --- /dev/null +++ b/src/old/xmlfilereader.cpp @@ -0,0 +1,58 @@ +#include "xmlfilereader.h" +#include "exceptions.h" +#include <string.h> + +XmlFileReader::XmlFileReader( const char *sFile, bool bStrip ) + : XmlReader( bStrip ) +{ + fh = fopen( sFile, "rt" ); + + if( fh == NULL ) + { + throw XmlException("Couldn't open file: %s", sFile ); + //nError = 1; + } + else + { + buildDoc(); + } +} + +XmlFileReader::~XmlFileReader() +{ +} + +char XmlFileReader::getChar( int nIndex ) +{ + // Make sure we always have a little data left in the buffer + if( fbDataIn.getLength() <= nIndex+1 && fh ) + { + int nBytes = fbDataIn.getCapacity()-1; + char *buf = new char[nBytes]; + int nRead = fread( buf, 1, nBytes, fh ); + fbDataIn.appendData( buf, nRead ); + delete[] buf; + + if( nRead < nBytes ) + { + fclose( fh ); + fh = NULL; + } + } + if( fbDataIn.getLength() >= nIndex+1 ) + { + return fbDataIn.getData()[nIndex]; + } + else + { + throw XmlException("End of XML stream read."); + } +} + +void XmlFileReader::usedChar( int nAmnt ) +{ + if( fbDataIn.getLength()-nAmnt >= 0 ) + { + fbDataIn.usedData( nAmnt ); + } +} diff --git a/src/old/xmlfilereader.h b/src/old/xmlfilereader.h new file mode 100644 index 0000000..e3e02c2 --- /dev/null +++ b/src/old/xmlfilereader.h @@ -0,0 +1,47 @@ +#ifndef XMLFILEREADER +#define XMLFILEREADER + +#include <stdio.h> +#include "xmlreader.h" +#include "flexbuf.h" + +/** + * Takes care of reading in xml formatted data from a file. This could/should + * be made more arbitrary in the future so that we can read the data from any + * source. This is actually made quite simple already since all data read in + * is handled by one single helper function and then palced into a FlexBuf for + * easy access by the other functions. The FlexBuf also allows for block + * reading from disk, which improves speed by a noticable amount. + * <br> + * There are also some extra features implemented that allow you to break the + * standard XML reader specs and eliminate leading and trailing whitespace in + * all read content. This is useful in situations where you allow additional + * whitespace in the files to make them easily human readable. The resturned + * content will be NULL in sitautions where all content between nodes was + * stripped. + *@author Mike Buland + */ +class XmlFileReader : public XmlReader +{ +public: + /** + * Construct an XmlReader around an xml file on your file system. + *@param sFile The file to read. + *@param bStrip Set to true to strip out leading and trailing whitespace in + * node contents. + */ + XmlFileReader( const char *sFile, bool bStrip=false ); + + /** + * Destroy the reader and cleanup. + */ + virtual ~XmlFileReader(); + +private: + char getChar( int nIndex = 0 ); + void usedChar( int nAmnt = 1 ); + FILE *fh; /**< The file handle. */ + FlexBuf fbDataIn; /**< The input buffer. */ +}; + +#endif diff --git a/src/old/xmlfilewriter.cpp b/src/old/xmlfilewriter.cpp new file mode 100644 index 0000000..3c6fb41 --- /dev/null +++ b/src/old/xmlfilewriter.cpp @@ -0,0 +1,28 @@ +#include <stdio.h> +#include <stdlib.h> +#include "xmlfilewriter.h" + +XmlFileWriter::XmlFileWriter( const char *sFileName, const char *sIndent, XmlNode *pRoot ) : + XmlWriter( sIndent, pRoot ) +{ + this->sFileName = sFileName; + fh = fopen( sFileName, "wt"); + fprintf( fh, "<?xml version=\"1.0\"?>\n"); +} + +XmlFileWriter::XmlFileWriter( FILE *fh, const char *sIndent, XmlNode *pRoot ) : + XmlWriter( sIndent, pRoot ), + fh( fh ) +{ +} + +XmlFileWriter::~XmlFileWriter() +{ + fclose( fh ); +} + +void XmlFileWriter::writeString( const char *sString ) +{ + fputs( sString, fh ); +} + diff --git a/src/old/xmlfilewriter.h b/src/old/xmlfilewriter.h new file mode 100644 index 0000000..e328f96 --- /dev/null +++ b/src/old/xmlfilewriter.h @@ -0,0 +1,45 @@ +#ifndef XML_FILE_WRITER +#define XML_FILE_WRITER + +#include "xmlnode.h" +#include "xmlwriter.h" + +/** + * Implements xml writing in the XML standard format. Also allows you to + * break that format and auto-indent your exported xml data for ease of + * reading. The auto-indenting will only be applied to sections that + * have no content of their own already. This means that except for + * whitespace all of your data will be preserved perfectly. + * You can create an XmlWriter object around a file, or access the static + * write function directly and just hand it a filename and a root XmlNode. + * When using an XmlWriter object the interface is identicle to that of + * the XmlDocument class, so reference that class for API info. However + * when the initial (or root) node is closed, and the document is finished + * the file will be created and written to automatically. The user can + * check to see if this is actually true by calling the isFinished + * function in the XmlDocument class. + *@author Mike Buland + */ +class XmlFileWriter : public XmlWriter +{ +public: + /** + * Construct a file writer around a given file. + *@param sFileName The file to create or overwrite and write XML into. + *@param sIndent The indent text to use, if any. + */ + XmlFileWriter( const char *sFileName, const char *sIndent=NULL, XmlNode *pRoot=NULL ); + XmlFileWriter( FILE *fh, const char *sIndent=NULL, XmlNode *pRoot=NULL ); + + /** + * Destroy the writer. + */ + virtual ~XmlFileWriter(); + +private: + void writeString( const char *sString ); + std::string sFileName; /**< The filename to write to. */ + FILE *fh; /**< The file handle to the open file. */ +}; + +#endif diff --git a/src/old/xmlnode.cpp b/src/old/xmlnode.cpp new file mode 100644 index 0000000..b1ed9a9 --- /dev/null +++ b/src/old/xmlnode.cpp @@ -0,0 +1,445 @@ +#include "xmlnode.h" +#include "hashfunctionstring.h" + +XmlNode::XmlNode( const char *sName, XmlNode *pParent, const char *sContent ) : + hProperties( new HashFunctionString(), 53, false ), + hChildren( new HashFunctionString(), 53, true ) +{ + this->pParent = pParent; + if( sName != NULL ) + { + setName( sName ); + } + if( sContent != NULL ) + { + this->sPreContent = new std::string( sContent ); + } + else + { + this->sPreContent = NULL; + } + nCurContent = 0; +} + +XmlNode::~XmlNode() +{ + for( int j = 0; j < lChildren.getSize(); j++ ) + { + delete (XmlNode *)lChildren[j]; + } + for( int j = 0; j < lPropNames.getSize(); j++ ) + { + delete (std::string *)lPropNames[j]; + } + for( int j = 0; j < lPropValues.getSize(); j++ ) + { + delete (std::string *)lPropValues[j]; + } + for( int j = 0; j < lPostContent.getSize(); j++ ) + { + if( lPostContent[j] != NULL ) + { + delete (std::string *)lPostContent[j]; + } + } + if( sPreContent ) + { + delete sPreContent; + } +} + +void XmlNode::setName( const char *sName ) +{ + if( pParent ) + { + if( this->sName.size() == 0 ) + { + // We're not in the hash yet, so add us + this->sName = sName; + pParent->hChildren.insert( this->sName.c_str(), this ); + } + else + { + // Slightly more tricky, delete us, then add us... + pParent->hChildren.del( this->sName.c_str() ); + this->sName = sName; + pParent->hChildren.insert( this->sName.c_str(), this ); + } + } + else + { + // If we have no parent, then just set the name string, we don't need + // to worry about hashing. + this->sName = sName; + } +} + +void XmlNode::setContent( const char *sContent, int nIndex ) +{ + if( nIndex == -1 ) + { + nIndex = nCurContent; + } + if( nIndex == 0 ) + { + if( this->sPreContent ) + { + delete this->sPreContent; + } + + this->sPreContent = new std::string( sContent ); + } + else + { + nIndex--; + if( lPostContent[nIndex] ) + { + delete (std::string *)lPostContent[nIndex]; + } + + lPostContent.setAt( nIndex, new std::string( sContent ) ); + } +} + +const char *XmlNode::getContent( int nIndex ) +{ + if( nIndex == 0 ) + { + if( sPreContent ) + { + return sPreContent->c_str(); + } + } + else + { + nIndex--; + if( lPostContent[nIndex] ) + { + return ((std::string *)lPostContent[nIndex])->c_str(); + } + } + + return NULL; +} + +XmlNode *XmlNode::addChild( const char *sName, const char *sContent ) +{ + return addChild( new XmlNode( sName, this, sContent ) ); +} + +XmlNode *XmlNode::addChild( XmlNode *pNode ) +{ + lChildren.append( pNode ); + lPostContent.append( NULL ); + nCurContent++; + pNode->pParent = this; + + return pNode; +} + +XmlNode *XmlNode::getParent() +{ + return pParent; +} + +void XmlNode::addProperty( const char *sName, const char *sValue ) +{ + std::string *pName = new std::string( sName ); + std::string *pValue = new std::string( sValue ); + + hProperties.insert( pName->c_str(), pValue->c_str() ); + lPropNames.append( pName ); + lPropValues.append( pValue ); +} + +int XmlNode::getNumProperties() +{ + return lPropNames.getSize(); +} + +const char *XmlNode::getPropertyName( int nIndex ) +{ + std::string *tmp = ((std::string *)lPropNames[nIndex]); + if( tmp == NULL ) + return NULL; + return tmp->c_str(); +} + +const char *XmlNode::getProperty( int nIndex ) +{ + std::string *tmp = ((std::string *)lPropValues[nIndex]); + if( tmp == NULL ) + return NULL; + return tmp->c_str(); +} + +const char *XmlNode::getProperty( const char *sName ) +{ + const char *tmp = (const char *)hProperties[sName]; + if( tmp == NULL ) + return NULL; + return tmp; +} + +void XmlNode::deleteProperty( int nIndex ) +{ + hProperties.del( ((std::string *)lPropNames[nIndex])->c_str() ); + + delete (std::string *)lPropNames[nIndex]; + delete (std::string *)lPropValues[nIndex]; + + lPropNames.deleteAt( nIndex ); + lPropValues.deleteAt( nIndex ); +} + +bool XmlNode::hasChildren() +{ + return lChildren.getSize()>0; +} + +int XmlNode::getNumChildren() +{ + return lChildren.getSize(); +} + +XmlNode *XmlNode::getChild( int nIndex ) +{ + return (XmlNode *)lChildren[nIndex]; +} + +XmlNode *XmlNode::getChild( const char *sName, int nSkip ) +{ + return (XmlNode *)hChildren.get( sName, nSkip ); +} + +const char *XmlNode::getName() +{ + return sName.c_str(); +} + +void XmlNode::deleteNode( int nIndex, const char *sReplacementText ) +{ + XmlNode *xRet = detatchNode( nIndex, sReplacementText ); + + if( xRet != NULL ) + { + delete xRet; + } +} + +XmlNode *XmlNode::detatchNode( int nIndex, const char *sReplacementText ) +{ + if( nIndex < 0 || nIndex >= lChildren.getSize() ) + return NULL; + + // The real trick when deleteing a node isn't actually deleting it, it's + // reforming the content around the node that's now missing...hmmm... + + if( nIndex == 0 ) + { + // If the index is zero we have to deal with the pre-content + if( sReplacementText ) + { + if( sPreContent == NULL ) + { + sPreContent = new std::string( sReplacementText ); + } + else + { + *sPreContent += sReplacementText; + } + } + if( lPostContent.getSize() > 0 ) + { + if( lPostContent[0] != NULL ) + { + if( sPreContent == NULL ) + { + sPreContent = new std::string( + ((std::string *)lPostContent[0])->c_str() + ); + } + else + { + *sPreContent += + ((std::string *)lPostContent[0])->c_str(); + } + } + delete (std::string *)lPostContent[0]; + lPostContent.deleteAt( 0 ); + } + } + else + { + int nCont = nIndex-1; + // If it's above zero we deal with the post-content only + if( sReplacementText ) + { + if( lPostContent[nCont] == NULL ) + { + lPostContent.setAt( nCont, new std::string( sReplacementText ) ); + } + else + { + *((std::string *)lPostContent[nCont]) += sReplacementText; + } + } + if( lPostContent.getSize() > nIndex ) + { + if( lPostContent[nIndex] != NULL ) + { + if( lPostContent[nCont] == NULL ) + { + lPostContent.setAt( nCont, new std::string( + ((std::string *)lPostContent[nIndex])->c_str() + ) ); + } + else + { + *((std::string *)lPostContent[nCont]) += + ((std::string *)lPostContent[nIndex])->c_str(); + } + } + delete (std::string *)lPostContent[nIndex]; + lPostContent.deleteAt( nIndex ); + } + } + + XmlNode *xRet = (XmlNode *)lChildren[nIndex]; + hChildren.del( ((XmlNode *)lChildren[nIndex])->getName() ); + lChildren.deleteAt( nIndex ); + + return xRet; +} + +void XmlNode::replaceNode( int nIndex, XmlNode *pNewNode ) +{ + if( nIndex < 0 || nIndex >= lChildren.getSize() ) + return; //TODO: throw an exception + + delete (XmlNode *)lChildren[nIndex]; + lChildren.setAt( nIndex, pNewNode ); + pNewNode->pParent = this; +} + +XmlNode *XmlNode::getCopy() +{ + XmlNode *pNew = new XmlNode(); + + pNew->sName = sName; + if( sPreContent ) + { + pNew->sPreContent = new std::string( sPreContent->c_str() ); + } + else + { + pNew->sPreContent = NULL; + } + pNew->nCurContent = 0; + + int nSize = lPostContent.getSize(); + pNew->lPostContent.setSize( nSize ); + for( int j = 0; j < nSize; j++ ) + { + if( lPostContent[j] ) + { + pNew->lPostContent.setAt( + j, new std::string( + ((std::string *)lPostContent[j])->c_str() + ) + ); + } + else + { + pNew->lPostContent.setAt( j, NULL ); + } + } + + nSize = lChildren.getSize(); + pNew->lChildren.setSize( nSize ); + for( int j = 0; j < nSize; j++ ) + { + XmlNode *pChild = ((XmlNode *)lChildren[j])->getCopy(); + pNew->lChildren.setAt( j, pChild ); + pChild->pParent = pNew; + pNew->hChildren.insert( pChild->getName(), pChild ); + } + + nSize = lPropNames.getSize(); + pNew->lPropNames.setSize( nSize ); + pNew->lPropValues.setSize( nSize ); + for( int j = 0; j < nSize; j++ ) + { + std::string *pProp = new std::string( ((std::string *)lPropNames[j])->c_str() ); + std::string *pVal = new std::string( ((std::string *)lPropValues[j])->c_str() ); + pNew->lPropNames.setAt( j, pProp ); + pNew->lPropValues.setAt( j, pVal ); + pNew->hProperties.insert( pProp->c_str(), pVal->c_str() ); + pNew->nCurContent++; + } + + return pNew; +} + +void XmlNode::deleteNodeKeepChildren( int nIndex ) +{ + // This is a tricky one...we need to do some patching to keep things all + // even... + XmlNode *xRet = (XmlNode *)lChildren[nIndex]; + + if( xRet == NULL ) + { + return; + } + else + { + if( getContent( nIndex ) ) + { + std::string sBuf( getContent( nIndex ) ); + sBuf += xRet->getContent( 0 ); + setContent( sBuf.c_str(), nIndex ); + } + else + { + setContent( xRet->getContent( 0 ), nIndex ); + } + + int nSize = xRet->lChildren.getSize(); + for( int j = 0; j < nSize; j++ ) + { + XmlNode *pCopy = ((XmlNode *)xRet->lChildren[j])->getCopy(); + pCopy->pParent = this; + lChildren.insertBefore( pCopy, nIndex+j ); + + if( xRet->lPostContent[j] ) + { + lPostContent.insertBefore( + new std::string( ((std::string *)xRet->lPostContent[j])->c_str() ), + nIndex+j + ); + } + else + { + lPostContent.insertBefore( NULL, nIndex+j ); + } + } + + if( getContent( nIndex+nSize ) ) + { + //SString sBuf( getContent( nIndex+nSize ) ); + //sBuf.catfrom( xRet->getContent( nSize ) ); + //setContent( sBuf, nIndex+nSize ); + } + else + { + setContent( xRet->getContent( nSize ), nIndex+nSize ); + } + + deleteNode( nIndex+nSize ); + } +} + +void XmlNode::replaceNodeWithChildren( int nIndex, XmlNode *pNewNode ) +{ +} + diff --git a/src/old/xmlnode.h b/src/old/xmlnode.h new file mode 100644 index 0000000..7525306 --- /dev/null +++ b/src/old/xmlnode.h @@ -0,0 +1,236 @@ +#ifndef XMLNODE +#define XMLNODE + +#include <iostream> +#include "linkedlist.h" +#include "hashtable.h" + +/** + * Maintains all data pertient to an XML node, including sub-nodes and content. + * All child nodes can be accessed through index and through name via a hash + * table. This makes it very easy to gain simple and fast access to all of + * your data. For most applications, the memory footprint is also rather + * small. While XmlNode objects can be used directly to create XML structures + * it is highly reccomended that all operations be performed through the + * XmlDocument class. + *@author Mike Buland + */ +class XmlNode +{ +public: + /** + * Construct a new XmlNode. + *@param sName The name of the node. + *@param pParent The parent node. + *@param sContent The initial content string. + */ + XmlNode( + const char *sName=NULL, + XmlNode *pParent = NULL, + const char *sContent=NULL + ); + + /** + * Delete the node and cleanup all memory. + */ + virtual ~XmlNode(); + + /** + * Change the name of the node. + *@param sName The new name of the node. + */ + void setName( const char *sName ); + + /** + * Construct a new node and add it as a child to this node, also return a + * pointer to the newly constructed node. + *@param sName The name of the new node. + *@param sContent The initial content of the new node. + *@returns A pointer to the newly created child node. + */ + XmlNode *addChild( const char *sName, const char *sContent=NULL ); + + /** + * Add an already created XmlNode as a child to this node. The new child + * XmlNode's parent will be changed appropriately and the parent XmlNode + * will take ownership of the child. + *@param pChild The child XmlNode to add to this XmlNode. + *@returns A pointer to the child node that was just added. + */ + XmlNode *addChild( XmlNode *pChild ); + + /** + * Add a new property to the XmlNode. Properties are name/value pairs. + *@param sName The name of the property. Specifying a name that's already + * in use will overwrite that property. + *@param sValue The textual value of the property. + */ + void addProperty( const char *sName, const char *sValue ); + + /** + * Get a pointer to the parent node, if any. + *@returns A pointer to the node's parent, or NULL if there isn't one. + */ + XmlNode *getParent(); + + /** + * Tells you if this node has children. + *@returns True if this node has at least one child, false otherwise. + */ + bool hasChildren(); + + /** + * Tells you how many children this node has. + *@returns The number of children this node has. + */ + int getNumChildren(); + + /** + * Get a child node at a specific index. + *@param nIndex The zero-based index of the child to retreive. + *@returns A pointer to the child, or NULL if you requested an invalid + * index. + */ + XmlNode *getChild( int nIndex ); + + /** + * Get a child with the specified name, and possibly skip value. For an + * explination of skip values see the HashTable. + *@param sName The name of the child to find. + *@param nSkip The number of nodes with that name to skip. + *@returns A pointer to the child, or NULL if no child with that name was + * found. + */ + XmlNode *getChild( const char *sName, int nSkip=0 ); + + /** + * Get a pointer to the name of this node. Do not change this, use setName + * instead. + *@returns A pointer to the name of this node. + */ + const char *getName(); + + /** + * Set the content of this node, optionally at a specific index. Using the + * default of -1 will set the content after the last added node. + *@param sContent The content string to use. + *@param nIndex The index of the content. + */ + void setContent( const char *sContent, int nIndex=-1 ); + + /** + * Get the content string at a given index, or zero for initial content. + *@param nIndex The index of the content. + *@returns A pointer to the content at that location. + */ + const char *getContent( int nIndex = 0 ); + + /** + * Get the number of properties in this node. + *@returns The number of properties in this node. + */ + int getNumProperties(); + + /** + * Get a property's name by index. + *@param nIndex The index of the property to examine. + *@returns A pointer to the name of the property specified, or NULL if none + * found. + */ + const char *getPropertyName( int nIndex ); + + /** + * Get a proprty's value by index. + *@param nIndex The index of the property to examine. + *@returns A pointer to the value of the property specified, or NULL if none + * found. + */ + const char *getProperty( int nIndex ); + + /** + * Get a propery's value by name. + *@param sName The name of the property to examine. + *@returns A pointer to the value of the property specified, or NULL if none + * found. + */ + const char *getProperty( const char *sName ); + + /** + * Delete a property by index. + *@param nIndex The index of the property to delete. + *@returns True if the property was found and deleted, false if it wasn't + * found. + */ + void deleteProperty( int nIndex ); + + /** + * Delete a child node, possibly replacing it with some text. This actually + * fixes all content strings around the newly deleted child node. + *@param nIndex The index of the node to delete. + *@param sReplacementText The optional text to replace the node with. + *@returns True of the node was found, and deleted, false if it wasn't + * found. + */ + void deleteNode( int nIndex, const char *sReplacementText = NULL ); + + /** + * Delete a given node, but move all of it's children and content up to + * replace the deleted node. All of the content of the child node is + * spliced seamlessly into place with the parent node's content. + *@param nIndex The node to delete. + *@returns True if the node was found and deleted, false if it wasn't. + */ + void deleteNodeKeepChildren( int nIndex ); + + /** + * Detatch a given child node from this node. This effectively works just + * like a deleteNode, except that instead of deleting the node it is removed + * and returned, and all ownership is given up. + *@param nIndex The index of the node to detatch. + *@param sReplacementText The optional text to replace the detatched node + * with. + *@returns A pointer to the newly detatched node, which then passes + * ownership to the caller. + */ + XmlNode *detatchNode( int nIndex, const char *sReplacementText = NULL ); + + /** + * Replace a given node with a different node that is not currently owned by + * this XmlNode or any ancestor. + *@param nIndex The index of the node to replace. + *@param pNewNode The new node to replace the old node with. + *@returns True if the node was found and replaced, false if it wasn't. + */ + void replaceNode( int nIndex, XmlNode *pNewNode ); + + /** + * Replace a given node with the children and content of a given node. + *@param nIndex The index of the node to replace. + *@param pNewNode The node that contains the children and content that will + * replace the node specified by nIndex. + *@returns True if the node was found and replaced, false if it wasn't. + */ + void replaceNodeWithChildren( int nIndex, XmlNode *pNewNode ); + + /** + * Get a copy of this node and all children. getCopy is recursive, so + * beware copying large trees of xml. + *@returns A newly created copy of this node and all of it's children. + */ + XmlNode *getCopy(); + +private: + std::string sName; /**< The name of the node. */ + std::string *sPreContent; /**< The content that goes before any node. */ + LinkedList lChildren; /**< The children. */ + LinkedList lPostContent; /**< The content that comes after children. */ + HashTable hProperties; /**< Property hashtable. */ + HashTable hChildren; /**< Children hashtable. */ + LinkedList lPropNames; /**< List of property names. */ + LinkedList lPropValues; /**< List of property values. */ + XmlNode *pParent; /**< A pointer to the parent of this node. */ + int nCurContent; /**< The current content we're on, for using the -1 on + setContent. */ +}; + +#endif diff --git a/src/old/xmlreader.cpp b/src/old/xmlreader.cpp new file mode 100644 index 0000000..18df69c --- /dev/null +++ b/src/old/xmlreader.cpp @@ -0,0 +1,602 @@ +#include "xmlreader.h" +#include "exceptions.h" +#include <string.h> +#include "hashfunctionstring.h" + +XmlReader::XmlReader( bool bStrip ) : + bStrip( bStrip ), + htEntity( new HashFunctionString(), 11 ) +{ +} + +XmlReader::~XmlReader() +{ + void *i = htEntity.getFirstItemPos(); + while( (i = htEntity.getNextItemPos( i ) ) ) + { + free( (char *)(htEntity.getItemID( i )) ); + delete (StaticString *)htEntity.getItemData( i ); + } +} + +void XmlReader::addEntity( const char *name, const char *value ) +{ + if( htEntity[name] ) return; + + char *sName = strdup( name ); + StaticString *sValue = new StaticString( value ); + + htEntity.insert( sName, sValue ); +} + +#define gcall( x ) if( x == false ) return false; + +bool XmlReader::isws( char chr ) +{ + return ( chr == ' ' || chr == '\t' || chr == '\n' || chr == '\r' ); +} + +bool XmlReader::ws() +{ + while( true ) + { + char chr = getChar(); + if( isws( chr ) ) + { + usedChar(); + } + else + { + return true; + } + } + return true; +} + +bool XmlReader::buildDoc() +{ + // take care of initial whitespace + gcall( ws() ); + textDecl(); + entity(); + addEntity("gt", ">"); + addEntity("lt", "<"); + addEntity("amp", "&"); + addEntity("apos", "\'"); + addEntity("quot", "\""); + gcall( node() ); + + return true; +} + +void XmlReader::textDecl() +{ + if( getChar() == '<' && getChar( 1 ) == '?' ) + { + usedChar( 2 ); + for(;;) + { + if( getChar() == '?' ) + { + if( getChar( 1 ) == '>' ) + { + usedChar( 2 ); + return; + } + } + usedChar(); + } + } +} + +void XmlReader::entity() +{ + for(;;) + { + ws(); + + if( getChar() == '<' && getChar( 1 ) == '!' ) + { + usedChar( 2 ); + ws(); + std::string buf; + for(;;) + { + char chr = getChar(); + usedChar(); + if( isws( chr ) ) break; + buf += chr; + } + + if( strcmp( buf.c_str(), "ENTITY") == 0 ) + { + ws(); + std::string name; + for(;;) + { + char chr = getChar(); + usedChar(); + if( isws( chr ) ) break; + name += chr; + } + ws(); + char quot = getChar(); + usedChar(); + if( quot != '\'' && quot != '\"' ) + { + throw XmlException( + "Only quoted entity values are supported." + ); + } + std::string value; + for(;;) + { + char chr = getChar(); + usedChar(); + if( chr == '&' ) + { + StaticString *tmp = getEscape(); + if( tmp == NULL ) throw XmlException("Entity thing"); + value += tmp->getString(); + delete tmp; + } + else if( chr == quot ) + { + break; + } + else + { + value += chr; + } + } + ws(); + if( getChar() == '>' ) + { + usedChar(); + + addEntity( name.c_str(), value.c_str() ); + } + else + { + throw XmlException( + "Malformed ENTITY: unexpected '%c' found.", + getChar() + ); + } + } + else + { + throw XmlException( + "Unsupported header symbol: %s", + buf.c_str() + ); + } + } + else + { + return; + } + } +} + +bool XmlReader::node() +{ + gcall( startNode() ) + + // At this point, we are closing the startNode + char chr = getChar(); + if( chr == '>' ) + { + usedChar(); + + // Now we process the guts of the node. + gcall( content() ); + } + else if( chr == '/' ) + { + // This is the tricky one, one more validation, then we close the node. + usedChar(); + if( getChar() == '>' ) + { + closeNode(); + usedChar(); + } + else + { + throw XmlException("Close node in singleNode malformed!"); + } + } + else + { + throw XmlException("Close node expected, but not found."); + return false; + } + + return true; +} + +bool XmlReader::startNode() +{ + if( getChar() == '<' ) + { + usedChar(); + + if( getChar() == '/' ) + { + // Heh, it's actually a close node, go figure + FlexBuf fbName; + usedChar(); + gcall( ws() ); + + while( true ) + { + char chr = getChar(); + if( isws( chr ) || chr == '>' ) + { + // Here we actually compare the name we got to the name + // we already set, they have to match exactly. + if( !strcasecmp( getCurrent()->getName(), fbName.getData() ) ) + { + closeNode(); + break; + } + else + { + throw XmlException("Got a mismatched node close tag."); + } + } + else + { + fbName.appendData( chr ); + usedChar(); + } + } + + gcall( ws() ); + if( getChar() == '>' ) + { + // Everything is cool. + usedChar(); + } + else + { + throw XmlException("Got extra junk data instead of node close tag."); + } + } + else + { + // We're good, format is consistant + addNode(); + + // Skip extra whitespace + gcall( ws() ); + gcall( name() ); + gcall( ws() ); + gcall( paramlist() ); + gcall( ws() ); + } + } + else + { + throw XmlException("Expected to find node opening char, '<'."); + } + + return true; +} + +bool XmlReader::name() +{ + FlexBuf fbName; + + while( true ) + { + char chr = getChar(); + if( isws( chr ) || chr == '>' || chr == '/' ) + { + setName( fbName.getData() ); + return true; + } + else + { + fbName.appendData( chr ); + usedChar(); + } + } + + return true; +} + +bool XmlReader::paramlist() +{ + while( true ) + { + char chr = getChar(); + if( chr == '/' || chr == '>' ) + { + return true; + } + else + { + gcall( param() ); + gcall( ws() ); + } + } + + return true; +} + +StaticString *XmlReader::getEscape() +{ + if( getChar( 1 ) == '#' ) + { + // If the entity starts with a # it's a character escape code + int base = 10; + usedChar( 2 ); + if( getChar() == 'x' ) + { + base = 16; + usedChar(); + } + char buf[4]; + int j = 0; + for( j = 0; getChar() != ';'; j++ ) + { + buf[j] = getChar(); + usedChar(); + } + usedChar(); + buf[j] = '\0'; + buf[0] = (char)strtol( buf, (char **)NULL, base ); + buf[1] = '\0'; + + return new StaticString( buf ); + } + else + { + // ...otherwise replace with the appropriate string... + std::string buf; + usedChar(); + for(;;) + { + char cbuf = getChar(); + usedChar(); + if( cbuf == ';' ) break; + buf += cbuf; + } + + StaticString *tmp = (StaticString *)htEntity[buf.c_str()]; + if( tmp == NULL ) return NULL; + + StaticString *ret = new StaticString( *tmp ); + return ret; + } +} + +bool XmlReader::param() +{ + FlexBuf fbName; + FlexBuf fbValue; + + while( true ) + { + char chr = getChar(); + if( isws( chr ) || chr == '=' ) + { + break; + } + else + { + fbName.appendData( chr ); + usedChar(); + } + } + + gcall( ws() ); + + if( getChar() == '=' ) + { + usedChar(); + + gcall( ws() ); + + char chr = getChar(); + if( chr == '"' ) + { + // Better quoted rhs + usedChar(); + + while( true ) + { + chr = getChar(); + if( chr == '"' ) + { + usedChar(); + addProperty( fbName.getData(), fbValue.getData() ); + return true; + } + else + { + if( chr == '&' ) + { + StaticString *tmp = getEscape(); + if( tmp == NULL ) return false; + fbValue.appendData( tmp->getString() ); + delete tmp; + } + else + { + fbValue.appendData( chr ); + usedChar(); + } + } + } + } + else + { + // Simple one-word rhs + while( true ) + { + chr = getChar(); + if( isws( chr ) || chr == '/' || chr == '>' ) + { + addProperty( fbName.getData(), fbValue.getData() ); + return true; + } + else + { + if( chr == '&' ) + { + StaticString *tmp = getEscape(); + if( tmp == NULL ) return false; + fbValue.appendData( tmp->getString() ); + delete tmp; + } + else + { + fbValue.appendData( chr ); + usedChar(); + } + } + } + } + } + else + { + throw XmlException("Expected an equals to seperate the params."); + return false; + } + + return true; +} + +bool XmlReader::content() +{ + FlexBuf fbContent; + + if( bStrip ) gcall( ws() ); + + while( true ) + { + char chr = getChar(); + if( chr == '<' ) + { + if( getChar(1) == '/' ) + { + if( fbContent.getLength() > 0 ) + { + if( bStrip ) + { + int j; + for( j = fbContent.getLength()-1; isws(fbContent.getData()[j]); j-- ); + ((char *)fbContent.getData())[j+1] = '\0'; + } + setContent( fbContent.getData() ); + } + usedChar( 2 ); + gcall( ws() ); + FlexBuf fbName; + while( true ) + { + chr = getChar(); + if( isws( chr ) || chr == '>' ) + { + if( !strcasecmp( getCurrent()->getName(), fbName.getData() ) ) + { + closeNode(); + break; + } + else + { + throw XmlException("Mismatched close tag found: <%s> to <%s>.", getCurrent()->getName(), fbName.getData() ); + } + } + else + { + fbName.appendData( chr ); + usedChar(); + } + } + gcall( ws() ); + if( getChar() == '>' ) + { + usedChar(); + return true; + } + else + { + throw XmlException("Malformed close tag."); + } + } + else if( getChar(1) == '!' ) + { + // We know it's a comment, let's see if it's proper + if( getChar(2) != '-' || + getChar(3) != '-' ) + { + // Not a valid XML comment + throw XmlException("Malformed comment start tag found."); + } + + usedChar( 4 ); + + // Now burn text until we find the close tag + for(;;) + { + if( getChar() == '-' ) + { + if( getChar( 1 ) == '-' ) + { + // The next one has to be a '>' now + if( getChar( 2 ) != '>' ) + { + throw XmlException("Malformed comment close tag found. You cannot have a '--' that isn't followed by a '>' in a comment."); + } + usedChar( 3 ); + break; + } + else + { + // Found a dash followed by a non dash, that's ok... + usedChar( 2 ); + } + } + else + { + // Burn comment chars + usedChar(); + } + } + } + else + { + if( fbContent.getLength() > 0 ) + { + if( bStrip ) + { + int j; + for( j = fbContent.getLength()-1; isws(fbContent.getData()[j]); j-- ); + ((char *)fbContent.getData())[j+1] = '\0'; + } + setContent( fbContent.getData() ); + fbContent.clearData(); + } + gcall( node() ); + } + + if( bStrip ) gcall( ws() ); + } + else if( chr == '&' ) + { + StaticString *tmp = getEscape(); + if( tmp == NULL ) return false; + fbContent.appendData( tmp->getString() ); + delete tmp; + } + else + { + fbContent.appendData( chr ); + usedChar(); + } + } +} + diff --git a/src/old/xmlreader.h b/src/old/xmlreader.h new file mode 100644 index 0000000..c8f7202 --- /dev/null +++ b/src/old/xmlreader.h @@ -0,0 +1,141 @@ +#ifndef XMLREADER +#define XMLREADER + +#include <stdio.h> +#include "xmldocument.h" +#include "flexbuf.h" +#include "hashtable.h" +#include "staticstring.h" + +/** + * Takes care of reading in xml formatted data from a file. This could/should + * be made more arbitrary in the future so that we can read the data from any + * source. This is actually made quite simple already since all data read in + * is handled by one single helper function and then palced into a FlexBuf for + * easy access by the other functions. The FlexBuf also allows for block + * reading from disk, which improves speed by a noticable amount. + * <br> + * There are also some extra features implemented that allow you to break the + * standard XML reader specs and eliminate leading and trailing whitespace in + * all read content. This is useful in situations where you allow additional + * whitespace in the files to make them easily human readable. The resturned + * content will be NULL in sitautions where all content between nodes was + * stripped. + *@author Mike Buland + */ +class XmlReader : public XmlDocument +{ +public: + /** + * Create a standard XmlReader. The optional parameter bStrip allows you to + * create a reader that will strip out all leading and trailing whitespace + * in content, a-la html. + *@param bStrip Strip out leading and trailing whitespace? + */ + XmlReader( bool bStrip=false ); + + /** + * Destroy this XmlReader. + */ + virtual ~XmlReader(); + + /** + * Build a document based on some kind of input. This is called + * automatically by the constructor. + */ + bool buildDoc(); + +private: + /** + * This is called by the low level automoton in order to get the next + * character. This function should return a character at the current + * position plus nIndex, but does not increment the current character. + *@param nIndex The index of the character from the current stream position. + *@returns A single character at the requested position, or 0 for end of + * stream. + */ + virtual char getChar( int nIndex = 0 ) = 0; + + /** + * Called to increment the current stream position by a single character. + */ + virtual void usedChar( int nAmnt = 1) = 0; + + /** + * Automoton function: is whitespace. + *@param chr A character + *@returns True if chr is whitespace, false otherwise. + */ + bool isws( char chr ); + + /** + * Automoton function: ws. Skips sections of whitespace. + *@returns True if everything was ok, False for end of stream. + */ + bool ws(); + + /** + * Automoton function: node. Processes an XmlNode + *@returns True if everything was ok, False for end of stream. + */ + bool node(); + + /** + * Automoton function: startNode. Processes the begining of a node. + *@returns True if everything was ok, False for end of stream. + */ + bool startNode(); + + /** + * Automoton function: name. Processes the name of a node. + *@returns True if everything was ok, False for end of stream. + */ + bool name(); + + /** + * Automoton function: textDecl. Processes the xml text decleration, if + * there is one. + */ + void textDecl(); + + /** + * Automoton function: entity. Processes an entity from the header. + */ + void entity(); + + /** + * Adds an entity to the list, if it doesn't already exist. + *@param name The name of the entity + *@param value The value of the entity + */ + void addEntity( const char *name, const char *value ); + + StaticString *getEscape(); + + /** + * Automoton function: paramlist. Processes a list of node params. + *@returns True if everything was ok, False for end of stream. + */ + bool paramlist(); + + /** + * Automoton function: param. Processes a single parameter. + *@returns True if everything was ok, False for end of stream. + */ + bool param(); + + /** + * Automoton function: content. Processes node content. + *@returns True if everything was ok, False for end of stream. + */ + bool content(); + + FlexBuf fbContent; /**< buffer for the current node's content. */ + FlexBuf fbParamName; /**< buffer for the current param's name. */ + FlexBuf fbParamValue; /**< buffer for the current param's value. */ + bool bStrip; /**< Are we stripping whitespace? */ + + HashTable htEntity; /**< Entity type definitions. */ +}; + +#endif diff --git a/src/old/xmlstringreader.cpp b/src/old/xmlstringreader.cpp new file mode 100644 index 0000000..3956ff3 --- /dev/null +++ b/src/old/xmlstringreader.cpp @@ -0,0 +1,38 @@ +#include "xmlstringreader.h" +#include "exceptions.h" +#include <string.h> + +XmlStringReader::XmlStringReader( const char *sString, bool bStrip ) + : XmlReader( bStrip ) +{ + this->sString = sString; + + nIndex = 0; + nLength = strlen( sString ); + + buildDoc(); +} + +XmlStringReader::~XmlStringReader() +{ +} + +char XmlStringReader::getChar( int nAdd ) +{ + if( nLength >= nIndex+nAdd+1 ) + { + return sString[nIndex+nAdd]; + } + else + { + throw XmlException("End of XML stream read."); + } +} + +void XmlStringReader::usedChar( int nAmnt ) +{ + if( nLength >= nIndex+nAmnt ) + { + nIndex += nAmnt; + } +} diff --git a/src/old/xmlstringreader.h b/src/old/xmlstringreader.h new file mode 100644 index 0000000..1239ef4 --- /dev/null +++ b/src/old/xmlstringreader.h @@ -0,0 +1,49 @@ +#ifndef XMLSTRINGREADER +#define XMLSTRINGREADER + +#include <stdio.h> +#include "xmlreader.h" +#include "flexbuf.h" + +/** + * Takes care of reading in xml formatted data from a file. This could/should + * be made more arbitrary in the future so that we can read the data from any + * source. This is actually made quite simple already since all data read in + * is handled by one single helper function and then palced into a FlexBuf for + * easy access by the other functions. The FlexBuf also allows for block + * reading from disk, which improves speed by a noticable amount. + * <br> + * There are also some extra features implemented that allow you to break the + * standard XML reader specs and eliminate leading and trailing whitespace in + * all read content. This is useful in situations where you allow additional + * whitespace in the files to make them easily human readable. The resturned + * content will be NULL in sitautions where all content between nodes was + * stripped. + *@author Mike Buland + */ +class XmlStringReader : public XmlReader +{ +public: + /** + * Create a new string reader around an already created and formatted + * null-terminated string. + *@param sString A pointer to the string data that will be used. This data + * is not changed during processing. + *@param bStrip Strip out leading and trailing whitespace. + */ + XmlStringReader( const char *sString, bool bStrip=false ); + + /** + * Destroy this string reader. + */ + virtual ~XmlStringReader(); + +private: + char getChar( int nIndex = 0 ); + void usedChar( int nAmnt = 1 ); + const char *sString; /**< Internal pointer to the input string. */ + int nIndex; /**< Our index into the string */ + int nLength; /**< The computed length of the string */ +}; + +#endif diff --git a/src/old/xmlstringwriter.cpp b/src/old/xmlstringwriter.cpp new file mode 100644 index 0000000..adeed6a --- /dev/null +++ b/src/old/xmlstringwriter.cpp @@ -0,0 +1,23 @@ +#include <stdio.h> +#include <stdlib.h> +#include "xmlstringwriter.h" + +XmlStringWriter::XmlStringWriter( const char *sIndent ) : + XmlWriter( sIndent ) +{ +} + +XmlStringWriter::~XmlStringWriter() +{ +} + +void XmlStringWriter::writeString( const char *sString ) +{ + sXml += sString; +} + +std::string &XmlStringWriter::getString() +{ + return sXml; +} + diff --git a/src/old/xmlstringwriter.h b/src/old/xmlstringwriter.h new file mode 100644 index 0000000..0d567b9 --- /dev/null +++ b/src/old/xmlstringwriter.h @@ -0,0 +1,50 @@ +#ifndef XML_STRING_WRITER +#define XML_STRING_WRITER + +#include "xmlnode.h" +#include "xmlwriter.h" + +/** + * Implements xml writing in the XML standard format. Also allows you to + * break that format and auto-indent your exported xml data for ease of + * reading. The auto-indenting will only be applied to sections that + * have no content of their own already. This means that except for + * whitespace all of your data will be preserved perfectly. + * You can create an XmlWriter object around a file, or access the static + * write function directly and just hand it a filename and a root XmlNode. + * When using an XmlWriter object the interface is identicle to that of + * the XmlDocument class, so reference that class for API info. However + * when the initial (or root) node is closed, and the document is finished + * the file will be created and written to automatically. The user can + * check to see if this is actually true by calling the isFinished + * function in the XmlDocument class. + *@author Mike Buland + */ +class XmlStringWriter : public XmlWriter +{ +public: + /** + * Construct a string writer using an internal string buffer. + *@param sIndent Optional indent to add to each line. + */ + XmlStringWriter( const char *sIndent=NULL ); + + /** + * Destroy the string writer and the internal string. + */ + virtual ~XmlStringWriter(); + + /** + * Get the string that was built. This is only valid after the document has + * been completed, so check isCompleted or be sure your addNode and + * closeNode calls match up. + *@returns A reference to the internal string object. + */ + std::string &getString(); + +private: + void writeString( const char *sString ); + std::string sXml; /**< The string object we "write" to. */ +}; + +#endif diff --git a/src/old/xmlwriter.cpp b/src/old/xmlwriter.cpp new file mode 100644 index 0000000..56880b6 --- /dev/null +++ b/src/old/xmlwriter.cpp @@ -0,0 +1,173 @@ +#include <stdio.h> +#include <stdlib.h> +#include "xmlwriter.h" + +XmlWriter::XmlWriter( const char *sIndent, XmlNode *pRoot ) : + XmlDocument( pRoot ) +{ + if( sIndent == NULL ) + { + this->sIndent = ""; + } + else + { + this->sIndent = sIndent; + } +} + +XmlWriter::~XmlWriter() +{ +} + +void XmlWriter::write() +{ + write( getRoot(), sIndent.c_str() ); +} + +void XmlWriter::write( XmlNode *pRoot, const char *sIndent ) +{ + writeNode( pRoot, 0, sIndent ); +} + +void XmlWriter::closeNode() +{ + XmlDocument::closeNode(); + + if( isCompleted() ) + { + write( getRoot(), sIndent.c_str() ); + } +} + +void XmlWriter::writeIndent( int nIndent, const char *sIndent ) +{ + if( sIndent == NULL ) return; + for( int j = 0; j < nIndent; j++ ) + { + writeString( sIndent ); + } +} + +std::string XmlWriter::escape( std::string sIn ) +{ + std::string sOut; + + std::string::const_iterator i; + for( i = sIn.begin(); i != sIn.end(); i++ ) + { + if( ((*i >= ' ' && *i <= '9') || + (*i >= 'a' && *i <= 'z') || + (*i >= 'A' && *i <= 'Z') ) && + (*i != '\"' && *i != '\'' && *i != '&') + ) + { + sOut += *i; + } + else + { + sOut += "&#"; + char buf[4]; + sprintf( buf, "%u", (unsigned char)*i ); + sOut += buf; + sOut += ';'; + } + } + + return sOut; +} + +void XmlWriter::writeNodeProps( XmlNode *pNode, int nIndent, const char *sIndent ) +{ + for( int j = 0; j < pNode->getNumProperties(); j++ ) + { + writeString(" "); + writeString( pNode->getPropertyName( j ) ); + writeString("=\""); + writeString( escape( pNode->getProperty( j ) ).c_str() ); + writeString("\""); + } +} + +void XmlWriter::writeNode( XmlNode *pNode, int nIndent, const char *sIndent ) +{ + if( pNode->hasChildren() ) + { + writeIndent( nIndent, sIndent ); + writeString("<"); + writeString( pNode->getName() ); + writeNodeProps( pNode, nIndent, sIndent ); + if( sIndent ) + writeString(">\n"); + else + writeString(">"); + + if( pNode->getContent( 0 ) ) + { + writeIndent( nIndent+1, sIndent ); + if( sIndent ) + { + writeString( pNode->getContent( 0 ) ); + writeString("\n"); + } + else + writeString( pNode->getContent( 0 ) ); + } + + int nNumChildren = pNode->getNumChildren(); + for( int j = 0; j < nNumChildren; j++ ) + { + writeNode( pNode->getChild( j ), nIndent+1, sIndent ); + if( pNode->getContent( j+1 ) ) + { + writeIndent( nIndent+1, sIndent ); + if( sIndent ) + { + writeString( pNode->getContent( j+1 ) ); + writeString("\n"); + } + else + writeString( pNode->getContent( j+1 ) ); + } + } + + writeIndent( nIndent, sIndent ); + if( sIndent ) + { + writeString("</"); + writeString( pNode->getName() ); + writeString(">\n"); + } + else + { + writeString("</"); + writeString( pNode->getName() ); + writeString(">"); + } + } + else if( pNode->getContent() ) + { + writeIndent( nIndent, sIndent ); + writeString("<"); + writeString( pNode->getName() ); + writeNodeProps( pNode, nIndent, sIndent ); + writeString(">"); + writeString( pNode->getContent() ); + writeString("</"); + writeString( pNode->getName() ); + writeString(">"); + if( sIndent ) + writeString("\n"); + } + else + { + writeIndent( nIndent, sIndent ); + writeString("<"); + writeString( pNode->getName() ); + writeNodeProps( pNode, nIndent, sIndent ); + if( sIndent ) + writeString("/>\n"); + else + writeString("/>"); + } +} + diff --git a/src/old/xmlwriter.h b/src/old/xmlwriter.h new file mode 100644 index 0000000..c48e810 --- /dev/null +++ b/src/old/xmlwriter.h @@ -0,0 +1,96 @@ +#ifndef XMLWRITER +#define XMLWRITER + +#include "xmlnode.h" +#include "xmldocument.h" + +/** + * Implements xml writing in the XML standard format. Also allows you to + * break that format and auto-indent your exported xml data for ease of + * reading. The auto-indenting will only be applied to sections that + * have no content of their own already. This means that except for + * whitespace all of your data will be preserved perfectly. + * You can create an XmlWriter object around a file, or access the static + * write function directly and just hand it a filename and a root XmlNode. + * When using an XmlWriter object the interface is identicle to that of + * the XmlDocument class, so reference that class for API info. However + * when the initial (or root) node is closed, and the document is finished + * the file will be created and written to automatically. The user can + * check to see if this is actually true by calling the isFinished + * function in the XmlDocument class. + *@author Mike Buland + */ +class XmlWriter : public XmlDocument +{ +public: + /** + * Construct a standard XmlWriter. + *@param sIndent Set this to something other than NULL to include it as an + * indent before each node in the output that doesn't already have content. + * If you are using the whitespace stripping option in the XmlReader and set + * this to a tab or some spaces it will never effect the content of your + * file. + */ + XmlWriter( const char *sIndent=NULL, XmlNode *pRoot=NULL ); + + /** + * Destroy the writer. + */ + virtual ~XmlWriter(); + + /** + * This override of the parent class closeNode function calls the parent + * class, but also triggers a write operation when the final node is closed. + * This means that by checking the isCompleted() function the user may also + * check to see if their file has been written or not. + */ + void closeNode(); + + void write(); + +private: + std::string sIndent; /**< The indent string */ + + std::string escape( std::string sIn ); + + /** + * Write the file. + *@param pNode The root node + *@param sIndent The indent text. + */ + void write( XmlNode *pNode, const char *sIndent=NULL ); + + /** + * Write a node in the file, including children. + *@param pNode The node to write. + *@param nIndent The indent level (the number of times to include sIndent) + *@param sIndent The indent text. + */ + void writeNode( XmlNode *pNode, int nIndent, const char *sIndent ); + + /** + * Write the properties of a node. + *@param pNode The node who's properties to write. + *@param nIndent The indent level of the containing node + *@param sIndent The indent text. + */ + void writeNodeProps( XmlNode *pNode, int nIndent, const char *sIndent ); + + /** + * Called to write the actual indent. + *@param nIndent The indent level. + *@param sIndent The indent text. + */ + void writeIndent( int nIndent, const char *sIndent ); + + /** + * This is the function that must be overridden in order to use this class. + * It must write the null-terminated string sString, minus the mull, + * verbatum to it's output device. Adding extra characters for any reason + * will break the XML formatting. + *@param sString The string data to write to the output. + */ + virtual void writeString( const char *sString ) = 0; +}; + +#endif -- cgit v1.2.3