summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMike Buland <eichlan@xagasoft.com>2006-05-01 17:11:04 +0000
committerMike Buland <eichlan@xagasoft.com>2006-05-01 17:11:04 +0000
commitf7a9549bd6ad83f2e0bceec9cddacfa5e3f84a54 (patch)
tree53cec4864776e07950e3c72f2a990a1017d08045 /src
downloadlibbu++-f7a9549bd6ad83f2e0bceec9cddacfa5e3f84a54.tar.gz
libbu++-f7a9549bd6ad83f2e0bceec9cddacfa5e3f84a54.tar.bz2
libbu++-f7a9549bd6ad83f2e0bceec9cddacfa5e3f84a54.tar.xz
libbu++-f7a9549bd6ad83f2e0bceec9cddacfa5e3f84a54.zip
libbu++ is finally laid out the way it should be, trunk, branches, and tags.
Diffstat (limited to 'src')
-rw-r--r--src/arraylist.cpp100
-rw-r--r--src/arraylist.h80
-rw-r--r--src/cgi.cpp644
-rw-r--r--src/cgi.h196
-rw-r--r--src/connection.cpp432
-rw-r--r--src/connection.h387
-rw-r--r--src/connectionmanager.cpp343
-rw-r--r--src/connectionmanager.h138
-rw-r--r--src/connectionmonitor.cpp23
-rw-r--r--src/connectionmonitor.h41
-rw-r--r--src/flexbuf.cpp206
-rw-r--r--src/flexbuf.h160
-rw-r--r--src/hashfunction.cpp10
-rw-r--r--src/hashfunction.h48
-rw-r--r--src/hashfunctioncasestring.cpp39
-rw-r--r--src/hashfunctioncasestring.h28
-rw-r--r--src/hashfunctionint.cpp20
-rw-r--r--src/hashfunctionint.h26
-rw-r--r--src/hashfunctionstring.cpp36
-rw-r--r--src/hashfunctionstring.h27
-rw-r--r--src/hashtable.cpp345
-rw-r--r--src/hashtable.h299
-rw-r--r--src/http.cpp371
-rw-r--r--src/http.h271
-rw-r--r--src/linkedlist.cpp227
-rw-r--r--src/linkedlist.h87
-rw-r--r--src/linkmessage.cpp53
-rw-r--r--src/linkmessage.h39
-rw-r--r--src/list.cpp27
-rw-r--r--src/list.h101
-rw-r--r--src/md5.cpp190
-rw-r--r--src/md5.h81
-rw-r--r--src/multilog.cpp143
-rw-r--r--src/multilog.h145
-rw-r--r--src/multilogchannel.cpp13
-rw-r--r--src/multilogchannel.h46
-rw-r--r--src/multilogtext.cpp152
-rw-r--r--src/multilogtext.h70
-rw-r--r--src/pproc.cpp60
-rw-r--r--src/pproc.h35
-rw-r--r--src/pqueue.cpp33
-rw-r--r--src/pqueue.h48
-rw-r--r--src/programchain.cpp113
-rw-r--r--src/programchain.h88
-rw-r--r--src/programlink.cpp71
-rw-r--r--src/programlink.h99
-rw-r--r--src/protocol.cpp31
-rw-r--r--src/protocol.h58
-rw-r--r--src/protocoltelnet.cpp315
-rw-r--r--src/protocoltelnet.h77
-rw-r--r--src/queue.cpp26
-rw-r--r--src/queue.h45
-rw-r--r--src/ringlist.cpp106
-rw-r--r--src/ringlist.h112
-rw-r--r--src/stack.cpp33
-rw-r--r--src/stack.h50
-rw-r--r--src/test/hashtest.cpp107
-rw-r--r--src/test/httpsrv/httpconnectionmonitor.cpp72
-rw-r--r--src/test/httpsrv/httpconnectionmonitor.h16
-rw-r--r--src/test/httpsrv/main.cpp21
-rw-r--r--src/test/md5test.cpp19
-rw-r--r--src/test/teltest/main.cpp21
-rw-r--r--src/test/teltest/telnetmonitor.cpp53
-rw-r--r--src/test/teltest/telnetmonitor.h26
-rw-r--r--src/test/xmlreadtest.cpp29
-rw-r--r--src/test/xmlrepltest.cpp31
-rw-r--r--src/test/xmlwritetest.cpp41
-rw-r--r--src/tokenstring.cpp172
-rw-r--r--src/tokenstring.h120
-rw-r--r--src/xmldocument.cpp142
-rw-r--r--src/xmldocument.h163
-rw-r--r--src/xmlfilereader.cpp63
-rw-r--r--src/xmlfilereader.h47
-rw-r--r--src/xmlfilewriter.cpp22
-rw-r--r--src/xmlfilewriter.h44
-rw-r--r--src/xmlnode.cpp454
-rw-r--r--src/xmlnode.h236
-rw-r--r--src/xmlreader.cpp412
-rw-r--r--src/xmlreader.h133
-rw-r--r--src/xmlstringreader.cpp37
-rw-r--r--src/xmlstringreader.h49
-rw-r--r--src/xmlstringwriter.cpp23
-rw-r--r--src/xmlstringwriter.h50
-rw-r--r--src/xmlwriter.cpp173
-rw-r--r--src/xmlwriter.h96
85 files changed, 9916 insertions, 0 deletions
diff --git a/src/arraylist.cpp b/src/arraylist.cpp
new file mode 100644
index 0000000..ef21426
--- /dev/null
+++ b/src/arraylist.cpp
@@ -0,0 +1,100 @@
1#include "arraylist.h"
2#include <stdlib.h>
3#include <string.h>
4
5ArrayList::ArrayList( int initSize, int growByFactor )
6{
7 apData = new void *[initSize];
8 nSize = 0;
9 nCapacity = initSize;
10 nGrowByFactor = growByFactor;
11}
12
13ArrayList::~ArrayList( )
14{
15 delete[] apData;
16}
17
18void *ArrayList::getAt( int index )
19{
20 if( index < 0 || index > nSize )
21 return NULL;
22
23 return apData[index];
24}
25
26void ArrayList::append( void *data )
27{
28 insertBefore( data, nSize );
29}
30
31void ArrayList::insertBefore( void *data, int pos )
32{
33 if( pos < 0 || pos > nSize )
34 return;
35
36 checkResize();
37 memmove( &apData[pos+1], &apData[pos], (nSize-pos)*sizeof(void*) );
38 apData[pos] = data;
39 nSize++;
40}
41
42int ArrayList::getSize( )
43{
44 return nSize;
45}
46
47bool ArrayList::isEmpty( )
48{
49 return nSize==0;
50}
51
52void ArrayList::deleteAt( int index )
53{
54 if( index < 0 || index >= nSize )
55 return;
56
57 memmove( &apData[index], &apData[index+1], (nSize-index-1)*sizeof(void *) );
58 nSize--;
59}
60
61void ArrayList::empty()
62{
63 // Probably the easiest as far as things go.
64 nSize = 0;
65}
66
67void ArrayList::resizeTo( int newSize )
68{
69 void **apNew = new void *[newSize];
70 memmove( apNew, apData, nSize*sizeof(void *) );
71 nCapacity = newSize;
72 delete[] apData;
73 apData = apNew;
74}
75
76void ArrayList::checkResize()
77{
78 if( nSize >= nCapacity )
79 {
80 resizeTo( nCapacity + nGrowByFactor );
81 }
82}
83
84void ArrayList::setSize( int newSize )
85{
86 if( newSize < 0 )
87 return;
88
89 nSize = newSize;
90 checkResize();
91}
92
93void ArrayList::setAt( int index, void *data )
94{
95 if( index < 0 || index >= nSize )
96 return;
97
98 apData[index] = data;
99}
100
diff --git a/src/arraylist.h b/src/arraylist.h
new file mode 100644
index 0000000..74992cf
--- /dev/null
+++ b/src/arraylist.h
@@ -0,0 +1,80 @@
1/** \file arraylist.h
2 * Describes the ArrayList class.
3 *@author Mike Buland
4 */
5#ifndef ARRAY_LIST_H
6#define ARRAY_LIST_H
7
8#include "list.h"
9
10/** A simple list which uses an array. This is a great choice if you won't do
11 * a lot of adding and deleting and need a fast random access list. Otherwise
12 * use the LinkedList.
13 *@author Mike Buland
14 */
15class ArrayList : public List
16{
17public:
18 /** Creates an arraylist with some pre-defined specs spelled out.
19 *@param initSize the inital number of elements to allocate.
20 *@param growByFactor How much to increase the size of the array by
21 * each time we run out of room.
22 */
23 ArrayList( int initSize=100, int growByFactor=10 );
24 /**
25 * Destroy the ArrayList
26 */
27 ~ArrayList();
28
29 void *getAt( int nIndex );
30 void append( void *pData );
31 void insertBefore( void *pData, int nPos = 0 );
32 int getSize( );
33 bool isEmpty( );
34 void deleteAt( int nIndex );
35 void empty();
36 void setSize( int nNewSize );
37 void setAt( int nIndex, void *pData );
38
39private:
40 /**
41 * Checks to see if the system needs to be resized, if it does, this will
42 * automatically resize based on your parameters.
43 */
44 void checkResize();
45
46 /**
47 * Resize the system to a specified size. If it is larger, then all data
48 * will be retained, if smaller the elements at the end will be cut off.
49 *@param newSize The number of elements to include after resizing.
50 */
51 void resizeTo( int newSize );
52
53 /**
54 * Actual master array of pointers. This is done to follow the List specs.
55 * All data transactions are performed with pointers or compatable
56 * primitive data-types.
57 */
58 void **apData;
59
60 /**
61 * The number of filled in elements in the array. This is the practical
62 * real size of the ArrayList for all userspace applications.
63 */
64 int nSize;
65
66 /**
67 * The number of elements allocated in memory. Not all of these have to be
68 * filled in, and it is usually larger than nSize so that adding and
69 * deleting elements is fast and easy.
70 */
71 int nCapacity;
72
73 /**
74 * The amount to grow by whenever the array needs resizing.
75 */
76 int nGrowByFactor;
77};
78
79#endif
80
diff --git a/src/cgi.cpp b/src/cgi.cpp
new file mode 100644
index 0000000..1fecbbe
--- /dev/null
+++ b/src/cgi.cpp
@@ -0,0 +1,644 @@
1#include <string.h>
2#include <stdlib.h>
3#include <stdio.h>
4#include <stdarg.h>
5#include <sys/stat.h>
6
7#include "cgi.h"
8
9Cgi::Cgi( const char *strSource ) :
10 aContent( new HashFunctionString(), 151, true )
11{
12 int length, j, k, mode = 0, slen = 0;
13 char hexbuf[3] = { 0, 0, 0 };
14 char *buf, chr;
15 Item *cur = NULL;
16 int nCur = 0;
17
18 if( strSource != NULL )
19 {
20 loadContent( strSource );
21 }
22
23 if( ( getenv( "CONTENT_LENGTH" ) ) )
24 {
25 if( !strcmp
26 ( getenv( "CONTENT_TYPE" ),
27 "application/x-www-form-urlencoded" ) )
28 {
29 length = atoi( getenv( "CONTENT_LENGTH" ) );
30 buf = new char[length + 1];
31 fread( buf, 1, length, stdin );
32 cur = new Item( );
33 aVars.append( cur );
34 cur->type = VAR_STDINPUT;
35 for( j = 0; j < length; j++ )
36 {
37 switch ( buf[j] )
38 {
39 case '=':
40 cur->name = new char[slen + 1];
41 slen = 0;
42 break;
43
44 case '&':
45 cur->value = new char[slen + 1];
46 cur->len = slen;
47 slen = 0;
48 cur = new Item( );
49 aVars.append( cur );
50 cur->type = VAR_STDINPUT;
51 break;
52
53 default:
54 switch ( buf[j] )
55 {
56 case '%': /* per-cents mean a hex-code for an ASCII char */
57 j += 2;
58 slen++;
59 break;
60
61 default: /* Nothing special, move along, folks... */
62 slen++;
63 break;
64 }
65 break;
66 }
67 }
68 cur->value = new char[slen + 1];
69 cur->len = slen;
70 slen = 0;
71 mode = 0;
72 cur = ( Item * ) aVars.getAt( 0 );
73 k = 0;
74 nCur = 0;
75 for( j = 0; j < length; j++ )
76 {
77 switch ( buf[j] )
78 {
79 case '=':
80 mode = 1;
81 k = 0;
82 break;
83
84 case '&':
85 mode = 0;
86 k = 0;
87 nCur++;
88 cur = ( Item * ) aVars.getAt( nCur );
89 break;
90
91 default:
92 switch ( buf[j] )
93 {
94 case '%': /* per-cents mean a hex-code for an ASCII char */
95 hexbuf[0] = buf[++j];
96 hexbuf[1] = buf[++j];
97 chr = ( char ) ( strtol( hexbuf, NULL, 16 ) );
98 break;
99
100 case '+': /* Pluses mean spaces, odd, I know... */
101 chr = ' ';
102 break;
103
104 default: /* Nothing special, move along, folks... */
105 chr = buf[j];
106 break;
107 }
108 if( mode == 0 )
109 {
110 cur->name[k] = chr;
111 cur->name[++k] = '\0';
112 }
113 else
114 {
115 cur->value[k] = chr;
116 cur->value[++k] = '\0';
117 }
118 break;
119 }
120 }
121 delete buf;
122 }
123 else if( !strncmp
124 ( getenv( "CONTENT_TYPE" ), "multipart/form-data;", 20 ) )
125 {
126 char *boundary, *oname;
127 int blen, j, k, olen;
128
129 length = atoi( getenv( "CONTENT_LENGTH" ) );
130 buf = new char[length + 1];
131 fread( buf, 1, length, stdin );
132 for( blen = 0; buf[blen + 1] != '\n'; blen++ );
133 boundary = new char[blen + 1];
134 memcpy( boundary, buf, blen );
135 boundary[blen] = '\0';
136 j = blen + 2;
137 for( ;; )
138 {
139 cur = new Item( );
140 aVars.append( cur );
141 cur->type = VAR_STDINPUT;
142 if( !strncmp
143 ( buf + j, "Content-Disposition: form-data; name=\"",
144 38 ) )
145 {
146 j += 38;
147 for( k = 0; buf[j + k] != '\"'; k++ );
148 oname = cur->name = new char[k + 1];
149 memcpy( cur->name, buf + j, k );
150 olen = k;
151 cur->name[k] = '\0';
152 j += k + 1;
153 if( !strncmp( buf + j, "; filename=\"", 12 ) ) /* Must be a file */
154 {
155 /* Acquire file name */
156 j += 12;
157 for( k = 0; buf[j + k] != '\"'; k++ );
158 cur->value = new char[k + 1];
159 memcpy( cur->value, buf + j, k );
160 cur->value[k] = '\0';
161 cur->len = k;
162 j += k + 3;
163
164 /* Acquire content type */
165 if( !strncmp( "Content-Type: ", buf + j, 14 ) )
166 {
167 j += 14;
168 cur = new Item( );
169 aVars.append( cur );
170 cur->type = VAR_STDINPUT;
171 cur->name = new char[olen + 1];
172 memcpy( cur->name, oname, olen + 1 );
173 for( k = 0; buf[j + k + 1] != '\n'; k++ );
174 cur->value = new char[k + 1];
175 memcpy( cur->value, buf + j, k );
176 cur->value[k] = '\0';
177 cur->len = k;
178 j += k;
179 }
180 else
181 {
182 cur = new Item( );
183 aVars.append( cur );
184 cur->type = VAR_STDINPUT;
185 cur->name = new char[olen + 1];
186 memcpy( cur->name, oname, olen + 1 );
187 cur->value = new char[1];
188 cur->value[0] = '\0';
189 cur->len = 0;
190 }
191 j += 4;
192
193 /* Acquire content */
194 cur = new Item( );
195 aVars.append( cur );
196 cur->type = VAR_STDINPUT;
197 cur->name = new char[olen + 1];
198 memcpy( cur->name, oname, olen + 1 );
199 if( !strncmp( buf + j + k, boundary, blen ) )
200 {
201 cur->value = new char[1];
202 cur->value[0] = '\0';
203 j += blen + 4;
204 }
205 else if( !strncmp( buf + j + k + 1, boundary, blen ) )
206 {
207 cur->value = new char[1];
208 cur->value[0] = '\0';
209 j += blen + 5;
210 }
211 else
212 {
213 for( k = 0;
214 strncmp( buf + j + k + 2, boundary, blen );
215 k++ );
216 cur->value = new char[k + 1];
217 memcpy( cur->value, buf + j, k );
218 cur->value[k] = '\0';
219 cur->len = k;
220 j += k + blen + 4;
221 }
222 }
223 else
224 {
225 j += 4;
226 for( k = 0;
227 strncmp( buf + j + k + 2, boundary, blen );
228 k++ );
229 cur->value = new char[k + 1];
230 memcpy( cur->value, buf + j, k );
231 cur->value[k] = '\0';
232 cur->len = k;
233 j += k + blen + 4;
234 }
235 if( buf[j + 1] == '\n' )
236 j += 2;
237 if( j >= length )
238 break;
239 }
240 else
241 {
242 cur->name = ( char * ) "ERROR";
243 cur->value = ( char * ) "Error here";
244 }
245 }
246 }
247 delete buf;
248 }
249
250 if( ( buf = getenv( "HTTP_COOKIE" ) ) )
251 {
252 int lbase = aVars.getSize( );
253 length = strlen( buf );
254 cur = new Item( );
255 aVars.append( cur );
256 cur->type = VAR_COOKIE;
257 for( j = 0; j < length; j++ )
258 {
259 switch ( buf[j] )
260 {
261 case '=':
262 cur->name = new char[slen + 1];
263 slen = 0;
264 break;
265
266 case ';':
267 cur->value = new char[slen + 1];
268 cur->len = slen;
269 slen = 0;
270 cur = new Item( );
271 aVars.append( cur );
272 cur->type = VAR_COOKIE;
273 break;
274
275 default:
276 switch ( buf[j] )
277 {
278 case '%': /* per-cents mean a hex-code for an ASCII char */
279 j += 2;
280 slen++;
281 break;
282
283 default: /* Nothing special, move along, folks... */
284 slen++;
285 break;
286 }
287 break;
288 }
289 }
290 cur->value = new char[slen + 1];
291 cur->len = slen;
292 slen = 0;
293 cur = ( Item * ) aVars.getAt( lbase );
294 mode = 0;
295 k = 0;
296 nCur = lbase;
297 for( j = 0; j < length; j++ )
298 {
299 switch ( buf[j] )
300 {
301 case '=':
302 mode = 1;
303 k = 0;
304 break;
305
306 case ';':
307 mode = 0;
308 k = 0;
309 nCur++;
310 cur = ( Item * ) aVars.getAt( nCur );
311 break;
312
313 default:
314 switch ( buf[j] )
315 {
316 case '%': /* per-cents mean a hex-code for an ASCII char */
317 hexbuf[0] = buf[++j];
318 hexbuf[1] = buf[++j];
319 chr = ( char ) ( strtol( hexbuf, NULL, 16 ) );
320 break;
321
322 case '+': /* Pluses mean spaces, odd, I know... */
323 chr = ' ';
324 break;
325
326 case ' ':
327 continue;
328 break;
329
330 default: /* Nothing special, move along, folks... */
331 chr = buf[j];
332 break;
333 }
334 if( mode == 0 )
335 {
336 cur->name[k] = chr;
337 cur->name[++k] = '\0';
338 }
339 else
340 {
341 cur->value[k] = chr;
342 cur->value[++k] = '\0';
343 }
344 break;
345 }
346 }
347 }
348
349 if( ( buf = getenv( "QUERY_STRING" ) ) )
350 {
351 if( strlen( buf ) > 0 )
352 {
353 int lbase = aVars.getSize( );
354 length = strlen( buf );
355 cur = new Item( );
356 aVars.append( cur );
357 cur->type = VAR_CMDLINE;
358 for( j = 0; j < length; j++ )
359 {
360 switch ( buf[j] )
361 {
362 case '=':
363 cur->name = new char[slen + 1];
364 slen = 0;
365 break;
366
367 case '&':
368 cur->value = new char[slen + 1];
369 cur->len = slen;
370 slen = 0;
371 cur = new Item( );
372 aVars.append( cur );
373 cur->type = VAR_CMDLINE;
374 break;
375
376 default:
377 switch ( buf[j] )
378 {
379 case '%': /* per-cents mean a hex-code for an ASCII char */
380 j += 2;
381 slen++;
382 break;
383
384 default: /* Nothing special, move along, folks... */
385 slen++;
386 break;
387 }
388 break;
389 }
390 }
391 cur->value = new char[slen + 1];
392 cur->len = slen;
393 slen = 0;
394 cur = ( Item * ) aVars.getAt( lbase );
395 nCur = lbase;
396 mode = 0;
397 k = 0;
398 for( j = 0; j < length; j++ )
399 {
400 switch ( buf[j] )
401 {
402 case '=':
403 mode = 1;
404 k = 0;
405 break;
406
407 case '&':
408 mode = 0;
409 k = 0;
410 nCur++;
411 cur = ( Item * ) aVars.getAt( nCur );
412 break;
413
414 default:
415 switch ( buf[j] )
416 {
417 case '%': /* per-cents mean a hex-code for an ASCII char */
418 hexbuf[0] = buf[++j];
419 hexbuf[1] = buf[++j];
420 chr = ( char ) ( strtol( hexbuf, NULL, 16 ) );
421 break;
422
423 case '+': /* Pluses mean spaces, odd, I know... */
424 chr = ' ';
425 break;
426
427 default: /* Nothing special, move along, folks... */
428 chr = buf[j];
429 break;
430 }
431 if( mode == 0 )
432 {
433 cur->name[k] = chr;
434 cur->name[++k] = '\0';
435 }
436 else
437 {
438 cur->value[k] = chr;
439 cur->value[++k] = '\0';
440 }
441 break;
442 }
443 }
444 }
445 }
446}
447
448Cgi::~Cgi( )
449{
450}
451
452char *Cgi::getVarValue( const char *name, int skip, unsigned char type )
453{
454 for( int j = 0; j < aVars.getSize( ); j++ )
455 {
456 Item *cur = ( Item * ) aVars.getAt( j );
457 if( !strcmp( cur->name, name ) )
458 {
459 if( ( cur->type & type ) )
460 {
461 if( skip <= 0 )
462 {
463 return cur->value;
464 }
465 else
466 {
467 skip--;
468 }
469 }
470 }
471 }
472 return NULL;
473}
474
475int Cgi::getVarLength( const char *name, int skip, unsigned char type )
476{
477 for( int j = 0; j < aVars.getSize( ); j++ )
478 {
479 Item *cur = ( Item * ) aVars.getAt( j );
480 if( !strcmp( cur->name, name ) )
481 {
482 if( ( cur->type & type ) )
483 {
484 if( skip <= 0 )
485 {
486 return cur->len;
487 }
488 else
489 {
490 skip--;
491 }
492 }
493 }
494 }
495 return -1;
496}
497
498void Cgi::writeDebugInfo()
499{
500 printf( "<pre>\n" );
501 printf( "0x%02X - stdInput | 0x%02X - cookie | 0x%02X - cmdLine\n\n",
502 VAR_STDINPUT, VAR_COOKIE, VAR_CMDLINE );
503 for( int j = 0; j < aVars.getSize( ); j++ )
504 {
505 Item *item = ( Item * ) aVars.getAt( j );
506 printf("[%s] = \"%s\" [0x%02X]\n", item->name,
507 item->value, item->type );
508 }
509 printf( "</pre>\n" );
510}
511
512void Cgi::writeContentHeader( int type )
513{
514 switch( type )
515 {
516 case headerHTML:
517 printf("Content-type: text/html\n\n");
518 break;
519 }
520}
521
522void Cgi::writeContent( const char *name, ...)
523{
524 char *templ = (char *)aContent.get(name);
525
526 if( templ )
527 {
528 va_list ap;
529
530 va_start (ap, name);
531 vprintf (templ, ap);
532 va_end (ap);
533 }
534 else
535 {
536 printf("Error finding content labeled \"%s\"\n", name );
537 }
538}
539
540void Cgi::loadContent( const char *strSource )
541{
542 FILE *fh = NULL;
543 if( strSource == NULL )
544 {
545 extern char *program_invocation_short_name;
546 char *tmpName = new char[strlen(program_invocation_short_name)+10];
547 memset( tmpName, 0, strlen(program_invocation_short_name)+10 );
548 strcpy( tmpName, program_invocation_short_name );
549 strcat( tmpName, ".content" );
550 fh = fopen( tmpName, "rt" );
551 delete tmpName;
552 }
553 else
554 {
555 fh = fopen( strSource, "rt" );
556 }
557
558 if( fh == NULL ) return;
559
560 struct stat xStats;
561
562 fstat( fileno( fh ), &xStats );
563
564 char *bigBuf = new char[xStats.st_size+1];
565 memset( bigBuf, 0, xStats.st_size+1 );
566 fread( bigBuf, 1, xStats.st_size, fh );
567 fclose( fh );
568
569 // Now we can actually load stuff from the file, first we need to make us up a format...
570 int lSize=0;
571 struct Content
572 {
573 char *name;
574 char *value;
575 } xCont;
576 int j = 0;
577 while( j < xStats.st_size )
578 {
579 // We're looking for a content-block init statement
580 for( ; j < xStats.st_size; j++ )
581 {
582 if( bigBuf[j] == '#' )
583 {
584 if( bigBuf[j+1] == '{' )
585 {
586 break;
587 }
588 }
589 }
590 j=j+2;
591 if( j >= xStats.st_size ) break;
592 for( ; bigBuf[j] == ' ' || bigBuf[j] == '\t'; j++ );
593 for( lSize = 0; lSize+j < xStats.st_size && bigBuf[lSize+j] != '\n' && bigBuf[lSize+j] != '\r'; lSize++ );
594 xCont.name = new char[lSize+1];
595 memset( xCont.name, 0, lSize+1 );
596 memcpy( xCont.name, &bigBuf[j], lSize );
597 j += lSize+1;
598
599 for( lSize = 0; lSize+j < xStats.st_size; lSize++ )
600 {
601 if( bigBuf[lSize+j] == '#' )
602 {
603 if( bigBuf[lSize+j+1] == '}' )
604 {
605 break;
606 }
607 }
608 }
609 xCont.value = new char[lSize+1];
610 memset( xCont.value, 0, lSize+1 );
611 memcpy( xCont.value, &bigBuf[j], lSize );
612
613 aContent.insert( xCont.name, xCont.value );
614
615 j += lSize + 2;
616 }
617}
618
619void Cgi::writeCookie( char const *name, char const *value, char const *expires, char const *path, char const *domain, bool secure )
620{
621 printf("Set-Cookie: %s=%s", name, value );
622
623 if( expires != NULL )
624 {
625 printf("; expires=%s", expires );
626 }
627
628 if( path != NULL )
629 {
630 printf("; path=%s", path );
631 }
632
633 if( domain != NULL )
634 {
635 printf("; domain=%s", domain );
636 }
637
638 if( secure )
639 {
640 printf("; secure");
641 }
642
643 printf("\n");
644}
diff --git a/src/cgi.h b/src/cgi.h
new file mode 100644
index 0000000..8e9a584
--- /dev/null
+++ b/src/cgi.h
@@ -0,0 +1,196 @@
1/**\file cgi.h
2 * Describes extra params needed to use the Cgi class as well as the class
3 * itself.
4 *@author Mike Buland
5 */
6
7#include "linkedlist.h"
8#include "hashtable.h"
9#include "hashfunctionstring.h"
10
11#define VAR_STDINPUT 0x01 /**< Variable came from stdinput, web form */
12#define VAR_COOKIE 0x02 /**< Variable came from a cookie */
13#define VAR_CMDLINE 0x04 /**< Variable came from commandline / uri */
14#define VAR_ANY 0xFF /**< Mask including all other types */
15
16/**
17 * Cgi header processor originally designed for apache cgi programs. When used
18 * from apache with what I beleive are some sort of standard set of command
19 * line parameters and environment variables. This always worked for all of my
20 * purposes. This class will automatically extract all data from the system
21 * that you need and places it into tables and things for easy access.
22 * There are three types of input that data can come from, StandardInput,
23 * CommandLine, and Cookies. StandardInput is when you get formdata in
24 * multi-part forms, Cookies should usually be cookies that you set, and
25 * command line is everything after the question mark in the URL.
26 * This also contains some simple helpers for putting templated data into the
27 * HTTP data feed.
28 *@author Mike Buland
29 */
30class Cgi
31{
32public:
33 /**
34 * Create a complete CGI object, this object will automatically read data
35 * from all available sources and be ready for use on the very next line!
36 * If strSource is filled in it will also automatically read in a content
37 * file, which is a simple file format containing named blocks of reusable
38 * templates.
39 *@param strSource Set to a filename in order to load up a content file.
40 */
41 Cgi( const char *strSource = NULL );
42
43 /**
44 * Destroy the cgi object.
45 */
46 ~Cgi( );
47
48 /**
49 * Get's the value for a variable as a character string. The name is the
50 * name that was given on the URL or in the form or cookie. Skip can be
51 * set to any value above zero to retreive subsequent variables with the
52 * same name. The most obvious use of this is when dealing with file
53 * uploads, each file upload sends you three variables with the same name
54 * and different content. Finally the variable type determines where you
55 * will accept this variable from. This is generally a bit of a security
56 * thing, if you store login info in a cookie and don't want people getting
57 * in by faking the appropriate URL.
58 *@param name The name of the variable you wish to retreive.
59 *@param skip THe number of variables with the given name to skip before
60 * returning something meaningful. The only way to determine how many
61 * variables with the same name there are is to skip until you get a NULL
62 * value returned.
63 *@param type Can be set to any combination of VAR_STDINPUT, VAR_COOKIE,
64 * VAR_CMDLINE, or just VAR_ANY. This takes bitflags, so you can or the
65 * values together. If a variable is found but came from the wrong source
66 * it won't match any other criteria and will be treated as though it
67 * doesn't exist.
68 *@returns A null-terminated string representing the value of the requested
69 * variable, or NULL if the variable did not exist. If a variable does
70 * exist but has no value the string returned will start with a NULL char,
71 * but be a valid string.
72 */
73 char *getVarValue( const char *name, int skip=0, unsigned char type=VAR_ANY );
74
75 /**
76 * This functions identically in every way to getVarValue, except that
77 * instead of returning a pointer to the variable's value, it returns the
78 * length of the variable's value string. The params are the same and so
79 * a call to both functions with the same params should yeild a value and
80 * a corresponding length.
81 *@param name The name of the variable you wish to retreive.
82 *@param skip THe number of variables with the given name to skip before
83 * returning something meaningful. The only way to determine how many
84 * variables with the same name there are is to skip until you get a NULL
85 * value returned.
86 *@param type Can be set to any combination of VAR_STDINPUT, VAR_COOKIE,
87 * VAR_CMDLINE, or just VAR_ANY. This takes bitflags, so you can or the
88 * values together. If a variable is found but came from the wrong source
89 * it won't match any other criteria and will be treated as though it
90 * doesn't exist.
91 *@returns The length of the value-string of the requested variable. If
92 * the requested variable is not found, -1 is returned.
93 */
94 int getVarLength( const char *name, int skip=0, unsigned char type=VAR_ANY );
95
96 /**
97 * A handy little function that writes a load of debug info related to
98 * parsing CGI params to the standard output in html. This is generally
99 * best used at the end of a page.
100 */
101 void writeDebugInfo();
102
103 /**
104 * Write a content header to the standard output. This should also be the
105 * first thing that you do (except for writing cookies) after initializing
106 * the Cgi class. You can select a type of header or content from the
107 * header enum, and a properly formatted header will show up on the
108 * standard output.
109 *@param type Any value from the header enum in this class. The default is
110 * to write an html header, probably the most common as well.
111 */
112 void writeContentHeader( int type=headerHTML );
113
114 /**
115 * Write content to the stnadard output. The content variable should have
116 * been loaded during construction of the Cgi object or with the
117 * loadContent function. The content variable should be formatted just like
118 * a printf string, so that anything you want to put into it will have a %
119 * symbol replacement code, like %s, %d, etc. Since this actually uses a
120 * type of printf function everything from those docs work here.
121 *@param name The name of the content variable to format and write to
122 * stnadard output.
123 *@param ... As many params as you want to include, ala printf.
124 */
125 void writeContent( const char *name, ...);
126
127 /**
128 * Load a content file. I don't want to describe the format here, you can
129 * just read the code or find an example for now. Sorry.
130 *@param strSource The name of the file to open and read in to get the
131 * content loaded.
132 */
133 void loadContent( const char *strSource = NULL );
134
135 /**
136 * Write a cookie-set header to the output stream. This should be done
137 * before any other content-headers are written. The specifics of this
138 * function are very simple, since I rely on the user's understanding of
139 * how standard HTTP/1.1 or HTTP/1.0 cookie syntax works. If you don't
140 * care then just use the name and value and the defaults should keep you
141 * in good stead for a long time.
142 *@param name The name of the cookie variable to set.
143 *@param value The value to set to that variable.
144 *@param expires The formatted string value for the date and time this
145 * cookie should expire. A NULL here will put a "until the browser closes"
146 * tag in.
147 *@param path The path (URL) that this cookie belongs to. If you run a lot
148 * of hosted servers or sub-sites that may have some shared URL bits then
149 * you may want to set this. The cookie should only be sent to URL's that
150 * match this as their first part.
151 *@param domain The domain that is allowed to read this, if not set, it's
152 * the domain the web browser contacted when they got the cookie.
153 *@param secure I'm not sure, I think it's something to tell if the cookie
154 * is safe to keep because any potentially valuable data is encypted or
155 * otherwise unusable. I could be wrong.
156 */
157 void writeCookie( char const *name, char const *value, char const *expires=NULL, char const *path=NULL, char const *domain=NULL, bool secure=false );
158
159 /**
160 * A simple helper class to contain variable data.
161 */
162 class Item
163 {
164 public:
165 /**
166 * Build an empty Item.
167 */
168 Item( )
169 {
170 name = NULL;
171 value = NULL;
172 len = 0;
173 type = 0;
174 }
175 /** The name of the item. */
176 char *name;
177 /** The value of the item. */
178 char *value;
179 /** The length of the item's value. */
180 unsigned long len;
181 /** The type of the item (where it came from). */
182 unsigned char type;
183 };
184
185 /** Header values */
186 enum
187 {
188 headerHTML
189 };
190
191private:
192 /** Keeps track of all contained variables. */
193 LinkedList aVars;
194 /** Keeps track of all content variables. */
195 HashTable aContent;
196};
diff --git a/src/connection.cpp b/src/connection.cpp
new file mode 100644
index 0000000..a277ea7
--- /dev/null
+++ b/src/connection.cpp
@@ -0,0 +1,432 @@
1#include "connection.h"
2#include <string.h>
3#include <stdio.h>
4#include <errno.h>
5#include <stdlib.h>
6#include <unistd.h>
7#include <sys/types.h>
8#include <sys/socket.h>
9#include <netinet/in.h>
10#include <netdb.h>
11#include <arpa/inet.h>
12
13Connection::Connection()
14{
15 nSocket = -1;
16 bActive = false;
17 bDisconnectMe = false;
18 pProtocol = NULL;
19}
20
21Connection::~Connection()
22{
23 if( pProtocol != NULL ) delete pProtocol;
24}
25
26bool Connection::appendOutput( const char *lpOutput, int nSize )
27{
28 return xOutputBuf.appendData( lpOutput, nSize );
29}
30
31bool Connection::appendOutput( const char lOutput )
32{
33 return xOutputBuf.appendData( lOutput );
34}
35
36bool Connection::appendOutput( const short lOutput )
37{
38 return xOutputBuf.appendData( lOutput );
39}
40
41bool Connection::appendOutput( const int lOutput )
42{
43 return xOutputBuf.appendData( lOutput );
44}
45
46bool Connection::appendOutput( const long lOutput )
47{
48 return xOutputBuf.appendData( lOutput );
49}
50
51bool Connection::appendOutput( const float lOutput )
52{
53 return xOutputBuf.appendData( lOutput );
54}
55
56bool Connection::appendOutput( const double lOutput )
57{
58 return xOutputBuf.appendData( lOutput );
59}
60
61bool Connection::appendOutput( const unsigned char lOutput )
62{
63 return xOutputBuf.appendData( lOutput );
64}
65
66bool Connection::appendOutput( const unsigned short lOutput )
67{
68 return xOutputBuf.appendData( lOutput );
69}
70
71bool Connection::appendOutput( const unsigned long lOutput )
72{
73 return xOutputBuf.appendData( lOutput );
74}
75
76bool Connection::appendOutput( const unsigned int lOutput )
77{
78 return xOutputBuf.appendData( lOutput );
79}
80
81bool Connection::appendInput( const char *lpInput, int nSize )
82{
83 return xInputBuf.appendData( lpInput, nSize );
84}
85
86int Connection::scanInputFor( char cTarget )
87{
88 const char *lpTmp = xInputBuf.getData();
89 int jMax = xInputBuf.getLength();
90
91 for( int j = 0; j < jMax; j++ )
92 {
93 if( lpTmp[j] == cTarget )
94 {
95 return j;
96 }
97 }
98
99 return -1;
100}
101
102const char *Connection::getOutput()
103{
104 return xOutputBuf.getData();
105}
106
107const char *Connection::getInput()
108{
109 return xInputBuf.getData();
110}
111
112void Connection::setSocket( int nNewSocket )
113{
114 nSocket = nNewSocket;
115}
116
117int Connection::getSocket()
118{
119 return nSocket;
120}
121
122bool Connection::isActive()
123{
124 return bActive;
125}
126
127void Connection::close()
128{
129 if( bActive )
130 {
131 fsync( nSocket );
132 ::close( nSocket );
133 }
134 bActive = false;
135 //nSocket = -1;
136 xInputBuf.clearData();
137 xOutputBuf.clearData();
138 if( pProtocol != NULL )
139 {
140 delete pProtocol;
141 pProtocol = NULL;
142 }
143}
144
145bool Connection::open( int nNewSocket )
146{
147 bActive = true;
148 setSocket( nNewSocket );
149 bDisconnectMe = false;
150
151 return true;
152}
153
154bool Connection::open( const char *sAddr, int nPort )
155{
156 struct sockaddr_in xServerName;
157 bActive = false;
158
159 /* Create the socket. */
160 nSocket = socket( PF_INET, SOCK_STREAM, 0 );
161 if( nSocket < 0 )
162 {
163 bActive = false;
164 return false;
165 }
166
167 /* Connect to the server. */
168 {
169 struct hostent *hostinfo;
170
171 xServerName.sin_family = AF_INET;
172 xServerName.sin_port = htons( nPort );
173 hostinfo = gethostbyname( sAddr );
174 if (hostinfo == NULL)
175 {
176 return false;
177 }
178 xServerName.sin_addr = *(struct in_addr *) hostinfo->h_addr;
179 }
180
181 int ret = connect(
182 nSocket,
183 (struct sockaddr *)&xServerName,
184 sizeof(xServerName)
185 );
186
187 if( ret < 0 )
188 {
189 return false;
190 }
191
192 bActive = true;
193 bDisconnectMe = false;
194
195 return true;
196}
197
198bool Connection::readInput()
199{
200 char buffer[2048];
201 int nbytes;
202 int nTotalRead=0;
203
204 for(;;)
205 {
206 memset( buffer, 0, 2048 );
207
208 nbytes = read( nSocket, buffer, 2048 );
209 if (nbytes < 0)
210 {
211 /* Read error. */
212 //perror("readInput");
213 return false;
214 }
215 else if (nbytes == 0)
216 {
217 /* End-of-file. */
218 //perror("readInput");
219 return false;
220 }
221 else
222 {
223 nTotalRead += nbytes;
224 appendInput( buffer, nbytes );
225 /* Data read. */
226 if( nbytes < 2047 )
227 {
228 if( pProtocol != NULL && nTotalRead > 0 )
229 {
230 pProtocol->onNewData();
231 }
232
233 return true;
234 }
235 }
236 }
237
238 return true;
239}
240
241bool Connection::readInput( int nSec, int nUSec )
242{
243 fd_set rfds;
244 struct timeval tv;
245 int retval;
246
247 /* Watch stdin (fd 0) to see when it has input. */
248 FD_ZERO(&rfds);
249 FD_SET(nSocket, &rfds);
250 /* Wait up to five seconds. */
251 tv.tv_sec = nSec;
252 tv.tv_usec = nUSec;
253
254 retval = select( nSocket+1, &rfds, NULL, NULL, &tv );
255 /* Don't rely on the value of tv now! */
256
257 if (retval == -1)
258 {
259 // Oh my god!!! some kind of horrible problem!!!!
260 return false;
261 }
262 else if( retval )
263 {
264 // None of them have data, but the connection is still active.
265 return readInput();
266 }
267 else
268 {
269 return true;
270 }
271}
272
273bool Connection::clearOutput()
274{
275 return xOutputBuf.clearData();
276}
277
278bool Connection::clearInput()
279{
280 return xInputBuf.clearData();
281}
282
283#define min( a, b ) ((a<b)?(a):(b))
284
285bool Connection::writeOutput()
286{
287 int nBytes = TEMP_FAILURE_RETRY( write( nSocket, xOutputBuf.getData(), min( 2048, xOutputBuf.getLength() ) ) );
288 if( nBytes < 0 )
289 {
290 perror("writeOutput");
291 return true;
292 }
293 /*
294 if( nBytes < xOutputBuf.getLength() )
295 {
296 printf("Havn't written all the data (%d/%d/%d%%)\n", nBytes, xOutputBuf.getLength(), nBytes/(xOutputBuf.getLength()*100) );
297 }
298 else
299 {
300 printf("Wrote all pending data.\n");
301 }
302 */
303 xOutputBuf.usedData( nBytes );
304 //clearOutput();
305
306 return true;
307}
308
309bool Connection::hasOutput()
310{
311 if( xOutputBuf.getLength() == 0 )
312 {
313 return false;
314 }
315 else
316 {
317 return true;
318 }
319}
320
321bool Connection::hasInput()
322{
323 if( xInputBuf.getLength() == 0 )
324 {
325 return false;
326 }
327 else
328 {
329 return true;
330 }
331}
332
333bool Connection::usedInput( int nAmount )
334{
335 return xInputBuf.usedData( nAmount );
336}
337
338bool Connection::needDisconnect()
339{
340 return bDisconnectMe;
341}
342
343void Connection::disconnect()
344{
345 bDisconnectMe = true;
346}
347
348void Connection::setProtocol( class Protocol *pNewProtocol )
349{
350 pProtocol = pNewProtocol;
351 pProtocol->setConnection( this );
352}
353
354int Connection::getInputAmnt()
355{
356 return xInputBuf.getLength();
357}
358
359int Connection::getOutputAmnt()
360{
361 return xOutputBuf.getLength();
362}
363
364class Protocol *Connection::getProtocol()
365{
366 return pProtocol;
367}
368
369void Connection::printInputDebug( const char *lpPrefix, FILE *fh, int nBytesMax )
370{
371 printDataDebug(
372 (const unsigned char *)xInputBuf.getData(),
373 xInputBuf.getLength(),
374 "input",
375 lpPrefix,
376 fh,
377 nBytesMax
378 );
379}
380
381void Connection::printOutputDebug( const char *lpPrefix, FILE *fh, int nBytesMax )
382{
383 printDataDebug(
384 (const unsigned char *)xOutputBuf.getData(),
385 xOutputBuf.getLength(),
386 "output",
387 lpPrefix,
388 fh,
389 nBytesMax
390 );
391}
392
393void Connection::printDataDebug( const unsigned char *pData, long nDataLen, const char *lpName, const char *lpPrefix, FILE *fh, int nBytesMax )
394{
395 if( nBytesMax > 0 )
396 {
397 nDataLen = (nBytesMax<nDataLen)?(nBytesMax):(nDataLen);
398 }
399
400 fprintf( fh, "%sDisplaying %d bytes of %s.\n", lpPrefix, nDataLen, lpName );
401 int j = 0;
402 fprintf( fh, lpPrefix );
403 for( int l = 0; l < 8*3+2*8+2; l++ ) fprintf( fh, (l!=8*3)?("-"):("+") ); fprintf( fh, "\n");
404 for(;;)
405 {
406 fprintf( fh, lpPrefix );
407 int kmax = 8;
408 if( nDataLen-j < 8 ) kmax = nDataLen-j;
409 for(int k = 0; k < 8; k++ )
410 {
411 if( k < kmax )
412 {
413 fprintf( fh, "%02X ", (int)((unsigned char)pData[j+k]) );
414 }
415 else
416 {
417 fprintf( fh, "-- ");
418 }
419 }
420 printf("| ");
421 for(int k = 0; k < kmax; k++ )
422 {
423 fprintf( fh, "%c ", (pData[j+k]>32 && pData[j+k]<=128)?(pData[j+k]):('.') );
424 }
425 fprintf( fh, "\n");
426 j += kmax;
427 if( j >= nDataLen ) break;
428 }
429 fprintf( fh, lpPrefix );
430 for( int l = 0; l < 8*3+2*8+2; l++ ) fprintf( fh, (l!=8*3)?("-"):("+") ); fprintf( fh, "\n");
431}
432
diff --git a/src/connection.h b/src/connection.h
new file mode 100644
index 0000000..efb8630
--- /dev/null
+++ b/src/connection.h
@@ -0,0 +1,387 @@
1/**\file
2 * Contains the Connection class.
3 *@author Mike Buland
4 */
5
6#ifndef CONNECTION_H
7#define CONNECTION_H
8
9#include "multilog.h"
10#include "flexbuf.h"
11#include "protocol.h"
12
13/** Represents a single connection on a network. While these connections
14 * may be treated more or less just like files, occasionally problems arise
15 * when writing data at any time you feel like. Therefore you run all your
16 * data through a Connection, which buffers all data and makes sure no
17 * buffers are exceeded and nothing inappropriate for the recipient of the
18 * data is sent.
19 *@author Mike Buland
20 */
21class Connection
22{
23public:
24 /**
25 * Construct a blank and non-connected Connection. The created object is
26 * not yet connected to anything, and most of the functions except open are
27 * unusable.
28 */
29 Connection();
30
31 /**
32 * Destroy the connection, clean up all pending data requests and close the
33 * contained socket. This does not send out pending data, especially since
34 * such an operation could take considerable time, depending on the pending
35 * data and state of the receiving end.
36 */
37 ~Connection();
38
39 /**
40 * Open a connection to a remote server. This sets up this connection as
41 * a client instead of a server and does all of the work that needs to be
42 * done to actually open an INET_AF connection, which is a lot of work.
43 *@param sAddr The address to connect to. This can be in any format
44 * normally understood by your system to be an address, ip, domain name,
45 * etc.
46 *@param nPort The port number to connect to on the remote server.
47 *@returns True if the connection was successful and everything is setup,
48 * false if there were any of a dozen errors and the connection is not set.
49 *@todo Make this function add log entries to a standard MultiLog if
50 * something goes wrong.
51 */
52 bool open( const char *sAddr, int nPort );
53
54 /** Append the given data to the output. The data is presumed to be null
55 * terminated. To put binary data into the stream, use the other
56 * appendOutput function. This should be the only method used to
57 * communicate with the socket.
58 *@param lpOutput The data to add to the output queue.
59 *@param nSize How much data is in the lpOutput buffer. If this value
60 * is -1 then the program treats lpOutput as a null-terminated string.
61 *@returns True if everything is ok, false otherwise.
62 */
63 bool appendOutput( const char *lpOutput, int nSize=-1 );
64
65 /**
66 * Append the character to the output.
67 *@param lOutput The character to add to the output queue.
68 *@returns True if everything is ok, false otherwise.
69 */
70 bool appendOutput( const char lOutput );
71
72 /**
73 * Append the short to the output.
74 *@param lOutput The short to add to the output queue.
75 *@returns True if everything is ok, false otherwise.
76 */
77 bool appendOutput( const short lOutput );
78
79 /**
80 * Append the int to the output.
81 *@param lOutput The int to add to the output queue.
82 *@returns True if everything is ok, false otherwise.
83 */
84 bool appendOutput( const int lOutput );
85
86 /**
87 * Append the long to the output.
88 *@param lOutput The long to add to the output queue.
89 *@returns True if everything is ok, false otherwise.
90 */
91 bool appendOutput( const long lOutput );
92
93 /**
94 * Append the float to the output.
95 *@param lOutput The float to add to the output queue.
96 *@returns True if everything is ok, false otherwise.
97 */
98 bool appendOutput( const float lOutput );
99
100 /**
101 * Append the double to the output.
102 *@param lOutput The double to add to the output queue.
103 *@returns True if everything is ok, false otherwise.
104 */
105 bool appendOutput( const double lOutput );
106
107 /**
108 * Append the unsigned char to the output.
109 *@param lOutput The unsigned char to add to the output queue.
110 *@returns True if everything is ok, false otherwise.
111 */
112 bool appendOutput( const unsigned char lOutput );
113
114 /**
115 * Append the unsigned short to the output.
116 *@param lOutput The unsigned short to add to the output queue.
117 *@returns True if everything is ok, false otherwise.
118 */
119 bool appendOutput( const unsigned short lOutput );
120
121 /**
122 * Append the unsigned int to the output.
123 *@param lOutput The unsigned int to add to the output queue.
124 *@returns True if everything is ok, false otherwise.
125 */
126 bool appendOutput( const unsigned int lOutput );
127
128 /**
129 * Append the unsigned long to the output.
130 *@param lOutput The unsigned long to add to the output queue.
131 *@returns True if everything is ok, false otherwise.
132 */
133 bool appendOutput( const unsigned long lOutput );
134
135 /**
136 * Writes all input data in the buffer in a dual-view ascii and hex display
137 * to a file. There are a number of options that also help with debugging.
138 *@param lpPrefix Text to be added to the begining of every line written
139 * out. The default is a blank string.
140 *@param fh The file to write the data to in text mode. This is stdout by
141 * default, but could be any already open file handle.
142 *@param nBytesMax The maximum number of bytes to write to the output. The
143 * amount of data can be overwhelming sometimes, so you can limit it. The
144 * default value is -1, which is also unlimited.
145 */
146 void printInputDebug( const char *lpPrefix="", FILE *fh=stdout, int nBytesMax=-1 );
147
148 /**
149 * Writes all output data in the buffer in a dual-view ascii and hex display
150 * to a file. There are a number of options that also help with debugging.
151 *@param lpPrefix Text to be added to the begining of every line written
152 * out. The default is a blank string.
153 *@param fh The file to write the data to in text mode. This is stdout by
154 * default, but could be any already open file handle.
155 *@param nBytesMax The maximum number of bytes to write to the output. The
156 * amount of data can be overwhelming sometimes, so you can limit it. The
157 * default value is -1, which is also unlimited.
158 */
159 void printOutputDebug( const char *lpPrefix="", FILE *fh=stdout, int nBytesMax=-1 );
160
161 /**
162 * This is the low-level generic function that is called by both
163 * printInputDebug and printOutputDebug. It works effectively just like
164 * both of them, except that you can give it a raw pointer to the data to
165 * print out. This probably doesn't belong in this class, but this was
166 * where I was when I needed it.
167 *@param pData A pointer to the data to write. This is not treated as a
168 * null terminated string, so make sure that the nDataLen param is set
169 * properly.
170 *@param nDataLen The number of bytes that are in pData and that you want to
171 * see.
172 *@param lpName The name of the data, this is used in the header where it
173 * says "Displaying nnn bytes of <lpName>." A good example would be input
174 * or output.
175 *@param lpPrefix Text to put before every line output. This just makes it
176 * easier to tell large blocks apart in the output.
177 *@param fh The file handle to write all data to.
178 *@param nBytesMax The maximum number of bytes. This parameter is stupid.
179 * If it is set to -1, then nDataLen is used, otherwise the smaller value is
180 * used as the number of bytes to output.
181 *@todo Put this function somewhere more deserving.
182 *@todo Remove the nBytesMax param, we need that in the other functions,
183 * not this one!
184 */
185 void printDataDebug( const unsigned char *pData, long nDataLen, const char *lpName, const char *lpPrefix, FILE *fh, int nBytesMax );
186
187 /** Append the given data to the input. The data is presumed to be null
188 * terminated. To put binary data into the stream, use the other
189 * appendInput function. This is mainly used by internal routines.
190 *@param lpInput The data to add to the input queue.
191 *@param nSize How much data is in the lpInput buffer. If this value
192 * is -1 then the program treats lpOutput as a null-terminated string.
193 *@returns True if everything is ok, false otherwise.
194 */
195 bool appendInput( const char *lpInput, int nSize=-1 );
196
197 /** Searches through the current pending input for a certain character.
198 * This is useful for finding out where exactly the end of a line is, for
199 * example, to see if a command has been entered yet.
200 *@param cTarget The character to search for.
201 *@returns The position of the target relative to the begining of the input
202 * or -1 if the target wasn't found.
203 */
204 int scanInputFor( char cTarget );
205
206 /** Gets a pointer to the output buffer. This is mainly used by internal
207 * routines, and is cleared every click when data is sent out again.
208 *@returns A pointer to the buffer holding the pending output data.
209 */
210 const char *getOutput();
211
212 /** Gets a pointer to the start of the input buffer's active data
213 * section. Use this to gain access to the input you need to do
214 * your job.
215 *@returns A pointer to the data in the input buffer. Do not delete this.
216 */
217 const char *getInput();
218
219 /** Clears all pending output, this is mainly just used internally.
220 *@returns True if operation was a success, otherwise false.
221 */
222 bool clearOutput();
223
224 /** Clears all pending input, weather it's been used or not. Please
225 * refrain from calling this during normal operation, use usedInput
226 * instead, it's much safer.
227 *@returns True if the operation was a success, false otherwise.
228 */
229 bool clearInput();
230
231 /** Sets the socket that should be used internally.
232 *@param nNewSocket The new socket to work with.
233 */
234 void setSocket( int nNewSocket );
235
236 /** Gets the handle (number) of the working socket. This can be a
237 * dangerous function to call, please refrain from calling it directly
238 * if any alternative can be found.
239 *@returns The number of the working socket.
240 */
241 int getSocket();
242
243 /** Determines if the connection is still active.
244 *@returns True if the connection is active, false otherwise.
245 */
246 bool isActive();
247
248 /** Clears all buffers and sets up the connection to be reused.
249 * Does not actually close the socket, that's handled by the
250 * ConnectionManager
251 */
252 void close();
253
254 /** Opens a socket. Really just sets up the connection for use since
255 * the socket itself was created and opened by the ConnectionManager.
256 * This also calls setSocket so you don't have to.
257 *@param nNewSocket The socket to assosiate with.
258 */
259 bool open( int nNewSocket );
260
261 /**
262 * Reads all pending input from the connection. If this is called outside
263 * of the ConnectionManager it will usually block indefinately waiting for
264 * new data. The only way to change this behaviour is to modify the socket
265 * low-level when you connect it manually, or, preferably use the other
266 * readInput function to control blocking time.
267 *@returns True socket is still connected, otherwise false.
268 */
269 bool readInput();
270
271 /**
272 * Reads all pending input from the connection, blocking up to nSec
273 * seconds and nUSec micro-seconds for the data. This uses select to
274 * simulate blocking, but has the same effect as standard io blocking.
275 * If you don't want to block, just set both values to zero.
276 *@param nSec Max seconds to wait.
277 *@param nUSec Max micro-seconds to wait.
278 */
279 bool readInput( int nSec, int nUSec );
280
281 /** Writes all data that is pending to the socket.
282 *@returns True if all data was written succesfully, false otherwise.
283 */
284 bool writeOutput();
285
286 /** Determines if the connection has output waiting to go out.
287 *@returns true if there is pending output, otherwise false.
288 */
289 bool hasOutput();
290
291 /** Sets internal flags so that this connection will be deleted next
292 * time through the ConnectionManager.
293 */
294 void disconnect();
295
296 /** Determines if this connection is ready to be disconnected or not.
297 *@returns True if it is time to disconnect, false if it isn't.
298 */
299 bool needDisconnect();
300
301 /** Tells the caller if there is pending input waiting to be processed.
302 *@returns True if there is pending input that has not been used, returns
303 * false if there isn't.
304 */
305 bool hasInput();
306
307 /** Removes bytes from the begining of the input queue. Use this after
308 * getting the input and processing as much as you need to.
309 *@param nAmount The number of bytes used.
310 *@returns true if the update was successful, otherwise false.
311 */
312 bool usedInput( int nAmount );
313
314 /** Sets the protocol to be used by this connection. All data in and out
315 * passes through the protocol object, which may process that data to
316 * filter out and process any special messages that may have been
317 * included. Everything that isn't processed can be accessed in the
318 * standard method.
319 *@param pNewProtocol A pointer to a protocol object that you want to
320 * use.
321 */
322 void setProtocol( class Protocol *pNewProtocol );
323
324 /** Gets the number of bytes that are waiting in the input queue, the data
325 * that has yet to be processed.
326 *@returns The number of bytes in the input queue.
327 */
328 int getInputAmnt();
329
330 /** Gets the number of bytes that are waiting in the output queue, the data
331 * that has yet to be sent to the connected socket.
332 *@returns The number of bytes in the input queue.
333 */
334 int getOutputAmnt();
335
336 /** Gets a pointer to the protocol that is attatched to this connection
337 * object. This is useful to set modes, and send special commands in
338 * addition to the standard raw data reads and writes that are normally
339 * permitted. In fact, in everything besides a raw telnet protocol all
340 * data should be sent through the protocol and not the connection object.
341 *@returns A pointer to the Protocol assosiated with this connection.
342 */
343 class Protocol *getProtocol();
344
345private:
346 /**
347 * A buffer to keep data read from the socket in. This is filled in by
348 * the function readInput, which is automatically called by the
349 * ConnectionManager whenever new data is ready.
350 */
351 FlexBuf xInputBuf;
352
353 /**
354 * A buffer to keep data that should be sent to the socket. This is filled
355 * in by using the AppendOutput functions and is sent to the socket using
356 * the writeOutput function, which is automatically called every cycle by
357 * the ConnectionManager when there is pending data.
358 */
359 FlexBuf xOutputBuf;
360
361 /**
362 * The socket that the user is connected to. This is not the same as the
363 * socket number of the listening socket, this is the unique socket on the
364 * system that the data is coming to.
365 */
366 int nSocket;
367
368 /**
369 * True=active connection, False=connection lost
370 */
371 bool bActive;
372
373 /**
374 * True=disconnect next cycle (after data is transmitted), Flse=keep going.
375 */
376 bool bDisconnectMe;
377
378 /**
379 * A pointer to a protocol handler that can automatically process the data
380 * in the buffers. This is optional if you use the connections on your own
381 * but reccomended if you use this with the rest of the ConnectionManager
382 * system.
383 */
384 class Protocol *pProtocol;
385};
386
387#endif
diff --git a/src/connectionmanager.cpp b/src/connectionmanager.cpp
new file mode 100644
index 0000000..36ff961
--- /dev/null
+++ b/src/connectionmanager.cpp
@@ -0,0 +1,343 @@
1#include <time.h>
2#include <string.h>
3#include <stdio.h>
4#include <errno.h>
5#include <stdlib.h>
6#include <unistd.h>
7#include <sys/types.h>
8#include <sys/socket.h>
9#include <termios.h>
10#include <netinet/in.h>
11#include <netdb.h>
12#include <arpa/inet.h>
13#include "connectionmanager.h"
14#include <fcntl.h>
15
16ConnectionManager::ConnectionManager()
17{
18 pLog = MultiLog::getLog();
19 nMasterSocket = -1;
20 pMonitor = NULL;
21}
22
23ConnectionManager::~ConnectionManager()
24{
25 std::list<Connection *>::const_iterator i;
26 for( i = lActive.begin(); i != lActive.end(); i++ )
27 {
28 delete (*i);
29 }
30 for( i = lInactive.begin(); i != lInactive.end(); i++ )
31 {
32 delete (*i);
33 }
34}
35
36bool ConnectionManager::startServer( int nPort, int nInitPool )
37{
38 /* Create the socket and set it up to accept connections. */
39 struct sockaddr_in name;
40
41 /* Create the socket. */
42 nMasterSocket = socket (PF_INET, SOCK_STREAM, 0);
43 if (nMasterSocket < 0)
44 {
45 pLog->LineLog( MultiLog::LError, "Couldn't create a listen socket.");
46 return false;
47 }
48
49 /* Give the socket a name. */
50 name.sin_family = AF_INET;
51 name.sin_port = htons( nPort );
52
53 // I think this specifies who we will accept connections from,
54 // a good thing to make configurable later on
55 name.sin_addr.s_addr = htonl( INADDR_ANY );
56
57 int opt = 1;
58 setsockopt( nMasterSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt));
59
60 if (bind (nMasterSocket, (struct sockaddr *) &name, sizeof (name)) < 0)
61 {
62 pLog->LineLog( MultiLog::LError, "Couldn't bind to the listen socket.");
63 return false;
64 }
65
66 if (listen (nMasterSocket, 1) < 0)
67 {
68 pLog->LineLog( MultiLog::LError, "Couldn't begin listening to the server socket.");
69 return false;
70 }
71
72 /* Initialize the set of active sockets. */
73 FD_ZERO (&fdActive);
74 FD_ZERO (&fdRead);
75 FD_ZERO (&fdWrite);
76 FD_ZERO (&fdException);
77 FD_SET (nMasterSocket, &fdActive);
78
79 for( int j = 0; j < nInitPool; j++ )
80 {
81 lInactive.insert( lInactive.begin(), new Connection() );
82 }
83
84 return true;
85}
86
87bool ConnectionManager::startServer( int nPort, int nInitPool, int nNumTries, int nTimeout )
88{
89 struct timeval xTimeout;
90
91 for( int j = 0; j < nNumTries; j++ )
92 {
93 pLog->LineLog( MultiLog::LStatus, "Attempting to create server socket (attempt [%d/%d])...", j+1, nNumTries );
94 if( startServer( nPort, nInitPool ) == true )
95 {
96 return true;
97 }
98 else if( j < nNumTries-1 )
99 {
100 pLog->LineLog( MultiLog::LStatus, "Waiting for %d secconds to allow port to clear...", nTimeout );
101 xTimeout.tv_sec = nTimeout;
102 xTimeout.tv_usec = 0;
103 if (select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &xTimeout) < 0) {
104 pLog->LineLog( MultiLog::LError, "Error using select to sleep for a while.");
105 }
106 usleep( nTimeout );
107 }
108 }
109
110 return false;
111}
112
113bool ConnectionManager::scanConnections( int nTimeout, bool bForceTimeout )
114{
115 struct timeval xTimeout;
116
117 xTimeout.tv_sec = nTimeout / 1000000;
118 xTimeout.tv_usec = nTimeout % 1000000;
119
120 /* Block until input arrives on one or more active sockets. */
121 fdRead = fdActive;
122 fdWrite = fdActive;
123 fdException = fdActive;
124
125 // We removed the write checking because it just checks to see if you *can*
126 // write...that's stupid, they're all open, so it always exits immediately
127 // if there are ANY connections there...
128 if( TEMP_FAILURE_RETRY( select( FD_SETSIZE, &fdRead, (fd_set *)0/*&fdWrite*/, &fdException, &xTimeout ) ) < 0 )
129 {
130 pLog->LineLog( MultiLog::LError, "Error attempting to scan open connections.");
131 perror("ConnectionManager");
132 return false;
133 }
134 // Now we use select to sleep as well as to scan for connections, now we
135 // just need to fix the fact that if there are no connections, the seccond
136 // select call doesn't return until there is a connection...
137 if( bForceTimeout )
138 {
139 if (select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &xTimeout) < 0) {
140 pLog->LineLog( MultiLog::LError, "Error using select to sleep for a while.");
141 }
142 }
143
144 /* Service all the sockets with input pending. */
145 for( int i = 0; i < FD_SETSIZE; ++i )
146 {
147 if( FD_ISSET( i, &fdRead ) )
148 {
149 if( i == nMasterSocket )
150 {
151 addConnection();
152 }
153 else
154 {
155 Connection *pCon = findActiveConnection( i );
156 if( pCon == NULL )
157 {
158 pLog->LineLog( MultiLog::LError, "A connection object was lost, or never created!");
159 return false;
160 }
161
162 /* Data arriving on an already-connected socket. */
163 if( pCon->readInput() != true )
164 {
165 pLog->LineLog( MultiLog::LStatus, "Closing connection due to disconnect.");
166 close( i );
167 FD_CLR( i, &fdActive );
168 pMonitor->onClosedConnection( pCon );
169 pCon->close();
170 }
171 else
172 {
173 // We actually read something...but the connection handles
174 // protocol notification, so we don't need to do anything
175 // here...
176 }
177 }
178 }
179 }
180 std::list<Connection *>::iterator i;
181 for( i = lActive.begin(); i != lActive.end(); i++ )
182 {
183 if( (*i)->isActive() == false )
184 {
185 std::list<Connection *>::iterator l = i;
186 i--;
187 lInactive.insert( lInactive.end(), *l );
188 lActive.erase( l );
189 continue;
190 }
191 if( (*i)->hasOutput() )
192 {
193 (*i)->writeOutput();
194 }
195 if( (*i)->needDisconnect() )
196 {
197 int prt = (*i)->getSocket();
198 close( prt );
199 FD_CLR( prt, &fdActive );
200 pMonitor->onClosedConnection( *i );
201 (*i)->close();
202 lInactive.insert( lInactive.end(), *i );
203 std::list<Connection *>::iterator l = i;
204 i--;
205 lActive.erase( l );
206 pLog->LineLog( MultiLog::LStatus, "Closing connection due to server request.");
207 }
208 }
209
210 return true;
211}
212
213bool ConnectionManager::shutdownServer()
214{
215 while( !lActive.empty() )
216 {
217 Connection *i = *(lActive.begin());
218 if( i->isActive() )
219 {
220 i->close();
221 pMonitor->onClosedConnection( i );
222 lInactive.insert( lInactive.end(), i );
223 lActive.erase( lActive.begin() );
224 }
225 }
226/*
227 for( int i = 0; i < nPoolSize; i++ )
228 {
229
230 int prt = axConPool[i].getSocket();
231 close( prt );
232// FD_CLR( prt, &fdActive );
233 pMonitor->onClosedConnection( &axConPool[i] );
234 axConPool[i].close();
235 }
236*/
237 shutdown( nMasterSocket, SHUT_RDWR );
238 close( nMasterSocket );
239
240 return true;
241}
242
243bool ConnectionManager::broadcastMessage( const char *lpData, int nExcludeSocket )
244{
245 std::list<Connection *>::const_iterator i;
246 for( i = lActive.begin(); i != lActive.end(); i++ )
247 {
248 if( (*i)->isActive() &&
249 (*i)->getSocket() != nExcludeSocket )
250 {
251 (*i)->appendOutput( lpData );
252 }
253 }
254
255 return true;
256}
257
258bool ConnectionManager::addConnection()
259{
260 struct sockaddr_in clientname;
261 size_t size;
262 int newSocket;
263
264 size = sizeof( clientname );
265#ifdef __CYGWIN__
266 newSocket = accept( nMasterSocket, (struct sockaddr *) &clientname, (int *)&size );
267#else
268 newSocket = accept( nMasterSocket, (struct sockaddr *) &clientname, &size );
269#endif
270 if( newSocket < 0 )
271 {
272 pLog->LineLog( MultiLog::LError, "Error accepting a new connection!" );
273 return false;
274 }
275// char *tmpa = inet_ntoa(clientname.sin_addr);
276 char tmpa[20];
277 inet_ntop( AF_INET, (void *)&clientname.sin_addr, tmpa, 20 );
278 pLog->LineLog( MultiLog::LStatus, "New connection from host %s, port %hd.", tmpa, ntohs (clientname.sin_port) );
279/*
280 int nCnt = 0;
281 for( int j = 0; j < nPoolSize; j++ )
282 {
283 if( axConPool[j].isActive() )
284 {
285 nCnt++;
286 }
287 }
288 pLog->LineLog( MultiLog::LStatus, "Connections %d/%d.", nCnt, nPoolSize );
289 */
290// free( tmpa );
291 FD_SET( newSocket, &fdActive );
292
293 //void nonblock(socket_t s)
294 {
295 int flags;
296
297 flags = fcntl(newSocket, F_GETFL, 0);
298 flags |= O_NONBLOCK;
299 if (fcntl(newSocket, F_SETFL, flags) < 0)
300 {
301 return false;
302 }
303 }
304
305 Connection *pCon = getInactiveConnection();
306 pCon->open( newSocket );
307
308 pMonitor->onNewConnection( pCon );
309
310 lActive.insert( lActive.end(), pCon );
311
312 return true;
313}
314
315Connection *ConnectionManager::getInactiveConnection()
316{
317 if( lInactive.empty() )
318 {
319 return new Connection();
320 }
321 Connection *pCon = *(lInactive.begin());
322 lInactive.erase( lInactive.begin() );
323 return pCon;
324}
325
326Connection *ConnectionManager::findActiveConnection( int nSocket )
327{
328 std::list<Connection *>::const_iterator i;
329 for( i = lActive.begin(); i != lActive.end(); i++ )
330 {
331 if( (*i)->getSocket() == nSocket )
332 {
333 return *i;
334 }
335 }
336
337 return NULL;
338}
339
340void ConnectionManager::setConnectionMonitor( ConnectionMonitor *pNewMonitor )
341{
342 pMonitor = pNewMonitor;
343}
diff --git a/src/connectionmanager.h b/src/connectionmanager.h
new file mode 100644
index 0000000..53249a7
--- /dev/null
+++ b/src/connectionmanager.h
@@ -0,0 +1,138 @@
1/**
2 *@file
3 * Contains the ConnectionManager.
4 *@author Mike Buland
5 */
6
7#ifndef CONNECTIONMANAGER_H
8#define CONNECTIONMANAGER_H
9
10#include "multilog.h"
11#include "connection.h"
12#include "connectionmonitor.h"
13#include <sys/types.h>
14#include <list>
15
16/** Manges incoming network connections as a server. Creates and works with
17 * Connection objects. All operations are performed on TCP/IP v4 right now,
18 * and on a single port, although any number of connections can be handled.
19 *@author Mike Buland
20 */
21class ConnectionManager
22{
23public:
24 /**
25 * Sets up the basics, like storage for the pool, and so on. This does not
26 * actually start a server, bind to a port, or create a connection pool.
27 * That's all handled by startServer().
28 */
29 ConnectionManager();
30
31 /**
32 * Cleans up everything, and even clears out all still-connected Connection
33 * objects.
34 */
35 ~ConnectionManager();
36
37 /**
38 * Starts a server socket and binds to it, listening for new connections.
39 *@param nPort The port to listen on.
40 *@param nInitPool The size of the initial connection pool. This will
41 * grow automatically if necesarry.
42 *@returns True if the socket was bound to the port and serving was
43 * started. False if there was a problem connecting to the port.
44 */
45 bool startServer( int nPort, int nInitPool );
46
47 /**
48 * This is identicle to the simpler startServer function except that it
49 * will automatically try to connect multiple times in case the first
50 * attempt or two doesn't work for some reason. Initially this was
51 * written to compensate for server sockets staying locked after they were
52 * closed for a while.
53 *@param nPort The port to listen on.
54 *@param nInitPool The size of the initial connection pool. This will
55 * grow automatically if necesarry.
56 *@param nNumTries The maximum number of times to try to connect.
57 *@param nTimeout The amount of time to wait in-between connection
58 * attempts.
59 *@returns True if the socket was bound to the port and serving was
60 * started. False if there was a problem connecting to the port.
61 */
62 bool startServer( int nPort, int nInitPool, int nNumTries, int nTimeout );
63
64 /**
65 * Scans all open connections, halting the calling processes until data
66 * is received or nTimeout ms have gone by. While waiting for the timeout
67 * to complete the process is placed into an idle mode.
68 *@param nTimeout The number of millisecconds to wait if there is nothing
69 * to actually do.
70 *@param bForceTimeout If set to true, this will force the scanner to wait
71 * for the timout to complete before returning, even if there was pending
72 * data.
73 */
74 bool scanConnections( int nTimeout, bool bForceTimeout );
75
76 /** Shutdown the server and all assosiated sockets.
77 *@returns True if every socket was closed without problem.
78 */
79 bool shutdownServer();
80
81 /** Sends a message directly to every connected port.
82 *@param lpData A null-terminated string of data to send.
83 *@param nExcludeSocket An optional socket to exclude from the broadcast.
84 *@returns True if every socket that should have gotten the message did.
85 */
86 bool broadcastMessage( const char *lpData, int nExcludeSocket=-1 );
87
88 /** Sets a monitor for the manager. The monitor is sent notifications
89 * whenever a socket is connected, disconnected, or whenever an error
90 * occurs.
91 *@param pNewMonitor A pointer to a preconstructed ConnectionMonitor
92 */
93 void setConnectionMonitor( ConnectionMonitor *pNewMonitor );
94
95private:
96 /**
97 * Take care of the work of actually accepting a connection. This will
98 * accept the connection, set the initial modes, and add it to the master
99 * list of active connections, as well as fire off any messages that need
100 * to be handled by anything else.
101 *@returns True if everything worked, False otherwise.
102 */
103 bool addConnection();
104
105 /**
106 * Seraches the internal lists of connections for one with a specific
107 * socket.
108 *@param nSocket The socket the connection is using for communication.
109 * This is the unique socket and not the one that the connection was
110 * initially to.
111 *@returns NULL if no connection was found, otherwise a pointer to a live
112 * Connection object.
113 */
114 Connection *findActiveConnection( int nSocket );
115
116 /**
117 * Searches the connection pool for an object that isn't in use yet, and
118 * returns it, ready to be filled in and used.
119 *@returns An unused connection object ready for use.
120 *@todo Check this code over to insure that the pool grows appropriately
121 * when enough extra connections are detected.
122 */
123 Connection *getInactiveConnection();
124
125 MultiLog *pLog; /**< A pointer to the active MultiLog */
126 int nMasterSocket; /**< The listening or server socket. */
127 fd_set fdActive; /**< The active socket set. */
128 fd_set fdRead; /**< The sockets ready for a read. */
129 fd_set fdWrite; /**< The sockets ready for a write. */
130 fd_set fdException; /**< The sockets that have gotten errors. */
131 std::list<Connection *> lInactive; /**< The pool of inactive Connections */
132 std::list<Connection *> lActive; /**< The pool of active Connections */
133
134 /** The ConnectionMonitor to notify of new connections. */
135 ConnectionMonitor *pMonitor;
136};
137
138#endif
diff --git a/src/connectionmonitor.cpp b/src/connectionmonitor.cpp
new file mode 100644
index 0000000..1b49f5d
--- /dev/null
+++ b/src/connectionmonitor.cpp
@@ -0,0 +1,23 @@
1/***************************************************************************
2 connectionmonitor.cpp - description
3 -------------------
4 begin : Mon Sep 8 2003
5 copyright : (C) 2003 by Mike Buland
6 email : eichlan@yf-soft.com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "connectionmonitor.h"
19
20ConnectionMonitor::ConnectionMonitor(){
21}
22ConnectionMonitor::~ConnectionMonitor(){
23}
diff --git a/src/connectionmonitor.h b/src/connectionmonitor.h
new file mode 100644
index 0000000..b96b533
--- /dev/null
+++ b/src/connectionmonitor.h
@@ -0,0 +1,41 @@
1/**@file
2 * Describes the ConnectionMonitor class.
3 */
4#ifndef CONNECTIONMONITOR_H
5#define CONNECTIONMONITOR_H
6
7#include "connection.h"
8
9/** Connection Monitor defines the base class of the objects that will be
10 * notified whenever a connection is created or destroyed.
11 *@author Mike Buland
12 */
13class ConnectionMonitor
14{
15public:
16 /**
17 * This is only here for completeness. It does nothing.
18 */
19 ConnectionMonitor();
20
21 /**
22 * This is only here for completeness. It does nothing.
23 */
24 virtual ~ConnectionMonitor();
25
26 /** Receives the notification that new connection was received.
27 *@param pCon The connection that was created.
28 *@returns Should return a true value if everything is OK, a false to
29 * force a shutdown.
30 */
31 virtual bool onNewConnection( Connection *pCon ) = 0;
32
33 /** Receives the notification that a connection was closed.
34 *@param pCon The connection that was closed.
35 *@returns Should return a true value if everything is OK, a false to
36 * force a shutdown.
37 */
38 virtual bool onClosedConnection( Connection *pCon ) = 0;
39};
40
41#endif
diff --git a/src/flexbuf.cpp b/src/flexbuf.cpp
new file mode 100644
index 0000000..acd55a7
--- /dev/null
+++ b/src/flexbuf.cpp
@@ -0,0 +1,206 @@
1#include "flexbuf.h"
2#include <string.h>
3
4FlexBuf::FlexBuf()
5{
6 lpBuf = new char[1024];
7 nLastChar = 0;
8 nFirstChar = 0;
9 nSize = 1024;
10 nFill = 0;
11 clearData();
12}
13
14FlexBuf::~FlexBuf()
15{
16 delete[] lpBuf;
17}
18
19bool FlexBuf::appendData( const char *lpData, int nDSize )
20{
21 int nStrLen;
22 if( nDSize < 0 )
23 {
24 nStrLen = strlen( lpData );
25 }
26 else
27 {
28 nStrLen = nDSize;
29 }
30
31 if( nLastChar + nStrLen + 1 > nSize )
32 {
33 if( nFill + nStrLen + 1 < nSize )
34 {
35 memcpy( lpBuf, lpBuf+nFirstChar, nFill );
36 nLastChar -= nFirstChar;
37 nFirstChar = 0;
38 }
39 else
40 {
41 nSize += nStrLen+1;
42 char *lpNewBuf = new char[nSize];
43 memcpy( lpNewBuf, lpBuf+nFirstChar, nFill );
44 delete[] lpBuf;
45 lpBuf = lpNewBuf;
46 nLastChar -= nFirstChar;
47 nFirstChar = 0;
48 }
49 }
50
51 memcpy( &lpBuf[nLastChar], lpData, nStrLen );
52 nLastChar += nStrLen;
53 nFill += nStrLen;
54 lpBuf[nLastChar] = '\0';
55
56 return true;
57}
58
59bool FlexBuf::appendData( const char lData )
60{
61 if( nLastChar + 2 > nSize )
62 {
63 if( nFill+2 < nSize )
64 {
65 memcpy( lpBuf, lpBuf+nFirstChar, nFill );
66 nLastChar -= nFirstChar;
67 nFirstChar = 0;
68 }
69 else
70 {
71 nSize += 1024;
72 char *lpNewBuf = new char[nSize];
73 memcpy( lpNewBuf, lpBuf+nFirstChar, nFill );
74 delete[] lpBuf;
75 lpBuf = lpNewBuf;
76 nLastChar -= nFirstChar;
77 nFirstChar = 0;
78 }
79 }
80
81 lpBuf[nLastChar] = lData;
82 nLastChar++;
83 nFill++;
84 lpBuf[nLastChar] = '\0';
85
86 return true;
87}
88
89bool FlexBuf::appendData( const short lData )
90{
91 return appendData( (const char *)&lData, sizeof(short) );
92}
93
94bool FlexBuf::appendData( const int lData )
95{
96 return appendData( (const char *)&lData, sizeof(int) );
97}
98
99bool FlexBuf::appendData( const long lData )
100{
101 return appendData( (const char *)&lData, sizeof(long) );
102}
103
104bool FlexBuf::appendData( const float lData )
105{
106 return appendData( (const char *)&lData, sizeof(float) );
107}
108
109bool FlexBuf::appendData( const double lData )
110{
111 return appendData( (const char *)&lData, sizeof(double) );
112}
113
114bool FlexBuf::appendData( const unsigned char lData )
115{
116 return appendData( (const char)lData );
117}
118
119bool FlexBuf::appendData( const unsigned short lData )
120{
121 return appendData( (const char *)&lData, sizeof(short) );
122}
123
124bool FlexBuf::appendData( const unsigned long lData )
125{
126 return appendData( (const char *)&lData, sizeof(long) );
127}
128
129bool FlexBuf::appendData( const unsigned int lData )
130{
131 return appendData( (const char *)&lData, sizeof(int) );
132}
133
134bool FlexBuf::clearData()
135{
136 nFirstChar = nLastChar = nFill = 0;
137 lpBuf[nLastChar] = '\0';
138
139 return true;
140}
141
142const char *FlexBuf::getData()
143{
144 return (lpBuf+nFirstChar);
145}
146
147int FlexBuf::getLength()
148{
149 return nFill;
150}
151
152int FlexBuf::getCapacity()
153{
154 return nSize;
155}
156
157bool FlexBuf::usedData( int nAmount )
158{
159 // Remove from the end if negative
160 if( nAmount < 0 )
161 {
162 if( nFill+nAmount < 0 )
163 {
164 nFill = nFirstChar = nLastChar = 0;
165 return true;
166 }
167 nLastChar += nAmount;
168 nFill += nAmount;
169 return true;
170 }
171 if( nAmount > nFill )
172 {
173 nAmount = nSize;
174// return false;
175 }
176
177 //nLastChar -= nAmount;
178 nFirstChar += nAmount;
179 nFill -= nAmount;
180
181 if( nFill == 0 )
182 {
183 nFirstChar = nLastChar = 0;
184 }
185
186 //if( nLastChar > 0 )
187 //{
188 //memmove( lpBuf, &lpBuf[nAmount], nLastChar );
189 //}
190
191 return true;
192}
193
194int FlexBuf::findChar( char cTarget )
195{
196 for( int j = nFirstChar; j < nLastChar; j++ )
197 {
198 if( lpBuf[j] == cTarget )
199 {
200 return j;
201 }
202 }
203
204 return -1;
205}
206
diff --git a/src/flexbuf.h b/src/flexbuf.h
new file mode 100644
index 0000000..5ce4a89
--- /dev/null
+++ b/src/flexbuf.h
@@ -0,0 +1,160 @@
1/**\flexbuf.h
2 * Describes the FlexBuf class.
3 *@author Mike Buland
4 */
5
6#ifndef FLEXBUF_H
7#define FLEXBUF_H
8
9/** Stores any amount of data, but starts small, growing as necesarry.
10 * It is optimized to work with stream type situations, with data being
11 * added to the end while it is being taken from the begning.
12 *@todo Set this class up to auto-shrink back to a specified sized buffer each
13 * time it has shrunk below that for enough operations.
14 *@author Mike Buland
15 */
16class FlexBuf
17{
18public:
19 /**
20 * Construct a blank FlexBuf containing about 1k of buffer space.
21 */
22 FlexBuf();
23
24 /**
25 * Clean up the FlexBuf, delete all buffers.
26 */
27 ~FlexBuf();
28
29 /** Appends a whole string of data to the buffer. The string
30 * must be null terminated.
31 *@param lpData The data to append to the buffer.
32 *@param nDSize The size of the data described by lpData. If this
33 * value is -1 lpData is treated as a null-terminated string.
34 *@returns True if no problems occured, false otherwise.
35 */
36 bool appendData( const char *lpData, int nDSize=-1 );
37
38 /** Appends a single character to the end of the buffer.
39 *@param lData The character to append to the buffer.
40 *@returns True if no problems occured, false otherwise.
41 */
42 bool appendData( const char lData );
43
44 /**
45 * Append the short to the buffer.
46 *@param lData The short to add to the buffer queue.
47 *@returns True if everything is ok, false otherwise.
48 */
49 bool appendData( const short lData );
50
51 /**
52 * Append the int to the buffer.
53 *@param lData The int to add to the buffer queue.
54 *@returns True if everything is ok, false otherwise.
55 */
56 bool appendData( const int lData );
57
58 /**
59 * Append the long to the buffer.
60 *@param lData The long to add to the buffer queue.
61 *@returns True if everything is ok, false otherwise.
62 */
63 bool appendData( const long lData );
64
65 /**
66 * Append the float to the buffer.
67 *@param lData The float to add to the buffer queue.
68 *@returns True if everything is ok, false otherwise.
69 */
70 bool appendData( const float lData );
71
72 /**
73 * Append the double to the buffer.
74 *@param lData The double to add to the buffer queue.
75 *@returns True if everything is ok, false otherwise.
76 */
77 bool appendData( const double lData );
78
79 /**
80 * Append the unsigned char to the buffer.
81 *@param lData The unsigned char to add to the buffer queue.
82 *@returns True if everything is ok, false otherwise.
83 */
84 bool appendData( const unsigned char lData );
85
86 /**
87 * Append the unsigned short to the buffer.
88 *@param lData The unsigned short to add to the buffer queue.
89 *@returns True if everything is ok, false otherwise.
90 */
91 bool appendData( const unsigned short lData );
92
93 /**
94 * Append the unsigned int to the buffer.
95 *@param lData The unsigned int to add to the buffer queue.
96 *@returns True if everything is ok, false otherwise.
97 */
98 bool appendData( const unsigned int lData );
99
100 /**
101 * Append the unsigned long to the buffer.
102 *@param lData The unsigned long to add to the buffer queue.
103 *@returns True if everything is ok, false otherwise.
104 */
105 bool appendData( const unsigned long lData );
106
107 /** Removes all pending data from the buffer.
108 *@returns True if no problems occured, false otherwise.
109 */
110 bool clearData();
111
112 /** Gets a pointer to the internal buffer, at the begining of the current
113 * data stream.
114 *@returns A pointer to the internal data buffer.
115 */
116 const char *getData();
117
118 /** Gets the length of the current buffer (how much data is really in the
119 * buffer, not it's current capacity, for that check getCapacity)
120 *@returns The length of the current buffer.
121 */
122 int getLength();
123
124 /** Gets the current capacity of the FlexBuf. If the size nears this value
125 * then the entire buffer is resized to accomidate more data.
126 *@returns The current capacity of the FlexBuf.
127 */
128 int getCapacity();
129
130 /**
131 * Removes nAmount bytes from the begning of the buffer. Actually, if
132 * nAmount happens to be negative it will remove tha absolute value of
133 * nValue bytes from the end of the buffer, like the old delData command.
134 *@param nAmount The number of bytes used.
135 *@returns True if everything was successful, false if there was an error.
136 */
137 bool usedData( int nAmount );
138
139 /** Finds the first instance of the given character in the buffer and
140 * returns an index to it.
141 *@param cTarget The character you're looking for.
142 *@returns The index of the first instance of the given character, or
143 * -1 if it just wasn't found.
144 */
145 int findChar( char cTarget );
146
147private:
148 /** The raw storage location of the FlexBuf. */
149 char *lpBuf;
150 /** The real size of the FlexBuf. */
151 int nSize;
152 /** Where the last char is. */
153 int nLastChar;
154 /** Where the first char is. */
155 int nFirstChar;
156 /** The amount of real data in the FlexBuf. This is effectively nLastChar-nFirstChar. */
157 int nFill;
158};
159
160#endif
diff --git a/src/hashfunction.cpp b/src/hashfunction.cpp
new file mode 100644
index 0000000..51f2259
--- /dev/null
+++ b/src/hashfunction.cpp
@@ -0,0 +1,10 @@
1#include "hashfunction.h"
2
3HashFunction::HashFunction()
4{
5}
6
7HashFunction::~HashFunction()
8{
9}
10
diff --git a/src/hashfunction.h b/src/hashfunction.h
new file mode 100644
index 0000000..cbcf70f
--- /dev/null
+++ b/src/hashfunction.h
@@ -0,0 +1,48 @@
1#ifndef HASH_FUNCTION
2#define HASH_FUNCTION
3
4/** This represents the shell of a hash function. It must be aggregated in
5 * order to be used. Please read about it's two functions for specificatins
6 * relating to what values will be passed to them and what they should return
7 * for creating your own hash functions.
8 *@author Mike Buland.
9 */
10class HashFunction
11{
12public:
13 /**
14 * Standard Constructor.
15 */
16 HashFunction();
17
18 /**
19 * Standard Deconstructor.
20 */
21 virtual ~HashFunction();
22
23 /** Hashes the value represnted by id. This must return a fairly unique
24 * number in the range of 0-2^32 (or whatever the size of an unsigned long
25 * is on your system) based on the id given. The faster the number changes
26 * the better in a general sence. The return value will be the index
27 * (after probing takes place) to the data assosiated with an id, so this
28 * function should always produce the same number for any given id.
29 *@param id The identifier to use to create a unique numerical identifier.
30 *@returns A mostly unique numerical identifier generated using the given
31 * id.
32 */
33 virtual unsigned long int hash( const void *id ) = 0;
34
35 /** This function must compare two ids in the format that this hashfunction
36 * accepts. For example, if the hash function hashes strings it should
37 * probably { return strcmp( id1, id2 ) == 0 }.
38 *@param id1 One value to use in the comparison
39 *@param id2 Another value to use in the comparison
40 *@returns True if the two values match, otherwise false.
41 */
42 virtual bool cmpIDs( const void *id1, const void *id2 ) = 0;
43
44// virtual void *createPersistantID( const void *id ) = 0;
45// virtual void destroyPersistantID( const void *id ) = 0;
46};
47
48#endif
diff --git a/src/hashfunctioncasestring.cpp b/src/hashfunctioncasestring.cpp
new file mode 100644
index 0000000..6361f45
--- /dev/null
+++ b/src/hashfunctioncasestring.cpp
@@ -0,0 +1,39 @@
1#include <stdlib.h>
2#include <string.h>
3#include <ctype.h>
4#include "hashfunctioncasestring.h"
5
6HashFunctionCaseString::HashFunctionCaseString()
7{
8}
9
10HashFunctionCaseString::~HashFunctionCaseString()
11{
12}
13
14unsigned long int HashFunctionCaseString::hash( const void *id )
15{
16 const char *str = (const char *)id;
17 unsigned long int nPos = 0;
18 for( int j = 0; str[j] != '\0'; j++ )
19 {
20 nPos = tolower(str[j]) + (nPos << 6) + (nPos << 16) - nPos;
21// nPos += nPos<<16|(((unsigned long int)tolower(str[j]))<<((j*7)%24));
22 }
23 return nPos;
24}
25
26bool HashFunctionCaseString::cmpIDs( const void *id1, const void *id2 )
27{
28 const char *str1 = (const char *)id1;
29 const char *str2 = (const char *)id2;
30
31 int j;
32 for( j = 0; str1[j] != '\0' && str2[j] != '\0'; j++ )
33 {
34 if( tolower(str1[j]) != tolower(str2[j]) )
35 return false;
36 }
37 return (str1[j]==str2[j]);
38}
39
diff --git a/src/hashfunctioncasestring.h b/src/hashfunctioncasestring.h
new file mode 100644
index 0000000..9ca3d48
--- /dev/null
+++ b/src/hashfunctioncasestring.h
@@ -0,0 +1,28 @@
1#ifndef HASH_FUNCTION_CASE_STRING
2#define HASH_FUNCTION_CASE_STRING
3
4#include "hashfunction.h"
5
6/** A hash function for string data. This hash function does strings, but is
7 * actually generalized to handle any binary stream of characters terminated
8 * by a null character. This is different than HashFunctionString in that
9 * this does comparisons without regaurd to case.
10 *@author Mike Buland.
11 */
12class HashFunctionCaseString : public HashFunction
13{
14public:
15 /**
16 * Standard Constructor.
17 */
18 HashFunctionCaseString();
19
20 /**
21 * Standard Deconstructor.
22 */
23 ~HashFunctionCaseString();
24 unsigned long int hash( const void *id );
25 bool cmpIDs( const void *id1, const void *id2 );
26};
27
28#endif
diff --git a/src/hashfunctionint.cpp b/src/hashfunctionint.cpp
new file mode 100644
index 0000000..4bd0feb
--- /dev/null
+++ b/src/hashfunctionint.cpp
@@ -0,0 +1,20 @@
1#include "hashfunctionint.h"
2
3HashFunctionInt::HashFunctionInt()
4{
5}
6
7HashFunctionInt::~HashFunctionInt()
8{
9}
10
11unsigned long int HashFunctionInt::hash( const void *id )
12{
13 return (unsigned long)(id);
14}
15
16bool HashFunctionInt::cmpIDs( const void *id1, const void *id2 )
17{
18 return (unsigned long)(id1) == (unsigned long)(id2);
19}
20
diff --git a/src/hashfunctionint.h b/src/hashfunctionint.h
new file mode 100644
index 0000000..57bce89
--- /dev/null
+++ b/src/hashfunctionint.h
@@ -0,0 +1,26 @@
1#ifndef HASH_FUNCTION_INT
2#define HASH_FUNCTION_INT
3
4#include "hashfunction.h"
5
6/** A hash function for integer data. Really, this does almost nothing except
7 * ensure we're dealing with positive indicies.
8 *@author Mike Buland.
9 */
10class HashFunctionInt : public HashFunction
11{
12public:
13 /**
14 * Standard Constructor.
15 */
16 HashFunctionInt();
17
18 /**
19 * Standard Deconstructor.
20 */
21 ~HashFunctionInt();
22 unsigned long int hash( const void *id );
23 bool cmpIDs( const void *id1, const void *id2 );
24};
25
26#endif
diff --git a/src/hashfunctionstring.cpp b/src/hashfunctionstring.cpp
new file mode 100644
index 0000000..8ea9f57
--- /dev/null
+++ b/src/hashfunctionstring.cpp
@@ -0,0 +1,36 @@
1#include "hashfunctionstring.h"
2
3HashFunctionString::HashFunctionString()
4{
5}
6
7HashFunctionString::~HashFunctionString()
8{
9}
10
11unsigned long int HashFunctionString::hash( const void *id )
12{
13 const char *str = (const char *)id;
14 unsigned long int nPos = 0;
15 for( int j = 0; str[j] != '\0'; j++ )
16 {
17 nPos = str[j] + (nPos << 6) + (nPos << 16) - nPos;
18// nPos += nPos<<16|(((unsigned long int)str[j])<<((j*7)%24));
19 }
20 return nPos;
21}
22
23bool HashFunctionString::cmpIDs( const void *id1, const void *id2 )
24{
25 const char *str1 = (const char *)id1;
26 const char *str2 = (const char *)id2;
27
28 int j;
29 for( j = 0; str1[j] != '\0' && str2[j] != '\0'; j++ )
30 {
31 if( str1[j] != str2[j] )
32 return false;
33 }
34 return (str1[j]==str2[j]);
35}
36
diff --git a/src/hashfunctionstring.h b/src/hashfunctionstring.h
new file mode 100644
index 0000000..566f8ae
--- /dev/null
+++ b/src/hashfunctionstring.h
@@ -0,0 +1,27 @@
1#ifndef HASH_FUNCTION_STRING
2#define HASH_FUNCTION_STRING
3
4#include "hashfunction.h"
5
6/** A hash function for string data. This hash function does strings, but is
7 * actually generalized to handle any binary stream of characters terminated
8 * by a null character.
9 *@author Mike Buland.
10 */
11class HashFunctionString : public HashFunction
12{
13public:
14 /**
15 * Standard Constructor.
16 */
17 HashFunctionString();
18
19 /**
20 * Standard Deconstructor.
21 */
22 ~HashFunctionString();
23 unsigned long int hash( const void *id );
24 bool cmpIDs( const void *id1, const void *id2 );
25};
26
27#endif
diff --git a/src/hashtable.cpp b/src/hashtable.cpp
new file mode 100644
index 0000000..9dfe653
--- /dev/null
+++ b/src/hashtable.cpp
@@ -0,0 +1,345 @@
1#include <string.h>
2#include <stdio.h>
3#include <math.h>
4
5#include "hashtable.h"
6
7HashTable::HashTable( HashFunction *hNewFunc, unsigned long int nInitSize, bool bAllowDupes )
8{
9 hFunc = hNewFunc;
10 nTableSize = nextPrime( nInitSize );
11 aTable = new HashNode[nTableSize];
12 //for( int j = 0; j < nTableSize; j++ ) if( aTable[j].id || aTable[j].data || aTable[j].bDeleted ) printf("Unclean entry\n");
13 nSize = 0;
14 nFilled = 0;
15 this->bAllowDupes = bAllowDupes;
16}
17
18HashTable::~HashTable()
19{
20 delete[] aTable;
21 delete hFunc;
22}
23
24void HashTable::set( int j, const void *newID, const void *newData )
25{
26 if( newData == NULL )
27 {
28 printf("Inserting NULL data is indestinguishable from uninserted data!\n");
29 }
30 aTable[j].id = newID;
31 aTable[j].data = newData;
32}
33
34bool HashTable::isFilled( int j )
35{
36 return (aTable[j].id != NULL)||(aTable[j].bDeleted);
37}
38
39bool HashTable::reHash( unsigned long int nNewSize )
40{
41 HashNode *aOldTable = aTable;
42 unsigned long int oldSize = nTableSize;
43
44 // If the table can still be used if we just get rid of deleted items, don't
45 // change the size of the table, otherwise, go ahead and use the number
46 // passed in.
47 if( nSize > nTableSize>>1 )
48 {
49 nTableSize = nextPrime( nNewSize );
50 }
51
52 aTable = newTable( nTableSize );
53 //for( int j = 0; j < nTableSize; j++ ) if( aTable[j].id || aTable[j].data || aTable[j].bDeleted ) printf("Unclean entry\n");
54
55 nSize = 0;
56 nFilled = 0;
57
58 for( unsigned long int j = 0; j < oldSize; j++ )
59 {
60 if( aOldTable[j].id != NULL && aOldTable[j].bDeleted == false )
61 {
62 insert( aOldTable[j].id, aOldTable[j].data );
63 }
64 }
65
66 delete[] aOldTable;
67}
68
69unsigned long int HashTable::probe( unsigned long int nStart, const void *id )
70{
71 int nHash = nStart;
72 nStart = nStart%nTableSize;
73 if( bAllowDupes == true )
74 {
75 for(
76 unsigned long int j=0;
77 isFilled( nStart ) && j < 32;
78 nStart = (nStart+(1<<j))%nTableSize, j++
79 );
80
81 /**
82 * This is an ugly little hack. If the hash table is too full in allow-
83 * dups mode we have to fall back on a linear search, otherwise you can
84 * only get up to 32 entries with the same name.
85 */
86 if( isFilled( nStart ) )
87 {
88 int nOldStart = nStart;
89 for(
90 nStart++;
91 isFilled( nStart ) && nStart != nOldStart;
92 nStart = (nStart+1)%nTableSize
93 );
94 }
95 }
96 else
97 {
98 for(
99 unsigned long int j=0;
100 isFilled( nStart ) && j < 32;
101 nStart = (nStart+(1<<j))%nTableSize, j++
102 )
103 {
104 if( isFilled( nStart ) )
105 {
106 if( hFunc->cmpIDs( aTable[nStart].id, id ) == true &&
107 aTable[nStart].bDeleted == false )
108 {
109 return nStart;
110 }
111 }
112 }
113 }
114 // This is our insurance, if the table is full, then go ahead and rehash,
115 // then try again.
116 if( isFilled( nStart ) )
117 {
118 reHash( getCapacity()*2 );
119 return probe( nHash, id );
120 }
121 return nStart;
122}
123
124HashTable::HashNode *HashTable::newTable( unsigned long int nNewSize )
125{
126 return new HashNode[nNewSize];
127}
128
129#ifdef HASH_DEBUG_VIS
130void HashTable::printDebugLine( const char *exData )
131{
132 char *buf = new char[getCapacity()+3];
133 int j;
134 buf[0] = '[';
135 for( j = 0; j < getCapacity(); j++ )
136 {
137 buf[j+1] = (aTable[j].bDeleted)?('X'):((isFilled( j ))?('#'):('-'));
138 }
139 buf[j+1] = ']';
140 buf[j+2] = '\0';
141 printf("%s %s\n", buf, exData );
142 delete[] buf;
143}
144#endif
145
146bool HashTable::insert( const void *id, const void *data )
147{
148 unsigned long int nPos = probe( hFunc->hash( id ), id )%nTableSize;
149
150 if( bAllowDupes == true )
151 {
152 if( aTable[nPos].id == NULL && aTable[nPos].bDeleted == false )
153 {
154 set( nPos, id, data );
155#ifdef HASH_DEBUG_VIS
156 printDebugLine( (const char *)id );
157#endif
158 nSize++;
159 nFilled++;
160 return true;
161 }
162 else
163 {
164 return false;
165 }
166 }
167 else
168 {
169 if( aTable[nPos].id == NULL && aTable[nPos].bDeleted == false )
170 {
171 set( nPos, id, data );
172#ifdef HASH_DEBUG_VIS
173 printDebugLine( (const char *)id );
174#endif
175 nSize++;
176 nFilled++;
177 return true;
178 }
179 else if( hFunc->cmpIDs( aTable[nPos].id, id ) == true )
180 {
181 set( nPos, id, data );
182#ifdef HASH_DEBUG_VIS
183 printDebugLine( (const char *)id );
184#endif
185 return true;
186 }
187 else
188 {
189 return false;
190 }
191 }
192}
193
194const void *HashTable::get( const void *id, unsigned long int nSkip )
195{
196 unsigned long int nPos = hFunc->hash( id )%nTableSize;
197
198 for( unsigned long int j=0; j < 32; nPos = (nPos+(1<<j))%nTableSize, j++ )
199 {
200 if( !isFilled( nPos ) ) return NULL;
201 if( hFunc->cmpIDs( id, aTable[nPos].id ) &&
202 aTable[nPos].bDeleted == false )
203 {
204 if( nSkip == 0 )
205 {
206 return aTable[nPos].data;
207 }
208 else
209 {
210 nSkip--;
211 }
212 }
213 }
214
215 return NULL;
216}
217
218void *HashTable::getFirstItemPos()
219{
220 HashPos *pos = new HashPos;
221 return pos;
222}
223
224const void *HashTable::getItemData( void *xPos )
225{
226 return aTable[((HashPos *)xPos)->nPos].data;
227}
228
229const void *HashTable::getItemID( void *xPos )
230{
231 return aTable[((HashPos *)xPos)->nPos].id;
232}
233
234void *HashTable::getNextItemPos( void *xPos )
235{
236 HashPos *pos = (HashPos *)xPos;
237 if( pos->bStarted == false )
238 {
239 pos->bStarted = true;
240 pos->nPos = 0;
241 }
242 else
243 {
244 pos->nPos++;
245 }
246 if( pos->nPos < nTableSize )
247 {
248 for( ; pos->nPos < nTableSize; pos->nPos++ )
249 {
250 if( isFilled( pos->nPos ) &&
251 aTable[pos->nPos].bDeleted == false )
252 {
253 return xPos;
254 }
255 }
256 }
257
258 delete pos;
259
260 return NULL;
261}
262
263// Big-O sqrt(n)
264// Change this to be erethpothynies table with a storage
265// lookup later on.
266bool HashTable::isPrime (int num)
267{
268 if (num == 2) // the only even prime
269 return true;
270 else if (num % 2 == 0) // other even numbers are composite
271 return false;
272 else
273 {
274 //bool prime = true;
275 int divisor = 3;
276 int upperLimit = static_cast<int>(sqrt(num) + 1);
277 while (divisor <= upperLimit)
278 {
279 if (num % divisor == 0)
280 return false;
281 // prime = false;
282 divisor +=2;
283 }
284 return true;
285 }
286}
287
288// Big-O n^(3/2)
289int HashTable::nextPrime( int base )
290{
291 int nPrime;
292 for( nPrime = base; isPrime( nPrime ) == false; nPrime++ );
293 return nPrime;
294}
295
296unsigned long int HashTable::getCapacity()
297{
298 return nTableSize;
299}
300
301unsigned long int HashTable::getSize()
302{
303 return nSize;
304}
305
306double HashTable::getLoad()
307{
308 return (double)(nFilled)/(double)(nTableSize);
309}
310
311const void *HashTable::operator[](const void *id)
312{
313 return get( id );
314}
315
316bool HashTable::del( const void *id, int nSkip )
317{
318 unsigned long int nPos = hFunc->hash( id )%nTableSize;
319
320 for( unsigned long int j=0; j < 32; nPos = (nPos+(1<<j))%nTableSize, j++ )
321 {
322 if( !isFilled( nPos ) ) return false;
323 if( hFunc->cmpIDs( id, aTable[nPos].id ) &&
324 aTable[nPos].bDeleted == false )
325 {
326 if( nSkip == 0 )
327 {
328 aTable[nPos].bDeleted = true;
329// aTable[nPos].
330 nSize--;
331#ifdef HASH_DEBUG_VIS
332 printDebugLine( (const char *)id );
333#endif
334 return true;
335 }
336 else
337 {
338 nSkip--;
339 }
340 }
341 }
342
343 return false;
344}
345
diff --git a/src/hashtable.h b/src/hashtable.h
new file mode 100644
index 0000000..d14be71
--- /dev/null
+++ b/src/hashtable.h
@@ -0,0 +1,299 @@
1/**\hashtable.h
2 * Describes the HashFunction, HashFunctionString, and HashTable classes. It
3 * was just easier to put them all in one set of files.
4 *@author Mike Buland
5 */
6
7#ifndef HASH_TABLE_H
8#define HASH_TABLE_H
9
10//Uncomment this line to see a cool text-mode visualization of what's going on
11//#define HASH_DEBUG_VIS 1
12
13#include <stdlib.h>
14#include <string.h>
15#include <ctype.h>
16
17#include "hashfunction.h"
18
19/**
20 * A simple yet flexable hash-table. This uses several tricks to help ensure
21 * that the table is always running at maximum efficiency. You no longer have
22 * to specify a "danger fill level" when more space is needed a rehash is
23 * automatically trigered. Deleting elements is fully supported, as well as
24 * duplicate elements. To work with and allow duplicates simple construct your
25 * HashTable the way you normally would, but when deleting or getting elements
26 * you can specify a skip value. This effectively allows you to treat elements
27 * with duplicate ID's as though they were in a zero-based array. The first
28 * element inserted with a given ID would be at skip zero, the next at skip 1
29 * and so on. This allows you to quickly search for elements with duplicate
30 * names, just stop when you get a null for a skip number, i.e.
31 * <pre>
32 * for( int j = 0;; j++ )
33 * {
34 * void *pData = hash.get( myID, j );
35 * if( !pData ) break;
36 * // Do something interesting with pData
37 * }
38 * </pre>
39 * There are new features in this HashTable that also allow for memory saving
40 * when dealing with systems where many elements are being deleted from the
41 * table. In those cases the elements deleted cannot be simply deleted, instead
42 * they have to be marked as deleted and hidden from the user, but maintained in
43 * the table so that future hashing operations don't fail. When rehashing
44 * occurs all elements marked as deleted are quietly removed. In these cases,
45 * if the number of deleted items would free enough space in the table for the
46 * table to be used efficiently without resizing, it is left the same size and
47 * rehashing is performed effectively in place, allowing the deleted items to
48 * be removed.
49 * <br>
50 * For info on adding new hashing algorithms, please see the HashFunction class.
51 *@author Mike Buland
52 *@todo Fix probing for tables that allow duplicates, and delete an item, then
53 * insert an item with the same name.
54 */
55class HashTable
56{
57public:
58 /** Constructs a hash table.
59 *@param hNewFunc A pointer to a hashfunction class to use. If this is
60 * null the default general string type will be used.
61 *@param nInitSize The initial size of the hashtable.
62 *@param bAllowDupes Setting this value to true allows the system to
63 * insert more than one copy of any given key. This can be tricky, and
64 * will require you to use the nSkip parameter on the get function.
65 */
66 HashTable( HashFunction *hNewFunc, unsigned long int nInitSize, bool bAllowDupes=false );
67
68 /**
69 * Destroys the hashtable, cleaning up all internal storage, but not stored
70 * elements. Also deletes the HashFunction passed in in the constructor.
71 */
72 ~HashTable();
73
74 /** Inserts an item into the hashtable. This function will trigger a
75 * rehash if adding another item would force the table's load factor over
76 * the danger level.
77 *@param id used to find the data later.
78 *@param data The data item to insert into the table with the identifier
79 * id
80 *@returns True if insertion was successfull, and false if it failed.
81 */
82 bool insert( const void *id, const void *data );
83
84 /** Gets an item in the hashtable based on the id of that item. If there
85 * is more than one item with the same id you can use the nSkip parameter
86 * to access all of them.
87 *@param id The id of the item you're trying to find.
88 *@param nSkip The number of items with that id to skip before returning
89 * with the requested item.
90 *@returns A pointer to the data stored at the given id.
91 */
92 const void *get( const void *id, unsigned long int nSkip=0 );
93
94 /** Gets the total capacity of the hashtable. This is actually the number
95 * of total positions available inside the hashtable at the moment. This
96 * will change when the hashtable's load exceeds the danger level.
97 * Please note that this is NOT the actual amount of space available.
98 * In reality you can only access about 45-50 percent of that space.
99 *@returns The total capacity.
100 */
101 unsigned long int getCapacity();
102
103 /** Gets the number of filled in items in the hash table. This is roughly
104 * equivelent to the getSize function assosiated with the Lists.
105 *@returns The number of filled in items in the hash table.
106 */
107 unsigned long int getSize();
108
109 /** Gets the load (percentage) of filled in items in the table. This is
110 * technically the size divided by the capacity, but is definately usefull
111 * since it's required to check if it's time to rehash.
112 *@returns The table load in the range 0.0 to 1.0
113 */
114 double getLoad();
115
116 /** Sets up an xPos object for use indexing the items in the table. Call
117 * this first and follow the directions for getNextItemPos below to
118 * iterate through every item in the table, while avoiding the empty
119 * spaces.
120 *@returns A pointer to a xPos object telling the hashtable where to find
121 * the item you're looking at.
122 */
123 void *getFirstItemPos();
124
125 /** Get the item's data that is being pointed to by xPos. This is only
126 * valid after xPos was created using getFirstItemPos and getNextItemPos
127 * was called at least once.
128 *@param xPos supplied by getFirstItemPos.
129 *@returns The key value that was used to insert the data into the table.
130 */
131 const void *getItemData( void *xPos );
132
133 /** Get the item's ID that is being pointed to by xPos. This is only
134 * valid after xPos was created using getFirstItemPos and getNextItemPos
135 * was called at least once.
136 *@param xPos supplied by getFirstItemPos.
137 *@returns The key value that was used to insert the data into the table.
138 */
139 const void *getItemID( void *xPos );
140
141 /** Used for iterating through a hash table sequentially. This will
142 * update the xPos pointer to point to the next time, all ready to
143 * be accessed with getItemID and getItemData. This must be called at
144 * least once before xPos is meaningful, and will return a NULL when it
145 * has reached the last item.
146 *@param xPos This must be an object created by a call to the function
147 * getFirstItemPos, and is only meaningful to the internal routines.
148 * Aborting a call in the middle (not running to the end of the table)
149 * may result in a memory leak at the moment.
150 *@returns xPos if still iterating through the list, otherwise it will
151 * return NULL when the end has been reached and the xPos variable has
152 * been deleted.
153 */
154 void *getNextItemPos( void *xPos );
155
156 /** A helpful operator to make accessing items easier. Please note that
157 * this simply returns a pointer to the data stored internally, and cannot
158 * be used like the STL operator to store new data, use insert for that.
159 *@param id The identifier used to store the requested item.
160 *@returns The data value assosiated with the given id, or NULL if it
161 * wasn't found in the table.
162 */
163 const void *operator[](const void *id);
164
165 /**
166 * Delete the specified item from the hashtable. This actually keeps the
167 * data and marks it deleted. For all intents and purposes to the user it
168 * is deleted, except that the space is still used until a rehash is forced.
169 * This means that in hashtables where elements are being inserted and
170 * deleted frequently you may run into a higher rate of expansion.
171 *@param id The ID to delete.
172 *@param nSkip The number of similar id's to skip before deleting in a
173 * hashtable that allows duplicates.
174 *@returns True if the element was found and deleted, false otherwise.
175 */
176 bool del( const void *id, int nSkip=0 );
177
178private:
179 /**
180 * Contains info related to a position in the hashtable. Used for
181 * searching through hashtables one item at a time, in order. This class
182 * should never be created by anything but a HashTable, and should never
183 * be referenced directly. Instead the hashtable returns a void pointer,
184 * which is what should be passed back in next time you use a search
185 * function. Always finish a search, since the object is deleted at the
186 * end of the search.
187 *@author Mike Buland
188 */
189 class HashPos
190 {
191 public:
192 /** Create a blank HashPos. */
193 HashPos() { bStarted=false; nPos = 0; };
194 /** Has the search been started? */
195 bool bStarted;
196 /** The position (index) into the backend storage structure. */
197 unsigned long int nPos;
198 };
199
200 /**
201 * All data related to a single element in the hashtable. This should
202 * really only be used and manipulated by the HashTable itself.
203 *@author Mike Buland
204 */
205 typedef struct HashNode
206 {
207 public:
208 /** Create a new, empty HashNode. */
209 HashNode() { id = NULL; data = NULL; bDeleted = false; };
210 /** A pointer to the original ID that was used to key the data. */
211 const void *id;
212 /** A pointer to the data stored along with the above ID. */
213 const void *data;
214 /** Weather or not this data should really...exist */
215 bool bDeleted;
216 } HashNode;
217
218private:
219 /**
220 * Just sets the values in the element to some friendly values.
221 *@param newID The new ID to store.
222 *@param newData The new Data to store.
223 */
224 void set( int j, const void *newID, const void *newData );
225 /**
226 * Tells you if the node is filled or not.
227 *@returns True=an ID has been stored here, False=no ID.
228 */
229 bool isFilled( int j );
230 /**
231 * This actually resizes, but since every resize requires a reHash to go
232 * along with it, that's the name. This actually creates a new buffer for
233 * all of the contained data and then pulls every old element that was in
234 * the old table out and performs the hashing placement calculations again.
235 * This function skips all data that was marked as deleted, so at this
236 * point it really will be.
237 *@param nNewSize The new size to set the table to while re-hashing.
238 *@returns True if the operation was successful, false otherwise.
239 */
240 bool reHash( unsigned long int nNewSize );
241
242 /**
243 * Helper function to allocate a new table. Really just does the memory
244 * allocation.
245 *@param nNewSize The size of the table to generate.
246 *@returns A new, blank array of HashNode objects the size you specified.
247 */
248 HashNode *newTable( unsigned long int nNewSize );
249
250 /**
251 * This function is used once an actual hash code is obtained. nStart is
252 * the given hash code, which is then wrapped to the size of the table. If
253 * there is data at that location, tests are performed to see if it's the
254 * right one. If it is, then it is returned, otherwise a series of further
255 * tests based on a 2^n search pattern is performed. The position of the
256 * requested data in the back-end storage is returned if found, otherwise
257 * another less useful value is returned...
258 *@param nStart The initial hashcode of the ID testing for.
259 *@param id A pointer to the id that is being searched for.
260 *@returns The real location of the data requested.
261 */
262 unsigned long int probe( unsigned long int nStart, const void *id );
263
264 /**
265 * Simple helper function to determine if a number is prime or not.
266 * This function runs in sqrt(n) time.
267 *@param num Number to test for prime-hood.
268 *@returns True if the number is prime, false otherwise.
269 */
270 bool isPrime( int num );
271
272 /**
273 * Given any number, this function finds the first number after it that is
274 * prime. Since this number is a multiple internally it's rare that the
275 * starting number would be prime.
276 *@param base The number to start the prime search on.
277 *@returns The first prime after the number given.
278 */
279 int nextPrime( int base );
280
281#ifdef HASH_DEBUG_VIS
282 void printDebugLine( const char *exData );
283#endif
284
285 /** A pointer to the HashFunction subclass instance to use. */
286 HashFunction *hFunc;
287 /** The complete array of HashNode objects to store data in. */
288 HashNode *aTable;
289 /** The actual size of the table, not how many elements are in it. */
290 unsigned long int nTableSize;
291 /** The number of elements that are in the table. */
292 unsigned long int nSize;
293 /** The number of elements that are unavailable now. */
294 unsigned long int nFilled;
295 /** Allow duplicate ID's in the table. */
296 bool bAllowDupes;
297};
298
299#endif
diff --git a/src/http.cpp b/src/http.cpp
new file mode 100644
index 0000000..11950b7
--- /dev/null
+++ b/src/http.cpp
@@ -0,0 +1,371 @@
1#include <string.h>
2#include <stdlib.h>
3#include "http.h"
4#include "hashfunctionstring.h"
5
6Http::Http( Connection *pConnection ) : hReqHeader( new HashFunctionString(), 100 )
7{
8 pCon = pConnection;
9 nParseState = parseInit;
10}
11
12Http::~Http()
13{
14 for( int j = 0; j < lStrings.getSize(); j++ )
15 {
16 delete (std::string *)lStrings[j];
17 }
18}
19
20bool Http::parseRequest()
21{
22 for(;;)
23 {
24 pCon->readInput();
25 switch( nParseState )
26 {
27 case parseInit:
28 {
29 int nLen = pCon->scanInputFor( CR );
30 if( nLen == -1 )
31 {
32 return false;
33 }
34 else
35 {
36 nReqType = getRequestType( pCon->getInput() );
37 pCon->usedInput( pCon->scanInputFor(' ')+1 );
38
39 nLen = pCon->scanInputFor(' ');
40 sReqURI.append( pCon->getInput(), nLen );
41 pCon->usedInput( nLen+1 );
42
43 if( !strncmp( pCon->getInput(), "HTTP/", 5 ) )
44 {
45 char mbuf[2]={'\0','\0'};
46 unsigned char major, minor;
47
48 pCon->usedInput( 5 );
49 mbuf[0] = pCon->getInput()[0];
50 major = (unsigned char)atoi(mbuf);
51 mbuf[0] = pCon->getInput()[2];
52 minor = (unsigned char)atoi(mbuf);
53 setRequestVersion( major, minor );
54 if( checkRequestVer() )
55 {
56 nParseState = parseHeader;
57 }
58 else
59 {
60 setResponseStatus( statusHTTPVersionNotSupported );
61 printf("Verson not supported.\n");
62 return true;
63 }
64
65 pCon->usedInput( 5 );
66 }
67 else
68 {
69 setResponseStatus( statusBadRequest );
70 }
71
72 //return false;
73 }
74 }
75 break;
76
77 case parseHeader:
78 {
79 int nLen = pCon->scanInputFor( CR );
80 if( nLen == -1 )
81 {
82 return false;
83 }
84 else if( nLen == 0 )
85 {
86 // We've got our double-newline, time for content.
87 pCon->usedInput( 2 );
88 setResponseStatus( statusOK );
89 return true;
90 }
91 else
92 {
93 nLen = pCon->scanInputFor(':');
94 if( nLen == -1 )
95 {
96 printf("No colon? what are you trying to pull?\n");
97 }
98 else
99 {
100 std::string *pName = new std::string( pCon->getInput(), nLen );
101 lStrings.append( pName );
102 pCon->usedInput( nLen+1 );
103
104 nLen = pCon->scanInputFor( CR );
105 std::string *pValue = convSpaceString( pCon->getInput(), nLen );
106 lStrings.append( pValue );
107 pCon->usedInput( nLen+2 );
108
109 hReqHeader.insert(
110 pName->c_str(),
111 pValue->c_str()
112 );
113
114 printf("::%s = \"%s\"\n",
115 pName->c_str(),
116 pValue->c_str()
117 );
118 }
119 }
120 }
121 break;
122
123 case parseFinished:
124 break;
125 }
126 }
127}
128
129bool Http::buildResponse( short nResponseCode, const char *sResponse )
130{
131 if( nResponseCode > 0 )
132 {
133 nResStatus = nResponseCode;
134 }
135
136 if( sResponse == NULL )
137 {
138 sResStatusStr = "uh yeah";
139 }
140 else
141 {
142 sResStatusStr = sResponse;
143 }
144
145 time_t curTime;
146 time( &curTime );
147 gmtime_r( &curTime, &tResTime );
148
149 sServerStr = "YFHttp/0.0.1";
150 bResPersistant = false;
151
152 //char buf[30];
153 //strftime( buf, 30, "%a, %d %b %Y %H:%M:%S GMT", &tResponseTime );
154
155 return true;
156}
157
158bool Http::sendResponse()
159{
160 char buf[256];
161
162 sprintf( buf, "HTTP/1.1 %d %s\r\n", nResStatus, sResStatusStr.c_str() );
163 pCon->appendOutput( buf );
164
165 strftime( buf, 256, "Date: %a, %d %b %Y %H:%M:%S GMT\r\n", &tResTime );
166 pCon->appendOutput( buf );
167
168 sprintf( buf, "Server: %s\r\n", sServerStr.c_str() );
169 pCon->appendOutput( buf );
170
171 if( bResPersistant )
172 {
173 }
174 else
175 {
176 pCon->appendOutput("Connection: close\r\n");
177 }
178
179 sprintf( buf, "Content-Type: %s\r\n", sResMime.c_str() );
180 pCon->appendOutput( buf );
181
182 sprintf( buf, "Content-Length: %d\r\n", sResContent.size() );
183 pCon->appendOutput( buf );
184
185 pCon->appendOutput("\r\n");
186
187 pCon->appendOutput( sResContent.c_str(), sResContent.size() );
188
189 return true;
190}
191
192void Http::setResponsePersistant( bool bPersistant )
193{
194 bResPersistant = bPersistant;
195}
196
197void Http::setResponseContent( const char *sMime, const char *sContent, int nLen )
198{
199 sResMime = sMime;
200 sResContent.erase();
201 sResContent.append( sContent, nLen );
202}
203
204std::string *Http::convSpaceString( const char *sStr, int nLen )
205{
206 int nNewLen = 0;
207 bool bStart = true;
208 bool bSpace = false;
209
210 for( int j = 0; j < nLen; j++ )
211 {
212 if( sStr[j] == ' ' || sStr[j] == '\t' )
213 {
214 if( bStart )
215 {
216 }
217 else if( bSpace == false )
218 {
219 bSpace = true;
220 nNewLen++;
221 }
222 }
223 else
224 {
225 bStart = false;
226 bSpace = false;
227 nNewLen++;
228 }
229 }
230 if( bSpace )
231 {
232 nNewLen--;
233 }
234
235 std::string *pSStr = new std::string;
236 //char *pStr = pSStr->c_str();
237 nNewLen = 0;
238 bStart = true;
239 bSpace = false;
240
241 for( int j = 0; j < nLen; j++ )
242 {
243 if( sStr[j] == ' ' || sStr[j] == '\t' )
244 {
245 if( bStart )
246 {
247 }
248 else if( bSpace == false )
249 {
250 bSpace = true;
251 *pSStr += ' ';
252 //pStr[nNewLen++] = ' ';
253 }
254 }
255 else
256 {
257 bStart = false;
258 bSpace = false;
259 *pSStr += sStr[j];
260 //pStr[nNewLen++] = sStr[j];
261 }
262 }
263 if( bSpace == true )
264 {
265 nNewLen--;
266// pStr[nNewLen] = '\0';
267 }
268
269 return pSStr;
270}
271
272const char *Http::getRequestURI()
273{
274 return sReqURI.c_str();
275}
276
277short Http::getRequestType( const char *sType )
278{
279 if( !strncmp( sType, "OPTIONS", 7 ) )
280 {
281 return reqOptions;
282 }
283 else if( !strncmp( sType, "GET", 3 ) )
284 {
285 return reqGet;
286 }
287 else if( !strncmp( sType, "HEAD", 4 ) )
288 {
289 return reqHead;
290 }
291 else if( !strncmp( sType, "POST", 4 ) )
292 {
293 return reqPost;
294 }
295 else if( !strncmp( sType, "PUT", 3 ) )
296 {
297 return reqPut;
298 }
299 else if( !strncmp( sType, "DELETE", 6 ) )
300 {
301 return reqDelete;
302 }
303 else if( !strncmp( sType, "TRACE", 5 ) )
304 {
305 return reqTrace;
306 }
307 else if( !strncmp( sType, "CONNECT", 7 ) )
308 {
309 return reqConnect;
310 }
311 else
312 {
313 printf(" Uh oh, extension!\n");
314 return reqExtension;
315 }
316}
317
318const char *Http::getRequestType( short nType )
319{
320 switch( nType )
321 {
322 case reqOptions: return "OPTIONS";
323 case reqGet: return "GET";
324 case reqHead: return "HEAD";
325 case reqPost: return "POST";
326 case reqPut: return "PUT";
327 case reqDelete: return "DELETE";
328 case reqTrace: return "TRACE";
329 case reqConnect: return "CONNECT";
330 case reqExtension: return "EXTENSION";
331 default: return "INVALID VALUE";
332 }
333}
334
335short Http::getRequestType()
336{
337 return nReqType;
338}
339
340const char *Http::getRequestTypeStr()
341{
342 return getRequestType( nReqType );
343}
344
345void Http::setResponseStatus( short nStatus )
346{
347 nResStatus = nStatus;
348}
349
350void Http::setRequestVersion( unsigned char nMajor, unsigned char nMinor )
351{
352 cReqVersion = (nMajor<<4)|nMinor;
353}
354
355unsigned char Http::getRequestMinorVer()
356{
357 return cReqVersion&0x0F;
358}
359
360unsigned char Http::getRequestMajorVer()
361{
362 return cReqVersion>>4;
363}
364
365bool Http::checkRequestVer()
366{
367 if( cReqVersion == HTTP11 )
368 return true;
369 return false;
370}
371
diff --git a/src/http.h b/src/http.h
new file mode 100644
index 0000000..4ee4470
--- /dev/null
+++ b/src/http.h
@@ -0,0 +1,271 @@
1/**\file http.h
2 * Describe a Hyper Text Transfer Protocol processor. This class will allow
3 * any program to act as either an HTTP server, client, or both. It contains
4 * a number of additional helpers and subclasses.
5 *@author Mike Buland
6 */
7
8#ifndef HTTP_H
9#define HTTP_H
10
11#include <iostream>
12#include "connection.h"
13#include "linkedlist.h"
14#include "hashtable.h"
15
16#define CR '\r' /**< The ASCII value of a Carrage Return */
17#define LF '\n' /**< The ASCII value of a Line Feed */
18#define CRLF CR LF /**< Combo of CR+LF for use in http */
19
20/**
21 * Macro to create combined http version codes. This just makes processing a
22 * little bit faster for the most part.
23 *@param maj Major version number, between 0 and 15
24 *@param min Minor version number, between 0 and 15
25 *@returns A one byte combined version number suitable for use in switches.
26 */
27#define HTTPVER( maj, min ) ((maj<<4)|(min))
28
29#define HTTP10 HTTPVER( 1, 0 ) /**< Combined version code for http 1.0 */
30#define HTTP11 HTTPVER( 1, 1 ) /**< Combined version code for http 1.1 */
31
32/**
33 * This is the master HTTP processing class. One instance handles one
34 * transaction, in the future a different mechanism may be thought up, but for
35 * now this means that you must create multiple objects to handle a single
36 * connection that contains multiple requests.
37 * In the constructor the Http class is given a connection object. This object
38 * should already be initialized and connected to whatever socket it wants to
39 * be sending and receiving data to and from. Once that's done you can call
40 * parseRequest if you're acting as a server, or a variety of buildRequest
41 * functions to create and send a request if you're a client.
42 * Please note that this class does not provide any HTTP or extended format
43 * processing systems, but will allow for mime types tables to be registered.
44 *@author Mike Buland
45 */
46class Http
47{
48public:
49 /**
50 * Create an Http object tied to an existing connection object.
51 *@param pConnection The live connection object to deal with.
52 */
53 Http( Connection *pConnection );
54
55 /**
56 * Standard Deconstructor.
57 */
58 ~Http();
59
60 /**
61 * Perform all parsing needed to figure out what an HTTP client wants from
62 * us. This will setup a number of properties in the Http object itself
63 * and has the possibility of setting one or more response states initially.
64 * These states should be checked for immediately after parsing to see if
65 * an appropriate error message should be generated. These errors can
66 * include issues with protocol, data formats, or unknown versions of the
67 * protocol.
68 *@returns True means that all processing is finished, false means that
69 * the parseRequest function should be called again when more data is
70 * ready. A return value of true does not indicate success, only that
71 * processing is finished, the getResponseStatus function should be called
72 * to see what status was set in the parse routine. A 200 indicates that
73 * as far as the parser is concerned, everything when smoothly. Otherwise
74 * it's your responsibility to build the appropriate error response body
75 * (like an html file) and send it as the response.
76 */
77 bool parseRequest();
78
79 /**
80 * Get a request type's internal Http object id based on the string
81 * representation. These can be any HTTP/1.1 standard request type.
82 *@param sType The string that should be checked for type. This is in all
83 * caps, just like if it came from the HTTP client, which is most often
84 * the case.
85 *@returns The numerical ID of the given request type. Please note that
86 * HTTP/1.1 standard specifies that any string is valid here as long as
87 * the non-basic string is a request type understood by the serving
88 * software. This means that anything that is non-standard will return
89 * a type reqExtension and not an error. This is not a mistake.
90 */
91 short getRequestType( const char *sType );
92
93 /**
94 * Get the string representation of an Http object request type integer ID.
95 * This is used mainly for debugging to be sure the system has what we
96 * think it has.
97 *@param nType The integer ID of the request type to process.
98 *@returns The HTTP/1.1 string representation of that Http object ID code.
99 */
100 const char *getRequestType( short nType );
101
102 /**
103 * Returns the Http object request type ID code that is stored in the
104 * object by either the parseRequest function or use of the buildRequest
105 * functions.
106 *@returns The ID of the request type stored in the object.
107 */
108 short getRequestType();
109
110 /**
111 * Same as getRequestType, only returns the string representation.
112 *@returns The string representation of the request type ID stored in the
113 * object.
114 */
115 const char *getRequestTypeStr();
116
117 /**
118 * Sets the version of the request used by the system. This will be used
119 * by parse request, but is also part of the buildRequest tool functions.
120 *@param nMajor The major version number.
121 *@param nMinor The minor version number.
122 */
123 void setRequestVersion( unsigned char nMajor, unsigned char nMinor );
124
125 /**
126 * Gets the major version number of the protocol used/to be used in this
127 * request.
128 *@returns The major version number of the request protocol.
129 */
130 unsigned char getRequestMinorVer();
131
132 /**
133 * Gets the minor version number of the protocol used/to be used in this
134 * request.
135 *@returns The minor version number of the request protocol.
136 */
137 unsigned char getRequestMajorVer();
138
139 /**
140 * Checks the stored request version against an internal table of supported
141 * protocol versions.
142 *@returns True if the protocol version is supported, false otherwise.
143 */
144 bool checkRequestVer();
145
146 /**
147 * Converts an arbitrary string to a new string object with space saving
148 * operations performed ala the HTTP/1.1 specs. All leading and trailing
149 * whitespace is stripped, and all whitespace within the string is reduced
150 * to a single space char.
151 *@param sStr A pointer to the string data to process.
152 *@param nLen The length of the string to process. Since this function is
153 * often called on stream data, there is no null terminator where we need
154 * one. This is here for convinience so the data doesn't need to be hacked
155 * up or moved to an intermediate buffer.
156 *@returns A new string that may well be shorter than the original but that
157 * will have the same value as far as the HTTP/1.1 specs are concerned.
158 */
159 std::string *convSpaceString( const char *sStr, int nLen );
160
161 /**
162 * Gets a string pointer to the URI that was/is being requested. This can
163 * be any RFC standard URI, with or without protocol and domain.
164 *@returns A pointer to the URI that was/is being requested.
165 */
166 const char *getRequestURI();
167
168 /**
169 * Set a new response status. This status can be anything that the HTTP
170 * specs allow. Other values are allowed as well, but beware, not all
171 * servers/clients will accept values that are not in the tables in this
172 * class.
173 *@param nStatus The status to set.
174 */
175 void setResponseStatus( short nStatus );
176
177 bool buildResponse( short nResponseCode=-1, const char *sResponse=NULL );
178 void setResponseContent( const char *sMime, const char *sContent, int nLen );
179 void setResponsePersistant( bool bPersistant );
180 bool sendResponse();
181
182 enum
183 {
184 reqOptions,
185 reqGet,
186 reqHead,
187 reqPost,
188 reqPut,
189 reqDelete,
190 reqTrace,
191 reqConnect,
192 reqExtension
193 };
194
195 enum
196 {
197 statusContinue = 100,
198 statusSwitchProto = 101,
199
200 statusOK = 200,
201 statusCreated = 201,
202 statusAccepted = 202,
203 statusNonAuthInfo = 203,
204 statusNoContent = 204,
205 statusResetContent = 205,
206 statusPartialContent = 206,
207
208 statusMultiChoices = 300,
209 statusMovedPermanently = 301,
210 statusFound = 302,
211 statusSeeOther = 303,
212 statusNotModified = 304,
213 statusUseProxy = 305,
214 statusUnused = 306,
215 statusTempRedirect = 307,
216
217 statusBadRequest = 400,
218 statusUnauthorized = 401,
219 statusPaymentRequired = 402,
220 statusForbidden = 403,
221 statusNotFound = 404,
222 statusMethodNotAllowed = 405,
223 statusNotAcceptable = 406,
224 statusProxyAuthRequired = 407,
225 statusRequestTimeout = 408,
226 statusConflict = 409,
227 statusGone = 410,
228 statusLengthRequired = 411,
229 statusPreconditionFailed = 412,
230 statusRequestEntityTooLarge = 413,
231 statusRequestURITooLong = 414,
232 statusUnsupportedMediaType = 415,
233 statusRequestedRangeNotSatisfiable = 416,
234 statusExpectationFailed = 417,
235
236 statusInternalServerError = 500,
237 statusNotImplemented = 501,
238 statusBadGateway = 502,
239 statusServiceUnavailable = 503,
240 statusGatewayTimeout = 504,
241 statusHTTPVersionNotSupported = 505
242 };
243
244private:
245 Connection *pCon;
246 unsigned char nParseState;
247
248 short nReqType;
249 std::string *pReqStr;
250 std::string sReqURI;
251 unsigned char cReqVersion;
252 HashTable hReqHeader;
253 LinkedList lStrings;
254
255 std::string sServerStr;
256 std::string sResMime;
257 std::string sResContent;
258 std::string sResStatusStr;
259 bool bResPersistant;
260 struct tm tResTime;
261 short nResStatus;
262
263 enum
264 {
265 parseInit,
266 parseHeader,
267 parseFinished
268 };
269};
270
271#endif
diff --git a/src/linkedlist.cpp b/src/linkedlist.cpp
new file mode 100644
index 0000000..78a615a
--- /dev/null
+++ b/src/linkedlist.cpp
@@ -0,0 +1,227 @@
1/***************************************************************************
2 linkedlist.cpp - description
3 -------------------
4 begin : Sun Oct 19 2003
5 copyright : (C) 2003 by Mike Buland
6 email : eichlan@yf-soft.com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "linkedlist.h"
19
20LinkedList::LinkedList( )
21{
22 pBase = NULL;
23 pTop = NULL;
24 pLast = NULL;
25 nSize = 0;
26 nLast = -1;
27}
28
29LinkedList::~LinkedList( )
30{
31/*
32 Link *pCur = pBase;
33 while( pCur )
34 {
35 Link *pLast = pCur;
36 pCur = pCur->pNext;
37 delete pLast;
38 }
39*/
40 empty();
41}
42
43void *LinkedList::getAt( int index )
44{
45 if( index < 0 || index >= nSize )
46 return NULL;
47
48 return getPtrTo( index )->pData;
49}
50
51void LinkedList::append( void *data )
52{
53 if( pBase == NULL )
54 {
55 pBase = new Link( data );
56 pTop = pBase;
57 nSize++;
58 }
59 else
60 {
61 pTop->pNext = new Link( data );
62 pTop = pTop->pNext;
63 nSize++;
64 }
65}
66
67void LinkedList::insertBefore( void *data, int pos )
68{
69 if( pos < 0 || pos > nSize )
70 return;
71
72 if( pos == 0 )
73 {
74 Link *pTmp = new Link( data, pBase );
75 if( pBase == NULL )
76 {
77 pTop = pTmp;
78 }
79 pBase = pTmp;
80 if( nLast >= 0 ) nLast++;
81 nSize++;
82 }
83 else
84 {
85 Link *pCur;
86 if( (pCur = getPtrTo( pos-1 )) == NULL )
87 {
88 return;
89 }
90 Link *pNew = new Link( data, pCur->pNext );
91 pCur->pNext = pNew;
92 if( pNew->pNext == NULL )
93 {
94 pTop = pNew;
95 }
96 if( nLast >= pos ) nLast++;
97 nSize++;
98 }
99}
100
101int LinkedList::getSize( )
102{
103 return nSize;
104}
105
106bool LinkedList::isEmpty( )
107{
108 if( nSize == 0 )
109 return true;
110 return false;
111}
112
113void LinkedList::deleteAt( int index )
114{
115 if( index >= nSize ||
116 pBase == NULL )
117 return;
118
119 if( index == 0 )
120 {
121 Link *pTmp = pBase->pNext;
122 delete pBase;
123 pBase = pTmp;
124 if( nLast >= 0 ) nLast--;
125 nSize--;
126 if( pBase == NULL )
127 {
128 pTop = NULL;
129 }
130 else if( pBase->pNext == NULL )
131 {
132 pTop = pBase;
133 }
134 }
135 else
136 {
137 Link *pCur = getPtrTo( index-1 );
138 if( pCur->pNext == pTop )
139 {
140 pTop = pCur;
141 }
142 Link *pTmp;
143 if( pCur->pNext == NULL )
144 {
145 pTmp = NULL;
146 }
147 else
148 {
149 pTmp = pCur->pNext->pNext;
150 }
151 delete pCur->pNext;
152 pCur->pNext = pTmp;
153 if( nLast == index ) nLast = -1;
154 else if( index < nLast ) nLast--;
155 nSize--;
156 }
157}
158
159void LinkedList::empty()
160{
161 while( nSize > 0 )
162 {
163 deleteAt( 0 );
164 }
165}
166
167void LinkedList::setSize( int newSize )
168{
169 if( newSize < nSize )
170 {
171 // Delete items off of the end of the list.
172 while( nSize > newSize )
173 {
174 deleteAt( nSize-1 );
175 }
176 }
177 else
178 {
179 // Add null items to the end of the list.
180 while( nSize < newSize )
181 {
182 append( NULL );
183 }
184 }
185}
186
187void LinkedList::setAt( int index, void *data )
188{
189 if( index >= nSize || index < 0 )
190 return;
191
192 getPtrTo( index )->pData = data;
193}
194
195LinkedList::Link *LinkedList::getPtrTo( int index )
196{
197 if( index < 0 || index >= nSize )
198 return NULL;
199 if( index == nLast )
200 {
201 return pLast;
202 }
203 if( index == 0 )
204 {
205 pLast = pBase;
206 nLast = 0;
207 return pBase;
208 }
209 else
210 {
211 Link *pCur = pBase;
212 int nCur = 0;
213 if( nLast < index && nLast >= 0 )
214 {
215 pCur = pLast;
216 nCur = nLast;
217 }
218 while( nCur != index )
219 {
220 pCur = pCur->pNext;
221 nCur++;
222 }
223 nLast = index;
224 pLast = pCur;
225 return pCur;
226 }
227}
diff --git a/src/linkedlist.h b/src/linkedlist.h
new file mode 100644
index 0000000..c45cc9b
--- /dev/null
+++ b/src/linkedlist.h
@@ -0,0 +1,87 @@
1/**@file
2 * Describes the LinkedList implementation of the List ADT.
3 *@author Mike Buland
4 */
5
6#ifndef LINKEDLIST_H
7#define LINKEDLIST_H
8
9#include <stdio.h>
10#include "list.h"
11
12/** A linked-item implementation of the List ADT. Since the data is linked
13 * sequentially this is a great choice for lists that will grow and shrink
14 * a lot, but don't require as much random access. This implementation
15 * includes optomizations that make iterating through data, and appending
16 * items to the list take O(1) time.
17 *@author Mike Buland
18 */
19class LinkedList : public List
20{
21public:
22 /**
23 * Construct a blank LinkedList.
24 */
25 LinkedList();
26
27 /**
28 * Delete all list data, but do not delete any of the contained elements.
29 */
30 ~LinkedList();
31
32 void *getAt( int nIndex );
33 void append( void *pData );
34 void insertBefore( void *pData, int nPos = 0 );
35 int getSize( );
36 bool isEmpty( );
37 void deleteAt( int nIndex );
38 void empty();
39 void setSize( int nNewSize );
40 void setAt( int nIndex, void *pData );
41
42private:
43 /**
44 * A link in the linked list.
45 */
46 class Link
47 {
48 public:
49 /**
50 * Construct an empty link.
51 */
52 Link()
53 {
54 pData = NULL;
55 pNext = NULL;
56 }
57 /**
58 * Construct a link filled in with useful data.
59 *@param newData The data this link should hold.
60 *@param newNext The next link that this link should point to.
61 */
62 Link( void *newData = NULL, Link * newNext = NULL )
63 {
64 pData = newData;
65 pNext = newNext;
66 }
67 void *pData; /**< A pointer to the contained data. */
68 Link *pNext; /**< A pointer to the next link in the chain */
69 };
70
71 /**
72 * Finds a pointer to the link at index index. This is the core function
73 * called for all seek operations, and has been optimized as heavily as
74 * possible.
75 *@param index The zero-based index of the desired element.
76 *@returns A pointer to the requested Link, or NULL if it isn't found.
77 */
78 Link *getPtrTo( int index );
79 Link *pBase; /**< The first link in the list. */
80 Link *pTop; /**< The Last link in the list. */
81 Link *pLast; /**< The previously requested link. */
82 int nSize; /**< The number of contained links. */
83 int nLast; /**< The index of the previously requested link. */
84};
85
86#endif
87
diff --git a/src/linkmessage.cpp b/src/linkmessage.cpp
new file mode 100644
index 0000000..ce838f5
--- /dev/null
+++ b/src/linkmessage.cpp
@@ -0,0 +1,53 @@
1/***************************************************************************
2 * Copyright (C) 2003 by Mike Buland *
3 * eichlan@yf-soft.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 ***************************************************************************/
10#include "linkmessage.h"
11#include <string.h>
12
13LinkMessage::LinkMessage( int nNewMsg )
14{
15 nMsg = nNewMsg;
16}
17
18LinkMessage::~LinkMessage()
19{
20}
21
22/*
23void LinkMessage::setBroadcast( bool bOn )
24{
25 bBroadcast = bOn;
26}
27
28bool LinkMessage::isBroadcast()
29{
30 return bBroadcast;
31}
32
33
34void LinkMessage::setFromID( int id )
35{
36 nFromLinkID = id;
37}
38
39int LinkMessage::getFromID()
40{
41 return nFromLinkID;
42}
43
44void LinkMessage::setToID( int id )
45{
46 nTargetLinkID = id;
47}
48
49int LinkMessage::getToID()
50{
51 return nTargetLinkID;
52}
53*/
diff --git a/src/linkmessage.h b/src/linkmessage.h
new file mode 100644
index 0000000..6cdfb2f
--- /dev/null
+++ b/src/linkmessage.h
@@ -0,0 +1,39 @@
1/**\file linkmessage.h
2 */
3
4#ifndef LINKMESSAGE_H
5#define LINKMESSAGE_H
6
7/**
8 * A message to be broadcast accross ProgramLinks in a ProgramChain. Generally
9 * one would make a subclass of this in order to transmit more useful
10 * information, but sometimes it isn't necesarry.
11 *@author Mike Buland
12 */
13class LinkMessage
14{
15public:
16 /**
17 * Construct a blank LinkMessage.
18 */
19 LinkMessage() {};
20
21 /**
22 * Deconstruct a LinkMessage.
23 */
24 virtual ~LinkMessage();
25
26 /**
27 * Create a LinkMessage object with a specific message assosiated with it
28 * to start with.
29 *@param nNewMsg The message to use in the Message object.
30 */
31 LinkMessage( int nNewMsg );
32
33 /**
34 * The message contained in the Message object.
35 */
36 int nMsg;
37};
38
39#endif
diff --git a/src/list.cpp b/src/list.cpp
new file mode 100644
index 0000000..c8b88c1
--- /dev/null
+++ b/src/list.cpp
@@ -0,0 +1,27 @@
1/***************************************************************************
2 list.cpp - description
3 -------------------
4 begin : Sun Oct 19 2003
5 copyright : (C) 2003 by Mike Buland
6 email : eichlan@yf-soft.com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "list.h"
19
20List::List( )
21{
22}
23
24List::~List( )
25{
26}
27
diff --git a/src/list.h b/src/list.h
new file mode 100644
index 0000000..c71b328
--- /dev/null
+++ b/src/list.h
@@ -0,0 +1,101 @@
1#ifndef LIST_H
2#define LIST_H
3
4
5/** The basic List class ADT. This, on it's own, does absolutely nothing, but
6 * does define all standard interface functions to access a list.
7 *@author Mike Buland
8 */
9class List
10{
11public:
12 /**
13 * Construct a list.
14 */
15 List();
16
17 /**
18 * Desconstruct a list.
19 */
20 virtual ~List();
21
22 /** Gets the value at a specified index.
23 *@param nIndex The index of the item to return.
24 *@returns The specified item, or NULL if the index was beyond the range
25 * of the list.
26 *@author Mike Buland
27 */
28 virtual void *getAt( int nIndex ) = 0;
29
30 /** Append the given data to the end of the list. This increases the
31 * size of the list by one.
32 *@param pData The data to append to the list.
33 *@author Mike Buland
34 */
35 virtual void append( void *pData ) = 0;
36
37 /** Inserts an item at the specified position in the list. The
38 * new item takes the index that you specify, and all other items
39 * are moved up one position. The size of the list is increased by
40 * one.
41 *@param pData The value to insert into the list.
42 *@param nPos Where to insert the data into the list.
43 *@author Mike Buland
44 */
45 virtual void insertBefore( void *pData, int nPos = 0 ) = 0;
46
47 /** Determines the size of the list, in elements.
48 *@returns The size of the list.
49 *@author Mike Buland
50 */
51 virtual int getSize( ) = 0;
52
53 /** Determines if the list is empty or not.
54 *@returns True if the list is empty, or false if the list has
55 * data in it (if the size is greater than zero).
56 *@author Mike Buland
57 */
58 virtual bool isEmpty( ) = 0;
59
60 /** Deletes an item at the specified index and moves all other
61 * values down one index. The size of the list is decreased by one.
62 *@param nIndex The index of the item to delete.
63 *@author Mike Buland
64 */
65 virtual void deleteAt( int nIndex ) = 0;
66
67 /** Completely empties the list, and sets the effective size to
68 * zero.
69 *@author Mike Buland
70 */
71 virtual void empty() = 0;
72
73 /** Sets the size of the list. This can be larger or smaller
74 * than what it was previously. If larger, new blank items will
75 * be added to the end of the list. If smaller than the old list
76 * items will be deleted from the end.
77 *@param nNewSize The new size of the list.
78 *@author Mike Buland
79 */
80 virtual void setSize( int nNewSize ) = 0;
81
82 /** Sets a member at a specified location to a new value.
83 * If the member being set is outside of the range of the
84 * current list it should be expanded.
85 *@param nIndex The zero-based index of the item to change.
86 *@param pData The new value for that index.
87 *@author Mike Buland
88 */
89 virtual void setAt( int nIndex, void *pData ) = 0;
90
91 /** Makes the List work like an array. Just say listObj[2] to get
92 * the third element.
93 *@param nIndex The index to access in the list.
94 *@returns A pointer to the data at element index.
95 *@author Mike Buland
96 */
97 void *operator[]( int nIndex ) { return getAt( nIndex ); };
98};
99
100#endif
101
diff --git a/src/md5.cpp b/src/md5.cpp
new file mode 100644
index 0000000..ed7e4ac
--- /dev/null
+++ b/src/md5.cpp
@@ -0,0 +1,190 @@
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include "md5.h"
5
6// This is a fun macro that tells us where the length char goes after the data
7// section in the padded data segment. It's short for OBfuscation LOCaction.
8#define OBLOC(len) ((((len + 64) >> 9) << 4) + 14)
9// This performs a wrapping bitwise shift, kinda' fun!
10
11#define bit_roll( num, cnt ) \
12 (((num) << (cnt)) | (((num) >> (32 - (cnt))) & ~(-1<<(cnt))))
13
14//#define md5_cmn( q, a, b, x, s, t ) (bit_roll((a + q + x + t), s) + b)
15
16// The following are handy wrappers for the cmn function
17#define md5_ff( a, b, c, d, x, s, t ) \
18 (md5_cmn((b & c) | ((~b) & d), a, b, x, s, t))
19
20#define md5_gg( a, b, c, d, x, s, t ) \
21 (md5_cmn((b & d) | (c & (~d)), a, b, x, s, t))
22
23#define md5_hh( a, b, c, d, x, s, t ) \
24 (md5_cmn(b ^ c ^ d, a, b, x, s, t))
25
26#define md5_ii( a, b, c, d, x, s, t ) \
27 (md5_cmn(c ^ (b | (~d)), a, b, x, s, t))
28
29inline long md5_cmn( long q, long a, long b, long x, long s, long t )
30{
31 return bit_roll((a + q + x + t), s) + b;
32}
33
34md5::md5()
35{
36}
37
38md5::~md5()
39{
40}
41
42/*
43 * Calculate the MD5 of an array of little-endian words, and a bit length
44 */
45void md5::core_md5( long *x, long len, md5sum *output )
46{
47 long a = 1732584193, olda;
48 long b = -271733879, oldb;
49 long c = -1732584194, oldc;
50 long d = 271733878, oldd;
51
52 for( long i = 0; i < len; i += 16 )
53 {
54 olda = a;
55 oldb = b;
56 oldc = c;
57 oldd = d;
58
59 a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
60 d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
61 c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
62 b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
63 a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
64 d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
65 c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
66 b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
67 a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
68 d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
69 c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
70 b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
71 a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
72 d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
73 c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
74 b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
75
76 a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
77 d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
78 c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
79 b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
80 a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
81 d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
82 c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
83 b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
84 a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
85 d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
86 c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
87 b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
88 a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
89 d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
90 c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
91 b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
92
93 a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
94 d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
95 c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
96 b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
97 a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
98 d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
99 c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
100 b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
101 a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
102 d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
103 c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
104 b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
105 a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
106 d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
107 c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
108 b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
109
110 a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
111 d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
112 c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
113 b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
114 a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
115 d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
116 c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
117 b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
118 a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
119 d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
120 c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
121 b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
122 a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
123 d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
124 c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
125 b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
126
127 a = a + olda;
128 b = b + oldb;
129 c = c + oldc;
130 d = d + oldd;
131 }
132
133 output->data[0] = a;
134 output->data[1] = b;
135 output->data[2] = c;
136 output->data[3] = d;
137 delete[] x;
138}
139
140long *md5::c2l( const char *str, long len, long *nNewLen )
141{
142 long len8 = len*8;
143 long mlen = OBLOC( len8 );
144 long flen = (((mlen/16)+((mlen%16)?(1):(0))))*16;
145 long *aBin = new long[flen];
146 memset( aBin, 0, flen );
147
148 for( long i = 0; i < len8; i+=8 )
149 {
150 aBin[i>>5] |= ((long)str[i/8]) << (i%32);
151 }
152
153 aBin[len8 >> 5] |= 0x80 << ((len8) % 32);
154 aBin[OBLOC( len8 )] = len8;
155
156 (*nNewLen) = flen;
157
158 return aBin;
159}
160
161void md5::l2hexstr( long *binarray, char *str )
162{
163 static const char hex_tab[] = {"0123456789abcdef"};
164 //static char str[33];
165
166 int k = 0;
167 for( int i = 0; i < 16; i++)
168 {
169 str[k++] = hex_tab[(binarray[i>>2] >> ((i%4)*8+4)) & 0xF];
170 str[k++] = hex_tab[(binarray[i>>2] >> ((i%4)*8 )) & 0xF];
171 }
172}
173
174void md5::sumString( md5sum *pSum, const char *sStr )
175{
176 sumData( pSum, sStr, strlen( sStr ) );
177}
178
179void md5::sumData( md5sum *pSum, const char *aData, long nLen )
180{
181 long nNewLen;
182 long *aOb = c2l( aData, nLen, &nNewLen );
183 core_md5( aOb, nNewLen, pSum );
184}
185
186void md5::sumToHex( md5sum *pSum, char *sHex )
187{
188 l2hexstr( pSum->data, sHex );
189}
190
diff --git a/src/md5.h b/src/md5.h
new file mode 100644
index 0000000..810345e
--- /dev/null
+++ b/src/md5.h
@@ -0,0 +1,81 @@
1#ifndef MD5_H
2#define MD5_H
3
4/**
5 * Used to store an MD5 sum in a handy container.
6 */
7typedef struct
8{
9 /** The actual data-storage for an MD5 sum. */
10 long data[4];
11} md5sum;
12
13/**
14 * Class for easily calculating MD5 sums of just about any data.
15 *@author Mike Buland
16 */
17class md5
18{
19public:
20 /** Build an MD5 sum builder. */
21 md5();
22
23 /** Deconstruct */
24 ~md5();
25
26 /**
27 * Create a sum of a standard c string, null terminated. This is probably
28 * the easiest function to use.
29 *@param pSum The MD5 sum structure to fill up.
30 *@param sStr The null-terminated string to turn into an MD5 sum.
31 */
32 void sumString( md5sum *pSum, const char *sStr );
33
34 /**
35 * Create a sum of an array of arbitrary data. This is the most handy for
36 * dealing with files and so on.
37 *@param pSum The MD5 sum structure to fill up.
38 *@param aData A pointer to the base of the data to sum.
39 *@param nLen The number of bytes to use in the sum.
40 */
41 void sumData( md5sum *pSum, const char *aData, long nLen );
42
43 /**
44 * Convert an md5sum to standard hex representation. Make sure that sHex
45 * contains at least 17 characters of space.
46 *@param pSum The sum structure to convert to hex.
47 *@param sHex The string to store the hex value in.
48 */
49 void sumToHex( md5sum *pSum, char *sHex );
50
51private:
52 /**
53 * Do the bulk of the work of the md5 algorithm.
54 *@param x I'm not sure. I'll need to look it up.
55 *@param len The length of the data.
56 *@param output The sum structure to put the output in.
57 */
58 void core_md5( long *x, long len, md5sum *output );
59
60 /**
61 * Convert an array of charaters to an array of longs in a very crafty way.
62 * This also applies standard MD5 obfuscation to the resulting array, and
63 * makes it fit within MD5 size constraints.
64 *@param str The data to convert.
65 *@param len The length of the data.
66 *@param nNewLen A pointer to a variable that will hold the new length of
67 * the resulting array of longs.
68 *@returns The newly obfuscated and resized long array.
69 */
70 long *c2l( const char *str, long len, long *nNewLen );
71
72 /**
73 * Backend helper to convert an array of longs into a hex string.
74 *@param binarray The binary data to convert.
75 *@param str The string to store the hex string in.
76 */
77 void l2hexstr( long *binarray, char *str );
78
79};
80
81#endif
diff --git a/src/multilog.cpp b/src/multilog.cpp
new file mode 100644
index 0000000..64ff967
--- /dev/null
+++ b/src/multilog.cpp
@@ -0,0 +1,143 @@
1/***************************************************************************
2 multilog.cpp - description
3 -------------------
4 begin : Sat Sep 6 2003
5 copyright : (C) 2003 by Mike Buland
6 email : eichlan@yf-soft.com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "multilog.h"
19#include <stdio.h>
20#include <time.h>
21#include <string.h>
22#include <stdlib.h>
23
24#include "multilogchannel.h"
25
26// This section is what we need to make this a singleton
27// this makes this class easy to use from anywhere, without
28// worrying about re-creating every output form and all of that crazy jazz
29MultiLog *MultiLog::singleLog = NULL;
30
31MultiLog *MultiLog::getLog()
32{
33 if( singleLog == NULL )
34 {
35 singleLog = new MultiLog;
36 atexit( cleanup );
37 }
38 return singleLog;
39}
40
41void MultiLog::cleanup()
42{
43 if( singleLog != NULL )
44 {
45 delete singleLog;
46 singleLog = NULL;
47 }
48}
49
50MultiLog::MultiLog()
51{
52 lChannel = new LinkedList();
53 rEntry = new RingList( 150 );
54 nEntriesLost = 0;
55}
56
57MultiLog::~MultiLog()
58{
59 int nMax = lChannel->getSize();
60 for( int j = 0; j < nMax; j++ )
61 {
62 ((MultiLogChannel *)lChannel->getAt(j))->closeLog();
63 delete ((MultiLogChannel *)lChannel->getAt(j));
64 }
65 delete lChannel;
66
67 for( int j = 0; j < rEntry->getSize(); j++ )
68 {
69 delete (LogEntry *)rEntry->getAt( j );
70 }
71 delete rEntry;
72}
73/*
74void MultiLog::Log( int nLevel, const char *lpFormat, ...)
75{
76 switch( nLevel )
77 {
78 default:
79 break;
80 }
81 va_list ap;
82 va_start(ap, lpFormat);
83
84 vprintf( lpFormat, ap );
85
86 va_end(ap);
87}*/
88
89void MultiLog::DetailLog( int nLevel, const char *lpFile, int nLine, const char *lpFunction, const char *lpFormat, ...)
90{
91 LogEntry *e = new LogEntry();
92
93 va_list ap;
94 va_start(ap, lpFormat);
95 char *text;
96 vasprintf( &text, lpFormat, ap );
97 va_end(ap);
98
99 time( &e->xTime );
100 e->nLevel = nLevel;
101 e->nLine = nLine;
102 e->lpFile = new char[strlen(lpFile)+1];
103 strcpy( e->lpFile, lpFile );
104 e->lpText = new char[strlen(text)+1];
105 strcpy( e->lpText, text );
106 free( text );
107
108 append( e );
109}
110
111void MultiLog::append( LogEntry *pEntry )
112{
113 rEntry->append( pEntry );
114 if( rEntry->getPushBuf() )
115 {
116 delete (LogEntry *)rEntry->getPushBuf();
117 nEntriesLost++;
118 }
119
120 for( int j = 0; j < lChannel->getSize(); j++ )
121 {
122 ((MultiLogChannel *)lChannel->getAt( j ))->append( pEntry );
123 }
124}
125
126void MultiLog::addChannel( MultiLogChannel *pChannel )
127{
128 lChannel->append( pChannel );
129
130 pChannel->openLog();
131
132 for( int j = 0; j < rEntry->getSize(); j++ )
133 {
134 pChannel->append( (LogEntry *)rEntry->getAt( j ) );
135 }
136}
137
138MultiLog::LogEntry::~LogEntry()
139{
140 delete[] lpFile;
141 delete[] lpText;
142}
143
diff --git a/src/multilog.h b/src/multilog.h
new file mode 100644
index 0000000..30ad8d7
--- /dev/null
+++ b/src/multilog.h
@@ -0,0 +1,145 @@
1#ifndef MULTILOG_H
2#define MULTILOG_H
3
4#include <stdio.h>
5#include <stdarg.h>
6#include <time.h>
7
8#include "ringlist.h"
9#include "linkedlist.h"
10
11/**
12 * Calls the DetailLog function but includes pre-processor macros to fill in
13 * most of the fields for you. This makes your life a lot easier, and makes the
14 * log useful for system debugging as well as just letting people know what's
15 * going on.
16 *@param LEVEL The log level, comes from an enum in the MultiLog class.
17 *@param FORMAT The text to store in the log, using printf style formatting.
18 *@param ... Parameters to help format the text in the FROMAT param.
19 */
20#define LineLog( LEVEL, FORMAT, ...) DetailLog( LEVEL, __FILE__, __LINE__, __PRETTY_FUNCTION__, FORMAT, ##__VA_ARGS__ )
21
22/** MultiLog keeps track of logfile info in a myriad of varieties, and is
23 * easily configurable between them all. It allows output to the standard
24 * output, error output, files, networks, and streams, which includes memory
25 * buffers.
26 * MultiLog uses the singleton pattern to keep only a single instance of
27 * the log. Instead of instantiating a new copy, call the getLog method.
28 *@author Mike Buland
29 */
30class MultiLog
31{
32public:
33 /**
34 * Keeps track of a single log entry, in a standard format, that can be
35 * processed by any MultiLogChannel derrived class.
36 *@author Mike Buland
37 */
38 typedef struct LogEntry
39 {
40 /** Safely delete a log entry. */
41 ~LogEntry();
42 time_t xTime; /**< The time the log entry was made. */
43 int nLevel; /**< The log-level of the entry. */
44 char *lpFile; /**< The name of the file this entry came from. */
45 int nLine; /**< The line number that this log came from. */
46 char *lpText; /**< The text content of this log entry. */
47 } LogEntry;
48
49private:
50 /**
51 * Private constructor, this ensures that this is a singleton.
52 */
53 MultiLog();
54
55 /**
56 * The only instance of MultiLog ever.
57 */
58 static MultiLog *singleLog;
59
60 /**
61 * Append a new logentry to the log list, possibly pushing an old entry off.
62 *@param pEntry The new entry to append.
63 */
64 void append( LogEntry *pEntry );
65
66 /**
67 * The actual log entry storage mechanism.
68 */
69 RingList *rEntry;
70
71 /**
72 * The number of entries that have rolled off the end of the RingList.
73 */
74 unsigned long nEntriesLost;
75
76 /**
77 * A list of all channels that are registered with the MultiLog.
78 */
79 LinkedList *lChannel;
80
81public:
82 /**
83 * Destroy the multilog.
84 *@todo Why is this public? Does it need to be?
85 */
86 ~MultiLog();
87
88 /** Sends info to the logfile.
89 *@param nLevel The type of data being logged (error, info, etc.)
90 *@param lpFormat The data to send to the log.
91 *@author Mike Buland
92 */
93 //void Log( int nLevel, const char *lpFormat, ...);
94
95 /** Sends info to the logfile with extra information, including the files
96 * that it was called from and the line in the code. Besides that, it's
97 * exactly the same as Log. Please use the LineLog macro to make DetailLog
98 * really easy to use. It operates exacly like Log, but inserts the
99 * builtin macros as the lpFile and nLine parameters.
100 *@param nLevel The type of data being logged (error, info, etc.)
101 *@param lpFile The name of the file that called the log function.
102 *@param nLine The line in the file that this log function was called from.
103 *@param lpFunction The name of the function that called the log function.
104 *@param lpFormat The data to send to the log.
105 *@author Mike Buland
106 */
107 void DetailLog( int nLevel, const char *lpFile, int nLine, const char *lpFunction, const char *lpFormat, ...);
108
109 /** Gets a pointer to the only instantion of the MultiLog that can exist.
110 * If there is no instantion in existance, it creates one, so it's
111 * foolproof.
112 *@returns A pointer to the only MultiLog instantion.
113 *@author Mike Buland
114 */
115 static MultiLog *getLog();
116
117 /** Performs standard cleanup and deletes the only instantiation of MultiLog
118 * that can exist. This is just the same as delete and will nicely close
119 * all open logs. always call this when you are done with your MultiLog.
120 */
121 static void cleanup();
122
123 /**
124 * Adds a logging channel to the MultiLog channel chain. Every added
125 * channel will automatically receive a complete log of everything that
126 * happened before the channel was added as well as all future messages.
127 *@param pChannel A pointer to the pre-contructed channel object to add.
128 */
129 void addChannel( class MultiLogChannel *pChannel );
130
131 /** The various pre-defined levels available to use when logging.
132 * The person logging can make up their own, just make sure to remember
133 * which value is which (all levels are integers).
134 *@author Mike Buland
135 */
136 enum Levels
137 {
138 LError,
139 LWarning,
140 LInfo,
141 LStatus
142 };
143};
144
145#endif
diff --git a/src/multilogchannel.cpp b/src/multilogchannel.cpp
new file mode 100644
index 0000000..ee4c9bf
--- /dev/null
+++ b/src/multilogchannel.cpp
@@ -0,0 +1,13 @@
1//
2// C++ Implementation: multilogchannel
3//
4// Description:
5//
6//
7// Author: Mike Buland <eichlan@yf-soft.com>, (C) 2005
8//
9// Copyright: See COPYING file that comes with this distribution
10//
11//
12#include "multilogchannel.h"
13
diff --git a/src/multilogchannel.h b/src/multilogchannel.h
new file mode 100644
index 0000000..d891a65
--- /dev/null
+++ b/src/multilogchannel.h
@@ -0,0 +1,46 @@
1#ifndef MULTILOGCHANNEL_H
2#define MULTILOGCHANNEL_H
3
4#include "multilog.h"
5
6/**
7 * The baseclass for any MultiLog output channel. Any class that implements
8 * all of these functions can be put in the log chain and will be sent
9 * messages from active MultiLoggers.
10 *@author Mike Buland
11 */
12class MultiLogChannel
13{
14public:
15 /**
16 * Deconstruct a MultiLogChannel.
17 */
18 virtual ~MultiLogChannel() {};
19
20 /**
21 * Should perform any operations that need to take place in order to start
22 * the output of data into this channel. This will be called once by the
23 * MultiLog when the MultiLogChannel is registered.
24 *@returns True means that everything can go as planned. False means that
25 * the MultiLog should remove this channel from the list and delete it.
26 */
27 virtual bool openLog() = 0;
28
29 /**
30 * Should append a log entry to the long, by whatever means are necesarry.
31 *@param pEntry The LogEntry to append.
32 *@returns True means that everything can go as planned. False means that
33 * the MultiLog should remove this channel from the list and delete it.
34 */
35 virtual bool append( MultiLog::LogEntry *pEntry ) = 0;
36
37 /**
38 * Should perform any operations that need to take place in order to safely
39 * close and cleanup the log.
40 *@returns True means that everything can go as planned. False means that
41 * the MultiLog should remove this channel from the list and delete it.
42 */
43 virtual bool closeLog() = 0;
44};
45
46#endif
diff --git a/src/multilogtext.cpp b/src/multilogtext.cpp
new file mode 100644
index 0000000..be64595
--- /dev/null
+++ b/src/multilogtext.cpp
@@ -0,0 +1,152 @@
1
2#include <stdio.h>
3#include <stdlib.h>
4#include <fcntl.h>
5#include <unistd.h>
6#include <time.h>
7#include <string.h>
8#include "multilogtext.h"
9
10MultiLogText::MultiLogText( const char *sFileName, const char *lpFormat )
11{
12 this->lpFormat = NULL;
13 nFD = open( sFileName, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH );
14 setLogFormat( lpFormat );
15}
16
17MultiLogText::MultiLogText( int nFileDesc, const char *lpFormat )
18{
19 this->lpFormat = NULL;
20 nFD = nFileDesc;
21 setLogFormat( lpFormat );
22}
23
24MultiLogText::~MultiLogText()
25{
26 if( nFD != -1 )
27 {
28 close( nFD );
29 }
30
31 delete[] lpFormat;
32}
33
34bool MultiLogText::setLogFormat( const char *lpFormat )
35{
36 char buf[200];
37 int k = 0;
38 static char fmts[10][4]={
39 {'y', 'd', '0', '1'},
40 {'m', 'd', '0', '2'},
41 {'d', 'd', '0', '3'},
42 {'h', 'd', '0', '4'},
43 {'M', 'd', '0', '5'},
44 {'s', 'd', '0', '6'},
45 {'l', 'd', '0', '7'},
46 {'f', 's', '0', '8'},
47 {'L', 'd', '0', '9'},
48 {'t', 's', '1', '0'},
49 };
50
51 for( int j = 0; lpFormat[j] != '\0'; j++ )
52 {
53 if( lpFormat[j] == '%' )
54 {
55 buf[k++] = '%';
56 int nPlace = k++;
57 k++;
58 buf[k++] = '$';
59 bool bDone = false;
60 for( j++; bDone == false; j++ )
61 {
62 int l;
63 for( l = 0; l < 10; l++ )
64 {
65 if( lpFormat[j] == fmts[l][0] )
66 {
67 buf[nPlace] = fmts[l][2];
68 buf[nPlace+1] = fmts[l][3];
69 buf[k++] = fmts[l][1];
70 bDone = true;
71 break;
72 }
73 }
74 if( l == 10 )
75 {
76 buf[k++] = lpFormat[j];
77 }
78 }
79 j--;
80 }
81 else
82 {
83 buf[k++] = lpFormat[j];
84 }
85 }
86 buf[k++] = '\n';
87 buf[k] = '\0';
88
89 if( this->lpFormat != NULL )
90 {
91 delete[] this->lpFormat;
92 }
93 this->lpFormat = new char[k+1];
94 strcpy( this->lpFormat, buf );
95
96 return true;
97}
98
99bool MultiLogText::openLog()
100{
101 if( nFD == -1 )
102 {
103 return false;
104 }
105 return true;
106}
107
108bool MultiLogText::append( MultiLog::LogEntry *pEntry )
109{
110 if( nFD == -1 )
111 {
112 return false;
113 }
114
115 char *line = NULL;
116 struct tm *pTime;
117 pTime = localtime( &pEntry->xTime );
118 asprintf(
119 &line,
120 lpFormat,
121 pTime->tm_year+1900,
122 pTime->tm_mon+1,
123 pTime->tm_mday,
124 pTime->tm_hour,
125 pTime->tm_min,
126 pTime->tm_sec,
127 pEntry->nLevel,
128 pEntry->lpFile,
129 pEntry->nLine,
130 pEntry->lpText
131 );
132 write( nFD, line, strlen(line) );
133 free( line );
134
135 return true;
136}
137
138bool MultiLogText::closeLog()
139{
140 if( nFD == -1 )
141 {
142 return false;
143 }
144 // Don't close it if it's sdtout or errorout
145 if( nFD > 2 )
146 {
147 close( nFD );
148 }
149 nFD = -1;
150 return true;
151}
152
diff --git a/src/multilogtext.h b/src/multilogtext.h
new file mode 100644
index 0000000..aa32405
--- /dev/null
+++ b/src/multilogtext.h
@@ -0,0 +1,70 @@
1#ifndef MULTILOGTEXT_H
2#define MULTILOGTEXT_H
3
4#include "multilogchannel.h"
5
6/**
7 * Simple MultiLogChannel that takes the logdata, formats it textually, and
8 * writes it to a text device, either a file or the screen, yay! This takes
9 * the place of the old standard logging facility.
10 * The entries in the format follow the standard printf % style, and are as
11 * follows:
12 * <ul>
13 * <li>%y - current year</li>
14 * <li>%m - current month</li>
15 * <li>%d - current day</li>
16 * <li>%h - current hour (24-hour format)</li>
17 * <li>%M - current minute</li>
18 * <li>%s - current seccond</li>
19 * <li>%l - Loglevel (numerical)</li>
20 * <li>%f - Filename</li>
21 * <li>%L - Line number</li>
22 * <li>%t - Full text of the log entry</li>
23 * </ul>
24 *@author Mike Buland
25 */
26class MultiLogText : public MultiLogChannel
27{
28public:
29 /**
30 * Construct a MultiLogText object around a specific filename and format.
31 * The file named by sFileName will be opened for writting in text+append
32 * mode. No existing data will be destroyed.
33 *@param sFileName The file to output log-data to.
34 *@param lpFormat The format using the above specifications to be used for
35 * every log entry.
36 */
37 MultiLogText( const char *sFileName, const char *lpFormat );
38
39 /**
40 * Construct a MultiLogText object around a specific file and format.
41 * The file descriptor passed in should describe an already opened and set-
42 * up file or device. This could easily be a socket or stdout or stderr.
43 *@param nFileDesc The already opened descriptor to send data to.
44 *@param lpFormat The format using the above specifications to be used for
45 * every log entry.
46 */
47 MultiLogText( int nFileDesc, const char *lpFormat );
48
49 /**
50 * Destroy the object.
51 */
52 ~MultiLogText();
53
54 bool openLog();
55 bool append( MultiLog::LogEntry *pEntry );
56 bool closeLog();
57
58 /**
59 * Change the log format on the fly.
60 *@param lpFormat The new format to use for all future log entries.
61 *@returns True if everything was fine, false for catastrophic failure.
62 */
63 bool setLogFormat( const char *lpFormat );
64
65private:
66 int nFD; /**< The file descriptor we're writing to. */
67 char *lpFormat; /**< The format that we're using, converted for printf. */
68};
69
70#endif
diff --git a/src/pproc.cpp b/src/pproc.cpp
new file mode 100644
index 0000000..f5cb869
--- /dev/null
+++ b/src/pproc.cpp
@@ -0,0 +1,60 @@
1#include <stdio.h>
2#include <string.h>
3#include "pproc.h"
4
5void processParams( int argc, char *argv[], PPROC *pproc )
6{
7 // Loop over all the params except the first, no params, no looping!
8 for( int j = 1; j < argc; j++ )
9 {
10 //printf("Param[%d]: \"%s\"\n", j, argv[j] );
11 if( argv[j][0] == '-' )
12 {
13 if( argv[j][1] == '-' )
14 {
15 // Proccess a long-word param string
16 for( int k = 0;
17 pproc[k].proc != NULL || pproc[k].stateVar != NULL;
18 k++ )
19 {
20 if( !strcmp( pproc[k].lpWord, &argv[j][2] ) )
21 {
22 if( pproc[k].proc != NULL )
23 {
24 j += pproc[k].proc( argc-j, &argv[j] );
25 }
26 if( pproc[k].stateVar != NULL )
27 {
28 (*(pproc[k].stateVar)) = pproc[k].bSetState;
29 }
30 }
31 }
32 }
33 else
34 {
35 // Process a one char param string
36 for( int k = 0;
37 pproc[k].proc != NULL || pproc[k].stateVar != NULL;
38 k++ )
39 {
40 if( pproc[k].cChar == argv[j][1] )
41 {
42 if( pproc[k].proc != NULL )
43 {
44 j += pproc[k].proc( argc-j, &argv[j] );
45 }
46 if( pproc[k].stateVar != NULL )
47 {
48 (*(pproc[k].stateVar)) = pproc[k].bSetState;
49 }
50 }
51 }
52 }
53 }
54 else
55 {
56 // Handle generic params here.
57 }
58 }
59}
60
diff --git a/src/pproc.h b/src/pproc.h
new file mode 100644
index 0000000..bf5063c
--- /dev/null
+++ b/src/pproc.h
@@ -0,0 +1,35 @@
1#ifndef PPROC_H_
2#define PPROC_H_
3
4/**
5 * Contains all required info to handle a single program parameter.
6 *@author Mike Buland
7 */
8typedef struct PPROC
9{
10 const char *lpWord; /**< The full text-word to use as a param. */
11 const char cChar; /**< The short char version of the param. */
12 /**
13 * Pointer to the function to call when this param is triggered.
14 *@param argc The number of params after and including the one that
15 * triggered this call.
16 *@param argv The array of commandline tokens to use as parameters.
17 *@returns 0 for everything is ok. A number greater than zero signals that
18 * this parameter function used n parameters and they should be skipped by
19 * the processParams function.
20 */
21 int (*proc)( int argc, char *argv[] );
22 bool *stateVar; /**< A pointer to a bool to be setwhen this is triggered */
23 bool bSetState; /**< The state to set the above bool to. */
24} PPROC;
25
26/**
27 * Process command line parameters based on a null-terminated array of PPROC
28 * structures.
29 *@param argc Should come straight from your main function's argc.
30 *@param argv Should come straight from your main function's argv.
31 *@param pproc The array of params that this function can respond to.
32 */
33void processParams( int argc, char *argv[], PPROC *pproc );
34
35#endif /*PPROC_H_*/
diff --git a/src/pqueue.cpp b/src/pqueue.cpp
new file mode 100644
index 0000000..1f0b8b5
--- /dev/null
+++ b/src/pqueue.cpp
@@ -0,0 +1,33 @@
1#include "pqueue.h"
2
3PQueue::PQueue( int nNewNumQueues )
4{
5 nNumQueues = nNewNumQueues;
6 aQueue = new Queue[nNumQueues];
7}
8
9PQueue::~PQueue()
10{
11 delete[] aQueue;
12}
13
14void PQueue::enqueue( void *pData, int nQueueLevel )
15{
16 if( nQueueLevel < 0 || nQueueLevel >= nNumQueues )
17 return;
18
19 aQueue[nQueueLevel].enqueue( pData );
20}
21
22void *PQueue::dequeue()
23{
24 for( int j = 0; j < nNumQueues; j++ )
25 {
26 if( aQueue[j].isEmpty() == false )
27 {
28 return aQueue[j].dequeue();
29 }
30 }
31
32 return NULL;
33}
diff --git a/src/pqueue.h b/src/pqueue.h
new file mode 100644
index 0000000..1b45f75
--- /dev/null
+++ b/src/pqueue.h
@@ -0,0 +1,48 @@
1#ifndef PQUEUE_H
2#define PQUEUE_H
3
4#include "queue.h"
5
6/** Priority queue. This is just like a queue, but something with a higher
7 * priority will always come off the queue before something with a lower
8 * priority, even if it's added after. Otherwise works just like a queue.
9 *@author Mike Buland
10 */
11class PQueue
12{
13public:
14 /** Create a queue with any number of different priorities.
15 *@param nNewNumQueues The number of queues, the default is 3
16 */
17 PQueue( int nNewNumQueues=3 );
18
19 /**
20 * Cleanup all contained queues.
21 */
22 ~PQueue();
23
24 /** Add a new item to the queue at the specified priority. A lower
25 * number means a higher priority!
26 *@param pData A pointer to the data to add to the queue
27 *@param nQueueLevel The priority to set the new data to
28 */
29 void enqueue( void *pData, int nQueueLevel );
30
31 /** Pull the next item off the queue, high priority first.
32 *@returns A pointer to the data that was next in the priority queue
33 */
34 void *dequeue();
35
36private:
37 /**
38 * The queues we use for real data storage.
39 */
40 Queue *aQueue;
41
42 /**
43 * The number of priorities or queus that we need.
44 */
45 int nNumQueues;
46};
47
48#endif
diff --git a/src/programchain.cpp b/src/programchain.cpp
new file mode 100644
index 0000000..4e53ac8
--- /dev/null
+++ b/src/programchain.cpp
@@ -0,0 +1,113 @@
1/***************************************************************************
2 programchain.cpp - description
3 -------------------
4 begin : Sat Sep 6 2003
5 copyright : (C) 2003 by Mike Buland
6 email : eichlan@yf-soft.com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include <stdlib.h>
19#include "programchain.h"
20
21ProgramChain::ProgramChain()
22{
23 pLog = MultiLog::getLog();
24 pLog->LineLog( MultiLog::LStatus, "Program Chain Initialized." );
25}
26
27ProgramChain::~ProgramChain()
28{
29}
30
31bool ProgramChain::addLink( ProgramLink *pLink )
32{
33 if( pLink->init() == false )
34 {
35 emergencyShutdown();
36 return false;
37 }
38
39 lLink.append( pLink );
40
41 pLink->setChain( this );
42
43 return true;
44}
45
46ProgramLink *ProgramChain::getLink( const char *lpName )
47{
48 char a;
49 a = lpName[0];
50 return NULL;
51}
52
53ProgramLink *ProgramChain::getBaseLink()
54{
55 return NULL;
56}
57
58bool ProgramChain::execChainOnce()
59{
60 int nLen = lLink.getSize();
61 for( int j = 0; j < nLen; j++ )
62 {
63 if( ((ProgramLink *)lLink[j])->timeSlice() == false )
64 {
65 pLog->LineLog( MultiLog::LInfo, "Shutting down due to signal from link #%d", j );
66 emergencyShutdown();
67 return false;
68 }
69 }
70
71 return true;
72}
73
74bool ProgramChain::enterChainLoop()
75{
76 for(;;)
77 {
78 if( execChainOnce() == false )
79 {
80 return false;
81 }
82 }
83
84 return true;
85}
86
87void ProgramChain::emergencyShutdown()
88{
89 int nLen = lLink.getSize();
90 for( int j = 0; j < nLen; j++ )
91 {
92 ((ProgramLink *)lLink[j])->deInit();
93 delete (ProgramLink *)lLink[j];
94 }
95 lLink.empty();
96}
97
98LinkMessage *ProgramChain::broadcastIRM( LinkMessage *pMsgOut, ProgramLink *pSender )
99{
100 int nLen = lLink.getSize();
101 for( int j = 0; j < nLen; j++ )
102 {
103 LinkMessage *pMsg = ((ProgramLink *)lLink[j])->processIRM( pMsgOut );
104 if( pMsg != NULL )
105 {
106 delete pMsgOut;
107 return pMsg;
108 }
109 }
110
111 delete pMsgOut;
112 return NULL;
113}
diff --git a/src/programchain.h b/src/programchain.h
new file mode 100644
index 0000000..34f64f8
--- /dev/null
+++ b/src/programchain.h
@@ -0,0 +1,88 @@
1#ifndef PROGRAMCHAIN_H
2#define PROGRAMCHAIN_H
3
4#include "linkedlist.h"
5#include "multilog.h"
6#include "programlink.h"
7
8/** The Program Chain links together program "chunks" to more easily facilitate
9 * a generalized program loop with modular extensions.
10 *@author Mike Buland
11 */
12class ProgramChain
13{
14public:
15 /**
16 * Construct an empty chain.
17 */
18 ProgramChain();
19
20 /**
21 * Destroy your chain.
22 */
23 ~ProgramChain();
24
25 /** Adds a link to the end of the chain.
26 *@param pLink A pointer to the link to add to the chain.
27 *@returns True if adding the link was successful, otherwise false
28 *@author Mike Buland
29 */
30 bool addLink( ProgramLink *pLink );
31
32 /** Gets a link by name.
33 *@param lpName The name of the link you're looking for. Every link has a
34 * name, apparently.
35 *@returns A pointer to the specified ProgramLink, or NULL if none were found
36 * matching your criteria.
37 *@author Mike Buland
38 */
39 class ProgramLink *getLink( const char *lpName );
40
41 /** Gets the very first link in the chain.
42 *@returns A pointer to the first link in the chain.
43 *@author Mike Buland
44 */
45 class ProgramLink *getBaseLink();
46
47 /** Runs through the chain once. Useful if you want to have more control over
48 * the operation of the chain.
49 *@returns true if every link returned true. If at least one link returns false,
50 * then returns false.
51 *@author Mike Buland
52 */
53 bool execChainOnce();
54
55 /** Enters the master chain loop, looping over the entire chain and executing
56 * every link's TimeSlice routine in order, over and over, until a link returns
57 * a false value.
58 *@returns False, always. It returns true unless a link returned false, but loops
59 * until a link does return false.
60 *@author Mike Buland
61 **/
62 bool enterChainLoop();
63
64 /** Broadcasts an Immediate Response Message to all active links, save the
65 * sender. Whatever link first responds with a non-null response message
66 * will have it's messages sent back to the broadcasting link as the returns
67 * of this function call. Therefore it is very important that all message
68 * processing code is handled in a fairly timely fasion.
69 *@param pMsgOut The message to broadcast in hopes of a response.
70 *@param pSender The message that sent out the message and doesn't want to
71 * receive it's own message. This should always just be "this".
72 *@returns The message that was returned by the first link to return a
73 * non-null response. If all messages return null responses then this also
74 * returns null. Please note that whoever calls this will be responsible
75 * for deleting the message returned by it, if non-null.
76 */
77 class LinkMessage *broadcastIRM( LinkMessage *pMsgOut, ProgramLink *pSender );
78
79private:
80 /**
81 * Shuts down all operation no matter what point in the operation we were.
82 */
83 void emergencyShutdown();
84 MultiLog *pLog; /**< A pointer to a log. */
85 LinkedList lLink; /**< The linked list that contains all of the links. */
86};
87
88#endif
diff --git a/src/programlink.cpp b/src/programlink.cpp
new file mode 100644
index 0000000..de13be8
--- /dev/null
+++ b/src/programlink.cpp
@@ -0,0 +1,71 @@
1/***************************************************************************
2 programlink.cpp - description
3 -------------------
4 begin : Sat Sep 6 2003
5 copyright : (C) 2003 by Mike Buland
6 email : eichlan@yf-soft.com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "programlink.h"
19#include "programchain.h"
20
21ProgramLink::ProgramLink()
22{
23}
24
25ProgramLink::~ProgramLink()
26{
27}
28
29LinkMessage *ProgramLink::sendIRM( LinkMessage *pMsgOut )
30{
31 return pChain->broadcastIRM( pMsgOut, this );
32}
33
34void ProgramLink::setChain( ProgramChain *pNewChain )
35{
36 pChain = pNewChain;
37}
38
39/*
40void ProgramLink::postMessage( LinkMessage *pMsg, int nLvl )
41{
42 if( nLvl == msgToChain )
43 {
44 qMsgToChain.enqueue( pMsg );
45 }
46 else if( nLvl == msgToLink )
47 {
48 qMsgToLink.enqueue( pMsg );
49 }
50 else
51 {
52 // ERROR!
53 }
54}
55
56LinkMessage *ProgramLink::getMessage( int nLvl )
57{
58 if( nLvl == msgToChain )
59 {
60 return (LinkMessage *)qMsgToChain.dequeue();
61 }
62 else if( nLvl == msgToLink )
63 {
64 return (LinkMessage *)qMsgToLink.dequeue();
65 }
66 else
67 {
68 // ERROR!
69 }
70}
71*/
diff --git a/src/programlink.h b/src/programlink.h
new file mode 100644
index 0000000..6499fc2
--- /dev/null
+++ b/src/programlink.h
@@ -0,0 +1,99 @@
1#ifndef PROGRAMLINK_H
2#define PROGRAMLINK_H
3
4class ProgramLink;
5#include "queue.h"
6#include "linkmessage.h"
7#include "programchain.h"
8
9/**
10 * Program Link is the base class for any object that will be a piece of the
11 * main program chain loop.
12 *@author Mike Buland
13 */
14class ProgramLink
15{
16friend class ProgramChain;
17public:
18 /**
19 * Construct a program link.
20 */
21 ProgramLink();
22
23 /**
24 * Deconstruct.
25 */
26 virtual ~ProgramLink();
27
28 /**
29 * Initialization code required for a link that wasn't performed in the
30 * constructor.
31 *@returns true if initialization was successful. A false value will halt
32 * the chain.
33 */
34 virtual bool init()=0;
35
36 /**
37 * DeInitialization code that should happen, but doesn't belong in the
38 * destructor.
39 *@returns true means everything worked, false means failure, but is
40 * meaningless.
41 */
42 virtual bool deInit()=0;
43
44 /**
45 * Executed once per link per chain pass. Contains the guts of the program.
46 *@returns true if everything went well. A false value will halt the chain.
47 */
48 virtual bool timeSlice()=0;
49
50 /**
51 * This must be handled in order to process Instant Response Messages.
52 * This function should return null on all messages that it doesn't
53 * understand how to handle, and construct new messages to return to sender
54 * in the cases where it does understand.
55 *@param pMsgIn The message that must be processed.
56 *@returns Either a new message in cases where a response is required,
57 * or null if nothing needs to be done by this link.
58 */
59 virtual LinkMessage *processIRM( LinkMessage *pMsgIn ) = 0;
60
61 /**
62 * Broadcast a LinkMessage to all other links in the system. Each other
63 * link will get a call of their processIRM function. If the message gets
64 * a response then you will regain control immediately, otherwise the system
65 * will give all other Links a chance to respond before returning NULL.
66 *@param pMsgOut The message to broadcast.
67 *@returns The message response, or NULL if no Link understood your message.
68 */
69 LinkMessage *sendIRM( LinkMessage *pMsgOut );
70
71private:
72 /**
73 * Set which chain we're assosiated with. This is hope IRM messages make
74 * it out to the rest of the world.
75 *@param pNewChain A pointer to the containing program chain.
76 */
77 void setChain( class ProgramChain *pNewChain );
78
79 /**
80 * The pointer to the containing chain.
81 */
82 class ProgramChain *pChain;
83/*
84 void postMessage( LinkMessage *pMsg, int nLvl );
85 LinkMessage *getMessage( int nLvl );
86
87 enum
88 {
89 msgToChain,
90 msgToLink
91 };
92
93private:
94 Queue qMsgToChain;
95 Queue qMsgToLink;
96*/
97};
98
99#endif
diff --git a/src/protocol.cpp b/src/protocol.cpp
new file mode 100644
index 0000000..1b2621f
--- /dev/null
+++ b/src/protocol.cpp
@@ -0,0 +1,31 @@
1/***************************************************************************
2 * Copyright (C) 2003 by Mike Buland *
3 * eichlan@yf-soft.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 ***************************************************************************/
10#include "protocol.h"
11
12Protocol::Protocol()
13{
14 pConnection = NULL;
15}
16
17Protocol::~Protocol()
18{
19}
20
21void Protocol::setConnection( Connection *pNewConnection )
22{
23 pConnection = pNewConnection;
24
25 onNewConnection();
26}
27
28Connection *Protocol::getConnection()
29{
30 return pConnection;
31}
diff --git a/src/protocol.h b/src/protocol.h
new file mode 100644
index 0000000..cd18e37
--- /dev/null
+++ b/src/protocol.h
@@ -0,0 +1,58 @@
1#ifndef PROTOCOL_H
2#define PROTOCOL_H
3
4#include "connection.h"
5
6/** This is the template for a class that handles specialized input and output
7 * to connections of different types with different protocols.
8 *@author Mike Buland
9 */
10class Protocol
11{
12public:
13 /** Constructor */
14 Protocol();
15 /** Deconstructor */
16 virtual ~Protocol();
17
18 /**
19 * Function is called every time there is new data on the line. This is
20 * called directly from the Connection class to process data. This is not
21 * called whever there is pending data on the input, but every time new data
22 * is added to the input buffer.
23 *@returns True if processing went alright, false if something went wrong,
24 * I suppose. In truth this value is thrown away right now.
25 *@todo Either make a return value of false mean something, or make these
26 * void.
27 */
28 virtual bool onNewData()=0;
29
30 /**
31 * Function is called when there is a new connection. This should only
32 * happen once per Protocol object, but gives each protocol object a
33 * chance to perform connection handshaking and initialization at a point
34 * where they know that they have a handle to an active Connection.
35 *@returns See onNewData
36 */
37 virtual bool onNewConnection()=0;
38
39 /**
40 * Sets the Protocol's Connection object. This is rather important, and
41 * handled usually by the ConnectionManager.
42 *@param pNewConnection The Connection object that this protocol will use to
43 * deal with the outside world.
44 */
45 void setConnection( class Connection *pNewConnection );
46
47 /**
48 * Get a pointer to this object's Connection object, or NULL if one was
49 * never set. If used with the ConnectionManager that should never happen.
50 *@returns A pointer to the active Connection.
51 */
52 Connection *getConnection();
53
54private:
55 class Connection *pConnection; /**< The pointer to the Connection. */
56};
57
58#endif
diff --git a/src/protocoltelnet.cpp b/src/protocoltelnet.cpp
new file mode 100644
index 0000000..7beea5b
--- /dev/null
+++ b/src/protocoltelnet.cpp
@@ -0,0 +1,315 @@
1#include "protocoltelnet.h"
2#include <string.h>
3
4ProtocolTelnet::ProtocolTelnet()
5{
6 nTermType = termUnInited;
7 bEchoOn = true;
8}
9
10ProtocolTelnet::~ProtocolTelnet()
11{
12}
13
14bool ProtocolTelnet::onNewConnection()
15{
16 Connection *pCon = getConnection();
17
18 pCon->appendOutput( (char)IAC );
19 pCon->appendOutput( (char)WILL );
20 pCon->appendOutput( (char)SUPPRESSGA );
21
22 pCon->appendOutput( (char)IAC );
23 pCon->appendOutput( (char)DO );
24 pCon->appendOutput( (char)SUPPRESSGA );
25
26 pCon->appendOutput( (char)IAC );
27 pCon->appendOutput( (char)DONT );
28 pCon->appendOutput( (char)TERMTYPE );
29
30// pCon->appendOutput( IAC );
31// pCon->appendOutput( SB );
32// pCon->appendOutput( TERMTYPE );
33// pCon->appendOutput( 1 );
34// pCon->appendOutput( IAC );
35// pCon->appendOutput( SE );
36
37 pCon->appendOutput( (char)IAC );
38 pCon->appendOutput( (char)DONT );
39 pCon->appendOutput( (char)ECHO );
40
41 pCon->appendOutput( (char)IAC );
42 pCon->appendOutput( (char)WILL );
43 pCon->appendOutput( (char)ECHO );
44
45// 255(IAC),251(WILL),3
46}
47
48bool ProtocolTelnet::onNewData()
49{
50 Connection *pCon = getConnection();
51 if( !pCon->hasInput() )
52 {
53 return true;
54 }
55
56 int nInSize = pCon->getInputAmnt();
57 char *lpInStr = (char *)pCon->getInput();
58
59 // Here we interpret the basic commands and un-encapsulate them, so to
60 // speak. We'll allow this, even if the terminal is in raw mode, we
61 // just won't send anything in response...
62 for( int j = 0; j < nInSize; j++ )
63 {
64 switch( (unsigned char)lpInStr[j] )
65 {
66 case '\r':
67 fbEdited.appendData('\n');
68 if( bEchoOn ) pCon->appendOutput("\n\r");
69 break;
70
71 case '\n':
72 break;
73
74 case '\177': // backspace
75 if( fbEdited.getLength() > 0 )
76 {
77 fbEdited.usedData( -1 ); // Delete one char from the end
78 if( bEchoOn ) pCon->appendOutput(ESC "[D"); // Move the cursor back one
79 if( bEchoOn ) pCon->appendOutput(ESC "[P"); // Delete one character
80 }
81 break;
82
83 case '\x1B': // escape sequence
84 if( (unsigned char)lpInStr[j+1] == '[' )
85 {
86 switch( (unsigned char)lpInStr[j+2] )
87 {
88 case 'A': // Up
89 break;
90
91 case 'B': // Down
92 break;
93
94 case 'C': // Right
95 break;
96
97 case 'D': // Left
98 break;
99 }
100 j+=2;
101 }
102 break;
103
104 case 0: // NOP: No operation
105 break;
106
107 case IAC: // IAC: Interpret as command
108 switch( lpInStr[j+1] )
109 {
110 case SE: // SE: End of subnegotiation parameters.
111 break;
112
113 case NOP: // NOP: No operation
114 break;
115
116 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.
117 break;
118
119 case BRK: // BRK: Break. Indicates that the "break" or "attention" key was hit.
120 break;
121
122 case IP: // IP: Suspend, interrupt or abort the process to which the NVT is connected.
123 break;
124
125 case AO: // AO: Abort output. Allows the current process to run to completion but do not send its output to the user.
126 break;
127
128 case AYT: // AYT: Are you there. Send back to the NVT some visible evidence that the AYT was received.
129 break;
130
131 case EC: // EC: Erase character. The receiver should delete the last preceding undeleted character from the data stream.
132 break;
133
134 case EL: // EL: Erase line. Delete characters from the data stream back to but not including the previous CRLF.
135 break;
136
137 case GA: // GA: Go ahead. Used, under certain circumstances, to tell the other end that it can transmit.
138 break;
139
140 case SB: // SB: Subnegotiation of the indicated option follows.
141 switch( lpInStr[j+2] )
142 {
143 case TERMTYPE:
144 if( lpInStr[j+3] == 0 )
145 {
146 for( int k = 0; j+4+k < nInSize; k++ )
147 {
148 if( (unsigned char)lpInStr[j+4+k] == IAC &&
149 (unsigned char)lpInStr[j+5+k] == SE )
150 {
151 lpInStr[j+4+k] = 0;
152 //@TODO: Do something with the term type...
153 printf("Term type: %s\n", &lpInStr[j+4] );
154 j += 5+k;
155 }
156 }
157 }
158 else
159 {
160 }
161 break;
162
163 default:
164 //printf("unknown subnegotiation parameters! (%d)\n", lpInStr[j+2] );
165 break;
166 }
167 break;
168
169 case WILL: // WILL: Indicates the desire to begin performing
170 switch( lpInStr[j+2] )
171 {
172 case SUPPRESSGA:
173 j += 2;
174// pCon->usedInput( 3 );
175 break;
176
177 case TERMTYPE:
178 j += 2;
179// pCon->usedInput( 3 );
180 break;
181
182 case ECHO:
183 j += 2;
184// pCon->usedInput( 3 );
185 break;
186
187 case NAWS:
188 default:
189 pCon->appendOutput( (char)ESC[0] );
190 pCon->appendOutput( (char)DONT );
191 pCon->appendOutput( lpInStr[j+2] );
192 //printf("unknown will command used! (%d)\n", lpInStr[j+2] );
193 j += 2;
194 break;
195 }
196 break;
197
198 case WONT: // WONT: Indicates the refusal to perform
199 switch( lpInStr[j+2] )
200 {
201 case ECHO:
202 j += 2;
203// pCon->usedInput( 3 );
204 break;
205
206 default:
207 //printf("unknown wont command used! (%d)\n", lpInStr[j+2] );
208 j += 2;
209 break;
210 }
211 break;
212
213 case DO: // DO: Indicates the request that the other party perform
214 switch( lpInStr[j+2] )
215 {
216 case ECHO:
217 j += 2;
218 break;
219
220 case SUPPRESSGA:
221 j += 2;
222 break;
223
224 default:
225 pCon->appendOutput( (char)ESC[0] );
226 pCon->appendOutput( (char)DONT );
227 pCon->appendOutput( lpInStr[j+2] );
228 //printf("unknown do command used! (%d)\n", lpInStr[j+2] );
229 j += 2;
230 break;
231 }
232// pCon->usedInput( 3 );
233 break;
234
235 case DONT: // DONT: Indicates the demand that the other party stop performing
236 switch( lpInStr[j+2] )
237 {
238 case ECHO:
239 j += 2;
240// pCon->usedInput( 3 );
241 break;
242
243 default:
244 printf("unknown dont command used! (%d)\n", lpInStr[j+2] );
245 j += 2;
246 break;
247 }
248 break;
249 }
250 break;
251
252 default:
253 fbEdited.appendData( lpInStr[j] );
254 if( bEchoOn ) pCon->appendOutput( lpInStr[j] );
255 break;
256 }
257 }
258
259 pCon->usedInput( pCon->getInputAmnt() );
260
261 return true;
262}
263
264char *ProtocolTelnet::getLine( bool bFullOnly )
265{
266 int i = fbEdited.findChar('\n');
267
268 if( i < 0 )
269 {
270 if( bFullOnly == false )
271 {
272 i = fbEdited.getLength();
273 }
274 else
275 {
276 return NULL;
277 }
278 }
279
280 char *lpStr = new char[i+1];
281 strncpy( lpStr, fbEdited.getData(), i );
282 lpStr[i] = '\0';
283
284 fbEdited.usedData( i+1 );
285
286 return lpStr;
287}
288
289char *ProtocolTelnet::peekLine( bool bFullOnly )
290{
291 int i = fbEdited.findChar('\n');
292
293 if( i < 0 )
294 {
295 if( bFullOnly == false )
296 {
297 i = fbEdited.getLength();
298 }
299 else
300 {
301 return NULL;
302 }
303 }
304
305 char *lpStr = new char[i+1];
306 strncpy( lpStr, fbEdited.getData(), i );
307 lpStr[i] = '\0';
308
309 return lpStr;
310}
311
312void ProtocolTelnet::setEcho( bool bEchoOn )
313{
314 this->bEchoOn = bEchoOn;
315}
diff --git a/src/protocoltelnet.h b/src/protocoltelnet.h
new file mode 100644
index 0000000..4b2fb32
--- /dev/null
+++ b/src/protocoltelnet.h
@@ -0,0 +1,77 @@
1#ifndef PROTOCOLTELNET_H
2#define PROTOCOLTELNET_H
3
4#include "protocol.h"
5#include "flexbuf.h"
6
7#define ESC "\x1B" /**< A telnet escape code. */
8
9/** Handles all specialized protocol actions related to the telnet protocol.
10 * This includes setting modes, non-scrollable regions, and so on.
11 *@author Mike Buland
12 */
13class ProtocolTelnet : public Protocol
14{
15public:
16 ProtocolTelnet();
17 ~ProtocolTelnet();
18
19 bool onNewData();
20 bool onNewConnection();
21
22 char *getLine( bool bFullOnly = true );
23 char *peekLine( bool bFullOnly = true );
24
25 void setEcho( bool bEchoOn = true );
26
27 enum
28 {
29 termUnInited,
30 termRaw,
31 termUnknown,
32 termVT220,
33 termXTerm
34 };
35
36 enum
37 {
38 SE = 240, // SE: End of subnegotiation parameters.
39 NOP = 241, // NOP: No operation
40 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.
41 BRK = 243, // BRK: Break. Indicates that the "break" or "attention" key was hit.
42 IP = 244, // IP: Suspend, interrupt or abort the process to which the NVT is connected.
43 AO = 245, // AO: Abort output. Allows the current process to run to completion but do not send its output to the user.
44 AYT = 246, // AYT: Are you there. Send back to the NVT some visible evidence that the AYT was received.
45 EC = 247, // EC: Erase character. The receiver should delete the last preceding undeleted character from the data stream.
46 EL = 248, // EL: Erase line. Delete characters from the data stream back to but not including the previous CRLF.
47 GA = 249, // GA: Go ahead. Used, under certain circumstances, to tell the other end that it can transmit.
48 SB = 250, // SB: Subnegotiation of the indicated option follows.
49 WILL = 251, // WILL: Indicates the desire to begin performing, or confirmation that you are now performing, the indicated option.
50 WONT = 252, // WONT: Indicates the refusal to perform, or continue performing, the indicated option.
51 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.
52 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.
53 IAC = 255 // IAC: Interpret as command
54 };
55
56 enum
57 {
58 ECHO = 1, // Explain who'll echo
59 SUPPRESSGA = 3, // Suppress Go Ahead
60 TERMTYPE = 24, // Terminal Type
61 NAWS = 31, // Window size
62 TERMSPEED = 32, // Terminal Speed
63 LINEMODE = 34 // Linemode
64 };
65
66private:
67 int nTermType;
68
69 int nTermWidth;
70 int nTermHeight;
71
72 FlexBuf fbEdited;
73
74 bool bEchoOn;
75};
76
77#endif
diff --git a/src/queue.cpp b/src/queue.cpp
new file mode 100644
index 0000000..42999fe
--- /dev/null
+++ b/src/queue.cpp
@@ -0,0 +1,26 @@
1#include "queue.h"
2
3void Queue::enqueue( void *data )
4{
5 lQueueData.append( data );
6}
7
8void *Queue::dequeue()
9{
10 void *dat = lQueueData[0];
11 if( dat != NULL )
12 {
13 lQueueData.deleteAt( 0 );
14 }
15 return dat;
16}
17
18bool Queue::isEmpty()
19{
20 return lQueueData.isEmpty();
21}
22
23void Queue::empty()
24{
25 lQueueData.empty();
26}
diff --git a/src/queue.h b/src/queue.h
new file mode 100644
index 0000000..692f5d8
--- /dev/null
+++ b/src/queue.h
@@ -0,0 +1,45 @@
1#ifndef QUEUE_H
2#define QUEUE_H
3#include "linkedlist.h"
4
5/**
6 * An ultra-simple queue implementation. It just uses a linked list as it's
7 * container so we don't have to worry about anything!
8 *@author Mike Buland
9 */
10class Queue
11{
12public:
13 /**
14 * Puts a new item at the end of the queue.
15 *@param data A new value to put at the end of the queue.
16 */
17 void enqueue( void *data );
18
19 /**
20 * Gets the begining item of the queue off and returns it.
21 *@returns The value at the front of the queue.
22 */
23 void *dequeue();
24
25 /**
26 * Checks if the queueu is empty.
27 *@returns True if the queueu is empty, and false if it has things in it.
28 */
29 bool isEmpty();
30
31 /**
32 * Empty the queue.
33 */
34 void empty();
35
36 /**
37 * Get a pointer to the internal list object.
38 *@returns A pointer to the internal list object.
39 */
40 LinkedList *getList() { return &lQueueData; };
41
42private:
43 LinkedList lQueueData; /**< Where all of the real data is stored. */
44};
45#endif
diff --git a/src/ringlist.cpp b/src/ringlist.cpp
new file mode 100644
index 0000000..9efbbc4
--- /dev/null
+++ b/src/ringlist.cpp
@@ -0,0 +1,106 @@
1//
2// C++ Implementation: ringlist
3//
4// Description:
5//
6//
7// Author: Mike Buland <eichlan@yf-soft.com>, (C) 2005
8//
9// Copyright: See COPYING file that comes with this distribution
10//
11//
12#include <stdlib.h>
13
14#include "ringlist.h"
15
16RingList::RingList( int nInitSize )
17 : List()
18{
19 nFirstIndex = 0;
20 nRealLength = nInitSize;
21 nDataLength = 0;
22 apData = new void*[nInitSize];
23 pPushBuf = NULL;
24}
25
26RingList::~RingList()
27{
28 delete[] apData;
29}
30
31void *RingList::getAt( int nIndex )
32{
33 if( nIndex < 0 || nIndex >= nDataLength )
34 {
35 return NULL;
36 }
37
38 return apData[(nFirstIndex+nIndex)%nRealLength];
39}
40
41void RingList::append( void *pData )
42{
43 int nIndex = (nFirstIndex+nDataLength)%nRealLength;
44
45 pPushBuf = apData[nIndex];
46 apData[nIndex] = pData;
47
48 if( nDataLength == nRealLength )
49 {
50 nFirstIndex = (nFirstIndex+1)%nRealLength;
51 }
52 else
53 {
54 nDataLength++;
55 // We really didn't need it this time...
56 pPushBuf = NULL;
57 }
58}
59
60void RingList::insertBefore( void *pData, int nPos )
61{
62 // Not implemented right now, don't even try it!
63}
64
65int RingList::getSize()
66{
67 return nDataLength;
68}
69
70bool RingList::isEmpty()
71{
72 return nDataLength==0;
73}
74
75void RingList::deleteAt( int nIndex )
76{
77 // Also not implemented yet
78}
79
80void RingList::empty()
81{
82 nFirstIndex = 0;
83 nDataLength = 0;
84}
85
86void RingList::setSize( int nNewSize )
87{
88 if( apData )
89 {
90 delete[] apData;
91 }
92 nFirstIndex = 0;
93 nRealLength = nNewSize;
94 nDataLength = 0;
95 apData = new void*[nNewSize];
96}
97
98void RingList::setAt( int nIndex, void *pData )
99{
100 apData[(nIndex+nFirstIndex)%nRealLength] = pData;
101}
102
103void *RingList::getPushBuf()
104{
105 return pPushBuf;
106}
diff --git a/src/ringlist.h b/src/ringlist.h
new file mode 100644
index 0000000..1a4d3a9
--- /dev/null
+++ b/src/ringlist.h
@@ -0,0 +1,112 @@
1#ifndef RINGLIST_H
2#define RINGLIST_H
3
4#include "list.h"
5
6/**
7 * A RingList or Ring Buffer implementation. This is a list that never grows in
8 * size once it is created, but instead once it is full new items added to the
9 * RingList replace the oldest items and the zero-index is virtually shifted.
10 * Since no data is actually moved when zero-index moves, this is very
11 * efficient.
12 * <br>
13 * The items removed are not actually deleted by the RingList, so instead they
14 * are first moved into a temporary "Push Buffer" that can be accessed so that
15 * elements pushed off the edge of the RingList can be accessed for cleanup.
16 *@author Mike Buland
17 */
18class RingList : public List
19{
20public:
21 /**
22 * Construct a RingList with a fixed initial size. This size never changes
23 * unless setSize is called later during normal operation.
24 *@param nInitSize The number of elements to allocate.
25 */
26 RingList( int nInitSize );
27
28 /**
29 * Clean up the data structures, but not the contained elements.
30 */
31 ~RingList();
32
33 /**
34 * Get an element at the specified index.
35 *@param nIndex The index of the element to retreive.
36 *@returns A pointer to the requested element, or NULL if the element is
37 * not found or not initialized yet.
38 */
39 void *getAt( int nIndex );
40
41 /**
42 * Append an element to the end of the list, overwriting the begining if
43 * necesarry.
44 *@param pData The pointer to append to the RingList.
45 */
46 void append( void *pData );
47
48 /**
49 * Insert an element before another in the RingList, pushing all after it
50 * down the list.
51 *@param pData The data to insert.
52 *@param nPos The position that new the element should occupy in the list.
53 */
54 void insertBefore( void *pData, int nPos = 0 );
55
56 /**
57 * Get the size of the array.
58 */
59 int getSize();
60
61 /**
62 * Is the RingList empty?
63 *@returns True if it is empty, false otherwise.
64 */
65 bool isEmpty();
66
67 /**
68 * Delete an element in the list, moving all later elements down one index.
69 *@param nIndex The index of the element to delete.
70 */
71 void deleteAt( int nIndex );
72
73 /**
74 * Remove all elements from the RingList.
75 */
76 void empty();
77
78 /**
79 * Set a new size for the RingList. Be careful with this one, if shrinking
80 * this may quietly create a memory leak.
81 *@param nNewSize The new size to set the RingList to.
82 *@todo Either fix this memory leak somehow or remove this function.
83 */
84 void setSize( int nNewSize );
85
86 /**
87 * Set a specific element to a new value.
88 *@param nIndex The zero-based index to change the value of.
89 *@param pData The data to put at the location specified by nIndex.
90 */
91 void setAt( int nIndex, void *pData );
92
93 /**
94 * Retrieve the contents of the push buffer. This is the data that is
95 * pushed off the end of the array if you append data and the list is full.
96 * This should be checked after every append operation to be sure there
97 * isn't anything that needs deleting.
98 *@returns The last value pushed off the RingList, or NULL if nothing was
99 * pushed off.
100 */
101 void *getPushBuf();
102
103private:
104 int nFirstIndex; /**< The index to be translated as zero. */
105 int nRealLength; /**< The Amount of storage space available. */
106 int nDataLength; /**< The number of elements filled in. */
107 void **apData; /**< The actual data storage. */
108 void *pPushBuf; /**< The push buffer. */
109
110};
111
112#endif
diff --git a/src/stack.cpp b/src/stack.cpp
new file mode 100644
index 0000000..8d9565c
--- /dev/null
+++ b/src/stack.cpp
@@ -0,0 +1,33 @@
1#include "stack.h"
2
3void Stack::push( void *data )
4{
5 lStackData.append( data );
6}
7
8void *Stack::top()
9{
10 return lStackData.getAt( lStackData.getSize()-1 );
11}
12
13void Stack::pop()
14{
15 lStackData.deleteAt( lStackData.getSize()-1 );
16}
17
18void *Stack::poptop()
19{
20 void *dat = top();
21 pop();
22 return dat;
23}
24
25bool Stack::isEmpty()
26{
27 return lStackData.isEmpty();
28}
29
30void Stack::empty()
31{
32 lStackData.empty();
33}
diff --git a/src/stack.h b/src/stack.h
new file mode 100644
index 0000000..30e2a19
--- /dev/null
+++ b/src/stack.h
@@ -0,0 +1,50 @@
1#ifndef STACK_H
2#define STACK_H
3#include "linkedlist.h"
4
5/** An ultra-simple stack implementation that just uses a linked list.
6 *@author Mike Buland
7 */
8class Stack
9{
10public:
11 /** Pushes a new value onto the top of the stack.
12 *@param data A new value for the stack.
13 *@author Mike Buland
14 */
15 void push( void *data );
16
17 /** Returns the top value off of the stack, but doesn't remove it from the
18 * stack.
19 *@returns The value at the top of the stack.
20 *@author Mike Buland
21 */
22 void *top();
23
24 /** Pops the top item off of the stack.
25 *@author Mike Buland
26 */
27 void pop();
28
29 /** Gets the top item off of the stack, pops it off the stack, and returns
30 * it.
31 *@returns The value at the top of the stack.
32 *@author Mike Buland
33 */
34 void *poptop();
35
36 /** Checks if the stack is empty.
37 *@returns True if the stack is empty, and false if it has things in it.
38 *@author Mike Buland
39 */
40 bool isEmpty();
41
42 /** Empty the stack.
43 *@author Mike Buland
44 */
45 void empty();
46
47private:
48 LinkedList lStackData; /**< The actual stack data. */
49};
50#endif
diff --git a/src/test/hashtest.cpp b/src/test/hashtest.cpp
new file mode 100644
index 0000000..f31a3f8
--- /dev/null
+++ b/src/test/hashtest.cpp
@@ -0,0 +1,107 @@
1#include <stdio.h>
2#include <iostream>
3#include "hashtable.h"
4#include "hashfunctioncasestring.h"
5
6int main()
7{
8 const char *names[]={
9 "Homer the Great",
10 "And Maggie Makes Three",
11 "Bart's Comet",
12 "Homie The Clown",
13 "Bart Vs Australia",
14 "Homer vs Patty and Selma",
15 "A star is burns",
16 "Lisa's Wedding",
17 "Two Dozen and One Greyhounds",
18 "The PTA Disbands",
19 "Round Springfield",
20 "The Springfield connection",
21 "Lemon of Troy",
22 "Who Shot Mr. Burns (Pt. 1)",
23 "Who Shot Mr. Burns (pt. 2)",
24 "Radioactive Man",
25 "Home Sweet Homediddly-dum-doodly",
26 "Bart Sells His Soul",
27 "Lisa the Vegetarian",
28 "Treehouse of horror VI",
29 "King Size Homer",
30 "Mother Simpson",
31 "Sideshow Bob's Last Gleaming",
32 "The Simpson's 138th Show Spectacular",
33 "Marge Be Not Proud",
34 "Team Homer",
35 "Two Bad Neighbors",
36 "Scenes From the Class Struggle in Springfield",
37 "Bart the Fink",
38 "Lisa the Iconoclast",
39 "Homer the Smithers",
40 "The Day the Violence Died",
41 "A Fish Called Selma",
42 "Bart on the road",
43 "22 Short Films about Springfield",
44 "The Curse of the Flying Hellfish",
45 "Much Apu about Nothing",
46 "Homerpalooza",
47 "The Summer of 4 Ft 2",
48 "Treehouse of Horror VII",
49 "You Only Move Twice",
50 "The Homer They Fall",
51 "Burns Baby Burns",
52 "Bart After Dark",
53 "A Millhouse Divided",
54 "Lisas Date With Destiny",
55 "Hurricane Neddy",
56 "The Mysterious Voyage of Our Homer",
57 "The Springfield Files",
58 "The Twisted World of Marge Simpson",
59 "Mountain of Madness",
60 NULL
61 };
62
63 HashTable h( new HashFunctionCaseString(), 5, false );
64
65 int j;
66 printf("Inserting...\n");
67 for( j = 0; j < 10; j++ )
68 {
69 h.insert( names[j], (void *)(j+1) );
70 h.insert( names[j], (void *)(j+1) );
71 printf("Capacity: %d, Size: %d, Load: %f\n",
72 h.getCapacity(),
73 h.getSize(),
74 h.getLoad()
75 );
76 }
77
78 for( j = 0; j < 10; j++ )
79 {
80 printf("\"%s\" = %d\n", names[j], (int)h[names[j]] );
81 }
82
83 printf("\nDeleting some...\n");
84
85 for( int k = 0; k < 7; k++ )
86 {
87 h.del( names[k] );
88 //h.insert( names[j], (void *)(j+1) );
89 printf("Capacity: %d, Size: %d, Load: %f\n",
90 h.getCapacity(),
91 h.getSize(),
92 h.getLoad()
93 );
94 }
95
96 printf("\nInserting more...\n");
97
98 for( ; names[j] != NULL; j++ )
99 {
100 h.insert( names[j], (void *)(j+1) );
101 printf("Capacity: %d, Size: %d, Load: %f\n",
102 h.getCapacity(),
103 h.getSize(),
104 h.getLoad()
105 );
106 }
107}
diff --git a/src/test/httpsrv/httpconnectionmonitor.cpp b/src/test/httpsrv/httpconnectionmonitor.cpp
new file mode 100644
index 0000000..4eb6817
--- /dev/null
+++ b/src/test/httpsrv/httpconnectionmonitor.cpp
@@ -0,0 +1,72 @@
1#include "httpconnectionmonitor.h"
2#include "http.h"
3#include <sys/stat.h>
4
5HttpConnectionMonitor::HttpConnectionMonitor()
6{
7}
8
9HttpConnectionMonitor::~HttpConnectionMonitor()
10{
11}
12
13bool HttpConnectionMonitor::onNewConnection( Connection *pCon )
14{
15 Http hp( pCon );
16
17 pCon->readInput( 60, 0 );
18 printf("#######################\n%s\n#######################\n", pCon->getInput() );
19
20 while( hp.parseRequest() == false );
21 printf("Done parsing.\n\n");
22
23 if( hp.getRequestType() == Http::reqGet )
24 {
25 printf("\"\"\"%s\"\"\"\n", hp.getRequestURI() );
26 if( !strcmp( hp.getRequestURI(), "/" ) )
27 {
28 std::string content("<html><head><title>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>");
29 hp.buildResponse();
30 hp.setResponseContent(
31 "text/html",
32 content.c_str(),
33 content.size()
34 );
35 hp.sendResponse();
36 }
37 else
38 {
39 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>");
40 hp.buildResponse( 404, "File not found.");
41 hp.setResponseContent(
42 "text/html",
43 content.c_str(),
44 content.size()
45 );
46 hp.sendResponse();
47 }
48 }
49 else
50 {
51 printf("Non get: %s\n", hp.getRequestTypeStr() );
52 }
53 pCon->writeOutput();
54
55 if( pCon->hasInput() )
56 {
57 std::string s( pCon->getInput(), pCon->getInputAmnt() );
58
59 printf("Reamining data\n==============\n%s\n==============\n",
60 s.c_str() );
61 }
62
63 pCon->disconnect();
64
65 return true;
66}
67
68bool HttpConnectionMonitor::onClosedConnection( Connection *pCon )
69{
70 return true;
71}
72
diff --git a/src/test/httpsrv/httpconnectionmonitor.h b/src/test/httpsrv/httpconnectionmonitor.h
new file mode 100644
index 0000000..63f29e4
--- /dev/null
+++ b/src/test/httpsrv/httpconnectionmonitor.h
@@ -0,0 +1,16 @@
1#ifndef HTTPCONNECTIONMONITOR_H
2#define HTTPCONNECTIONMONITOR_H
3
4#include "connectionmonitor.h"
5
6class HttpConnectionMonitor : public ConnectionMonitor
7{
8public:
9 HttpConnectionMonitor();
10 ~HttpConnectionMonitor();
11
12 bool onNewConnection( Connection *pCon );
13 bool onClosedConnection( Connection *pCon );
14};
15
16#endif
diff --git a/src/test/httpsrv/main.cpp b/src/test/httpsrv/main.cpp
new file mode 100644
index 0000000..4ee1ad3
--- /dev/null
+++ b/src/test/httpsrv/main.cpp
@@ -0,0 +1,21 @@
1#include "connectionmanager.h"
2#include "httpconnectionmonitor.h"
3
4int main()
5{
6 printf("Starting server...\n");
7
8 ConnectionManager srv;
9 HttpConnectionMonitor http;
10
11 srv.setConnectionMonitor( &http );
12
13 srv.startServer( 7331, 40 );
14
15 for(;;)
16 {
17 srv.scanConnections( 5000, false );
18 }
19
20 return 0;
21}
diff --git a/src/test/md5test.cpp b/src/test/md5test.cpp
new file mode 100644
index 0000000..6f832df
--- /dev/null
+++ b/src/test/md5test.cpp
@@ -0,0 +1,19 @@
1#include <stdio.h>
2#include <string.h>
3#include "md5.h"
4
5int main()
6{
7 md5 mproc;
8 md5sum sum;
9 char hexstr[33];
10
11 memset( hexstr, 0, 33 );
12
13 mproc.sumString( &sum, "qwertyuiopasdfgh" );
14 mproc.sumToHex( &sum, hexstr );
15 printf("sum: %s\n", hexstr );
16 printf("chk: 1ebfc043d8880b758b13ddc8aa1638ef\n");
17
18 return 0;
19}
diff --git a/src/test/teltest/main.cpp b/src/test/teltest/main.cpp
new file mode 100644
index 0000000..ce968c4
--- /dev/null
+++ b/src/test/teltest/main.cpp
@@ -0,0 +1,21 @@
1#include "connectionmanager.h"
2#include "telnetmonitor.h"
3
4int main()
5{
6 printf("Starting server...\n");
7
8 ConnectionManager srv;
9 TelnetMonitor telnet;
10
11 srv.setConnectionMonitor( &telnet );
12
13 srv.startServer( 4001, 40 );
14
15 for(;;)
16 {
17 srv.scanConnections( 5000, false );
18 }
19
20 return 0;
21}
diff --git a/src/test/teltest/telnetmonitor.cpp b/src/test/teltest/telnetmonitor.cpp
new file mode 100644
index 0000000..001932f
--- /dev/null
+++ b/src/test/teltest/telnetmonitor.cpp
@@ -0,0 +1,53 @@
1#include "telnetmonitor.h"
2#include "protocoltelnet.h"
3#include <sys/stat.h>
4
5TelnetMonitor::TelnetMonitor()
6{
7}
8
9TelnetMonitor::~TelnetMonitor()
10{
11}
12
13bool TelnetMonitor::init()
14{
15 return true;
16}
17
18bool TelnetMonitor::deInit()
19{
20 return true;
21}
22
23bool TelnetMonitor::timeSlice()
24{
25 for( int j = 0; j < lCon.getSize(); j++ )
26 {
27 if( ((Connection *)lCon[j])->hasInput() )
28 {
29 printf("%s\n", ((Connection *)lCon[j])->getInput() );
30 }
31 }
32 return true;
33}
34
35LinkMessage* TelnetMonitor::processIRM( LinkMessage *pMsg )
36{
37}
38
39bool TelnetMonitor::onNewConnection( Connection *pCon )
40{
41 ProtocolTelnet *pt = new ProtocolTelnet();
42 pCon->setProtocol( pt );
43
44 lCon.append( pt );
45
46 return true;
47}
48
49bool TelnetMonitor::onClosedConnection( Connection *pCon )
50{
51 return true;
52}
53
diff --git a/src/test/teltest/telnetmonitor.h b/src/test/teltest/telnetmonitor.h
new file mode 100644
index 0000000..95c8493
--- /dev/null
+++ b/src/test/teltest/telnetmonitor.h
@@ -0,0 +1,26 @@
1#ifndef HTTPCONNECTIONMONITOR_H
2#define HTTPCONNECTIONMONITOR_H
3
4#include "connectionmonitor.h"
5#include "programlink.h"
6#include "linkedlist.h"
7
8class TelnetMonitor : public ConnectionMonitor, public ProgramLink
9{
10public:
11 TelnetMonitor();
12 ~TelnetMonitor();
13
14 bool init();
15 bool deInit();
16 bool timeSlice();
17 LinkMessage* processIRM( LinkMessage *pMsgIn );
18
19 bool onNewConnection( Connection *pCon );
20 bool onClosedConnection( Connection *pCon );
21
22private:
23 LinkedList lCon;
24};
25
26#endif
diff --git a/src/test/xmlreadtest.cpp b/src/test/xmlreadtest.cpp
new file mode 100644
index 0000000..5fbd021
--- /dev/null
+++ b/src/test/xmlreadtest.cpp
@@ -0,0 +1,29 @@
1#include "../xmlfilereader.h"
2#include "../xmlstringreader.h"
3#include "../xmlfilewriter.h"
4
5int main( int argc, char *argv[] )
6{
7 if( argc < 4 )
8 {
9 printf("Usage: %s f <file in> <file out>\n", argv[0] );
10 printf(" %s s <xml string> <file out>\n\n", argv[0] );
11 return 0;
12 }
13
14 if( argv[1][0] == 'f' )
15 {
16 XmlFileReader r( argv[2], true );
17 XmlFileWriter w( argv[3], "\t", r.getRoot() );
18 w.write();
19 //XmlWriter::write( argv[3], r.getRoot(), "\t" );
20 }
21 else if( argv[1][0] == 's' )
22 {
23 XmlStringReader r( argv[2], true );
24 //XmlWriter::write( argv[3], r.getRoot(), "\t" );
25 }
26
27 return 0;
28}
29
diff --git a/src/test/xmlrepltest.cpp b/src/test/xmlrepltest.cpp
new file mode 100644
index 0000000..1fe9ec2
--- /dev/null
+++ b/src/test/xmlrepltest.cpp
@@ -0,0 +1,31 @@
1#include "xmlwriter.h"
2
3int main()
4{
5 printf("Testing Xml Replacement...\n");
6 XmlDocument w;
7
8 w.addNode("text");
9 w.setContent("this text is before the node. ");
10 w.addNode("keepme", "This one we keep...", true );
11 w.setContent("this text is after.");
12 w.addNode("deleteme", "This one we don't...", true );
13 w.setContent("this is last..." );
14 w.closeNode();
15
16 //XmlWriter::writeNode( stdout, w.getRoot(), 0, NULL );
17
18 printf("\n\n");
19
20 XmlNode *xNode = w.getRoot()->detatchNode( 1 );
21
22 //XmlWriter::writeNode( stdout, w.getRoot(), 0, NULL );
23
24 printf("\n\n");
25
26 //XmlWriter::writeNode( stdout, xNode, 0, NULL );
27
28 printf("\n\n");
29
30 return 0;
31}
diff --git a/src/test/xmlwritetest.cpp b/src/test/xmlwritetest.cpp
new file mode 100644
index 0000000..340c9a3
--- /dev/null
+++ b/src/test/xmlwritetest.cpp
@@ -0,0 +1,41 @@
1#include "xmlfilewriter.h"
2#include "xmlstringwriter.h"
3
4void fillItIn( XmlWriter &w )
5{
6 w.addNode("thinglist");
7
8 w.addNode("thing");
9 w.addProperty("type", "Weapon");
10
11 w.addNode("id", "Klophin Staff", true );
12 w.addNode("name", "Klophin Staff", true );
13 w.addNode("durability", "0.01", true );
14 w.addNode("size", "0.1", true );
15
16 w.addNode("config");
17 w.addNode("damage", "3d6+4", true );
18 w.addNode("class", "melee", true );
19 w.addNode("type", "bludgeon", true );
20 w.addNode("damagedesc", "club/clubs", true );
21 w.closeNode();
22
23 w.closeNode();
24
25 w.closeNode();
26}
27
28int main()
29{
30 printf("Testing XmlWriter...\n");
31 XmlFileWriter wf("test.xml", "\t");
32
33 fillItIn( wf );
34
35 XmlStringWriter ws("\t");
36 fillItIn( ws );
37
38 printf("Now the string version:\n\n%s\n", ws.getString().c_str() );
39
40 return 0;
41}
diff --git a/src/tokenstring.cpp b/src/tokenstring.cpp
new file mode 100644
index 0000000..0c861ac
--- /dev/null
+++ b/src/tokenstring.cpp
@@ -0,0 +1,172 @@
1/***************************************************************************
2 * Copyright (C) 2003 by Mike Buland *
3 * eichlan@Xagafinelle *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 ***************************************************************************/
10#include "tokenstring.h"
11#include <string.h>
12
13TokenString::TokenString( const char *lpNewTokenString )
14{
15 lpTokenString = NULL;
16 if( lpNewTokenString )
17 {
18 parseLine( lpNewTokenString );
19 }
20}
21
22TokenString::~TokenString()
23{
24 delete[] lpTokenString;
25 for( int j = 0; j < lToken.getSize(); j++ )
26 {
27 delete[] (((Token *)lToken[j])->lpToken);
28 delete ((Token *)lToken[j]);
29 }
30}
31
32void TokenString::parseLine( const char *lpNewTokenString )
33{
34 if( lpTokenString != NULL )
35 {
36 delete[] lpTokenString;
37 lpTokenString = NULL;
38 for( int j = 0; j < lToken.getSize(); j++ )
39 {
40 delete[] (((Token *)lToken[j])->lpToken);
41 delete ((Token *)lToken[j]);
42 }
43 lToken.empty();
44 }
45 if( lpNewTokenString == NULL )
46 {
47 lpTokenString = new char[1];
48 lpTokenString[0] = '\0';
49 lToken.setSize(0);
50 return;
51 }
52 // First order of business, make an internal copy so someone can get it
53 // if they want to.
54 int nLen = strlen(lpNewTokenString);
55 lpTokenString = new char[nLen+1];
56 strcpy( lpTokenString, lpNewTokenString );
57
58 // Now we do a preliminary parse. This could be effected by later
59 // editing and aliasing, but we'll see...
60 int nTkStart, nTkEnd;
61 int mode=0; // 0 = startSearch, 1=endSearch
62 for( int j = 0; j <= nLen; j++ )
63 {
64 if( mode == 0 )
65 {
66 if( lpTokenString[j] != ' ' &&
67 lpTokenString[j] != '\t' )
68 {
69 nTkStart = j;
70 mode = 1;
71 }
72 }
73 else
74 {
75 if( lpTokenString[j] == ' ' ||
76 lpTokenString[j] == '\t' ||
77 lpTokenString[j] == '\0' )
78 {
79 nTkEnd = j-1;
80 mode = 0;
81
82 appendToken( nTkStart, nTkEnd );
83 }
84 }
85 }
86}
87
88void TokenString::appendToken( int nStart, int nEnd )
89{
90 Token *pToken = new Token;
91 pToken->lpOrig = &lpTokenString[nStart];
92
93 // nStart and nEnd are inclusive, we must add two for the end, and the null
94 pToken->lpToken = new char[nEnd-nStart+2];
95 memcpy( pToken->lpToken, &lpTokenString[nStart], nEnd-nStart+1 );
96 pToken->lpToken[nEnd-nStart+1] = '\0';
97
98// printf("%s\n", pToken->lpToken );
99 lToken.append( pToken );
100}
101
102void TokenString::insertToken( int nStart, int nEnd, char *lpOldOrig, const char *lpNewToken, int nIndex )
103{
104 Token *pToken = new Token;
105 pToken->lpOrig = lpOldOrig;
106
107 // nStart and nEnd are inclusive, we must add two for the end, and the null
108 pToken->lpToken = new char[nEnd-nStart+2];
109 memcpy( pToken->lpToken, &lpNewToken[nStart], nEnd-nStart+1 );
110 pToken->lpToken[nEnd-nStart+1] = '\0';
111
112 lToken.insertBefore( pToken, nIndex );
113}
114
115int TokenString::getNumTokens()
116{
117 return lToken.getSize();
118}
119
120char *TokenString::getToken( int nIndex )
121{
122 if( nIndex >= lToken.getSize() ) return NULL;
123 return (char *)(((Token *)lToken[nIndex])->lpToken);
124}
125
126char *TokenString::getTokenString( int nIndex )
127{
128 if( nIndex >= lToken.getSize() ) return NULL;
129 return (char *)(((Token *)lToken[nIndex])->lpOrig);
130}
131
132void TokenString::expandTokenTo( int nIndex, char *lpNewToken )
133{
134 // First, we delete the token at nIndex, then we keep inserting
135 // at that position...
136 // We also have to remember the index to the original string,
137 // since most of what we're expanding to won't be in the origingal
138 // we need to keep these indexes updated in order to make other parts
139 // of the system happy.
140 char *lpOldOrig = ((Token *)lToken[nIndex])->lpOrig;
141 delete[] ((Token *)lToken[nIndex])->lpToken;
142 delete ((Token *)lToken[nIndex]);
143 lToken.deleteAt( nIndex );
144
145 // We'll do this just like we did above, but instead we'll
146 // do tricky things when we find tokens...
147 int nLen = strlen(lpNewToken);
148 int nTkStart, nTkEnd, nNewIndex=nIndex;
149 int mode=0; // 0 = startSearch, 1=endSearch
150 for( int j = 0; j <= nLen; j++ )
151 {
152 if( mode == 0 )
153 {
154 if( lpNewToken[j] != ' ' && lpNewToken[j] != '\t' )
155 {
156 nTkStart = j;
157 mode = 1;
158 }
159 }
160 else
161 {
162 if( lpNewToken[j] == ' ' || lpNewToken[j] == '\t' || lpNewToken[j] == '\0' )
163 {
164 nTkEnd = j-1;
165 mode = 0;
166
167 insertToken( nTkStart, nTkEnd, lpOldOrig, lpNewToken, nNewIndex );
168 nNewIndex++;
169 }
170 }
171 }
172}
diff --git a/src/tokenstring.h b/src/tokenstring.h
new file mode 100644
index 0000000..25f710b
--- /dev/null
+++ b/src/tokenstring.h
@@ -0,0 +1,120 @@
1/***************************************************************************
2 * Copyright (C) 2003 by Mike Buland *
3 * eichlan@Xagafinelle *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 ***************************************************************************/
10#ifndef TOKENSTRING_H
11#define TOKENSTRING_H
12
13#include "linkedlist.h"
14
15/** A single tokenized command line. Contains all information necesarry to
16 * nicely access a stand-alone command line and to perform alias expansion
17 * inside of that command line.
18 * When expanding a token, the original command line is left intact, so any
19 * command usng a command line verbatum (getTokenString not getToken) will get
20 * the original, and not the expanded version.
21 * Since indexing into the original command line is also done by token, it
22 * means that using getTokenString( 0 ) will not always get you the first
23 * character of the command line, it will get you the first non-whitespace
24 * character.
25 * Furthermore, when expanding the expantion string is tokenized as well,
26 * but since the original string is unchanged, all tokens that expand any
27 * given index will all retain the same index into the original command line.
28 *@todo Update this to allow it to break on different types of token
29 * delimiters.
30 *@author Mike Buland
31 */
32class TokenString{
33public:
34 /** Automatically call parseLine when created.
35 *@param lpNewTokenString The command line to tokenize
36 *@author Mike Buland
37 */
38 TokenString( const char *lpNewTokenString=NULL );
39 ~TokenString();
40
41 /** Performs a tokenizing parse on the given command line, setting it as
42 * the internal command line for all future tokenizing (excluding
43 * expansion)
44 *@param lpNewTokenString The new command line to set to this object.
45 *@author Mike Buland
46 */
47 void parseLine( const char *lpNewTokenString );
48
49 /** Appends a token to the list of available tokens. This references the
50 * internal pointer to the command line, so no token string must be
51 * specified.
52 *@param nStart The first character of the token to insert.
53 *@param nEnd The last character of the token to insert.
54 *@author Mike Buland
55 */
56 void appendToken( int nStart, int nEnd );
57
58 /** Gets the number of tokens. This is particularly useful post-aliasing
59 * since the number of tokens may not match what is percieved from the
60 * original command line.
61 *@returns The number of available tokens.
62 *@author Mike Buland
63 */
64 int getNumTokens();
65
66 /** Gets a processed token specified by index.
67 *@param nIndex The index of the token to retrieve.
68 *@returns A pointer to the requested token. Please note that these tokens
69 * may not match the original command line.
70 *@author Mike Buland
71 */
72 char *getToken( int nIndex );
73
74 /** Gets the original command line based on tokens. Use this if you want
75 * to perform your own processing on parts of the command line, without
76 * resorting to tokens.
77 * The first character in the returned string will always be
78 * non-whitespace.
79 *@param nIndex The index of the token to start at, zero gets you the whole
80 * command line.
81 *@returns A pointer to the internal original command line string, starting
82 * at the position of the first non-whitespace character of the token
83 * specified.
84 *@author Mike Buland
85 */
86 char *getTokenString( int nIndex=0 );
87
88 /** Expands a token, replacing it with the string lpNewToken, but
89 * processing the new string for tokens before performing the replacement
90 *@param nIndex Which token should be replaced.
91 *@param lpNewToken The string to replace the token with.
92 *@author Mike Buland
93 */
94 void expandTokenTo( int nIndex, char *lpNewToken );
95
96 /** Inserts a token at any position in the command line. This does not
97 * effect the original command line.
98 *@param nStart The start of the token in the string lpNewToken. (inclusive)
99 *@param nEnd The end of the token in the string lpToken. (inclusive)
100 *@param lpOldOrig The pointer to the position in the orginal command
101 * line where this new token should point.
102 *@param lpNewToken The string containing the new token. May contain more
103 * than just one token.
104 *@param nIndex The position to insert the token to.
105 *@author Mike Buland
106 */
107 void insertToken( int nStart, int nEnd, char *lpOldOrig, const char *lpNewToken, int nIndex );
108
109private:
110 char *lpTokenString;
111 LinkedList lToken;
112
113 typedef struct Token
114 {
115 char *lpOrig; // This is just a pointer back to lpTokenString
116 char *lpToken; // This is really a whole token
117 } Token;
118};
119
120#endif
diff --git a/src/xmldocument.cpp b/src/xmldocument.cpp
new file mode 100644
index 0000000..234ff81
--- /dev/null
+++ b/src/xmldocument.cpp
@@ -0,0 +1,142 @@
1#include <stdio.h>
2#include <stdlib.h>
3#include "xmlwriter.h"
4
5XmlDocument::XmlDocument( XmlNode *pRoot )
6{
7 this->pRoot = pRoot;
8 pCurrent = NULL;
9 bCompleted = (pRoot!=NULL);
10}
11
12XmlDocument::~XmlDocument()
13{
14 if( pRoot )
15 {
16 delete pRoot;
17 }
18}
19
20void XmlDocument::addNode( const char *sName, const char *sContent, bool bClose )
21{
22 if( pRoot == NULL )
23 {
24 // This is the first node, so ignore position and just insert it.
25 pCurrent = pRoot = new XmlNode( sName, NULL, sContent );
26 }
27 else
28 {
29 pCurrent = pCurrent->addChild( sName, sContent );
30 }
31
32 if( bClose )
33 {
34 closeNode();
35 }
36}
37
38void XmlDocument::setName( const char *sName )
39{
40 pCurrent->setName( sName );
41}
42
43bool XmlDocument::isCompleted()
44{
45 return bCompleted;
46}
47
48XmlNode *XmlDocument::getRoot()
49{
50 return pRoot;
51}
52
53XmlNode *XmlDocument::getCurrent()
54{
55 return pCurrent;
56}
57
58void XmlDocument::closeNode()
59{
60 if( pCurrent != NULL )
61 {
62 pCurrent = pCurrent->getParent();
63
64 if( pCurrent == NULL )
65 {
66 bCompleted = true;
67 }
68 }
69}
70
71void XmlDocument::addProperty( const char *sName, const char *sValue )
72{
73 if( pCurrent )
74 {
75 pCurrent->addProperty( sName, sValue );
76 }
77}
78
79void XmlDocument::addProperty( const char *sName, const unsigned char nValue )
80{
81 char buf[12];
82 sprintf( buf, "%hhi", nValue );
83 addProperty( sName, buf );
84}
85
86void XmlDocument::addProperty( const char *sName, const char nValue )
87{
88 char buf[12];
89 sprintf( buf, "%hhi", nValue );
90 addProperty( sName, buf );
91}
92
93void XmlDocument::addProperty( const char *sName, const unsigned short nValue )
94{
95 char buf[12];
96 sprintf( buf, "%hi", nValue );
97 addProperty( sName, buf );
98}
99
100void XmlDocument::addProperty( const char *sName, const short nValue )
101{
102 char buf[12];
103 sprintf( buf, "%hi", nValue );
104 addProperty( sName, buf );
105}
106
107void XmlDocument::addProperty( const char *sName, const int nValue )
108{
109 char buf[12];
110 sprintf( buf, "%li", nValue );
111 addProperty( sName, buf );
112}
113
114void XmlDocument::addProperty( const char *sName, const unsigned long nValue )
115{
116 char buf[12];
117 sprintf( buf, "%li", nValue );
118 addProperty( sName, buf );
119}
120
121void XmlDocument::addProperty( const char *sName, const long nValue )
122{
123 char buf[12];
124 sprintf( buf, "%li", nValue );
125 addProperty( sName, buf );
126}
127
128void XmlDocument::addProperty( const char *sName, const double dValue )
129{
130 char buf[40];
131 sprintf( buf, "%f", dValue );
132 addProperty( sName, buf );
133}
134
135void XmlDocument::setContent( const char *sContent )
136{
137 if( pCurrent )
138 {
139 pCurrent->setContent( sContent );
140 }
141}
142
diff --git a/src/xmldocument.h b/src/xmldocument.h
new file mode 100644
index 0000000..f9a8606
--- /dev/null
+++ b/src/xmldocument.h
@@ -0,0 +1,163 @@
1#ifndef XMLDOCUMENT
2#define XMLDOCUMENT
3
4#include "xmlnode.h"
5
6/**
7 * Keeps track of an easily managed set of XmlNode information. Allows simple
8 * operations for logical writing to and reading from XML structures. Using
9 * already formed structures is simply done through the XmlNode structures,
10 * and the getRoot function here. Creation is performed through a simple set
11 * of operations that creates the data in a stream type format.
12 *@author Mike Buland
13 */
14class XmlDocument
15{
16public:
17 /**
18 * Construct either a blank XmlDocuemnt or construct a document around an
19 * existing XmlNode. Be careful, once an XmlNode is passed into a document
20 * the document takes over ownership and will delete it when the XmlDocument
21 * is deleted.
22 *@param pRoot The XmlNode to use as the root of this document, or NULL if
23 * you want to start a new document.
24 */
25 XmlDocument( XmlNode *pRoot=NULL );
26
27 /**
28 * Destroy all contained nodes.
29 */
30 ~XmlDocument();
31
32 /**
33 * Add a new node to the document. The new node is appended to the end of
34 * the current context, i.e. XmlNode, and the new node, provided it isn't
35 * close as part of this operation, will become the current context.
36 *@param sName The name of the new node to add.
37 *@param sContent A content string to be placed inside of the new node.
38 *@param bClose Set this to true to close the node immediately after adding
39 * the node and setting the content and name. If this is set to true the
40 * node is appended, but the context node doesn't change.
41 */
42 void addNode( const char *sName=NULL, const char *sContent=NULL, bool bClose=false );
43
44 /**
45 * Set the name of the current node context.
46 *@param sName The new name of the node.
47 */
48 void setName( const char *sName );
49
50 /**
51 * Close the current node context. This will move the current context to
52 * the parent node of the former current node. If the current node was the
53 * root then the "completed" flag is set and no more operations are allowed.
54 */
55 void closeNode();
56
57 /**
58 * Change the content of the current node at the current position between
59 * nodes.
60 *@param sContent The new content of the current node.
61 */
62 void setContent( const char *sContent );
63
64 /**
65 * Add a named property to the current context node.
66 *@param sName The name of the property to add.
67 *@param sValue The string value of the property.
68 */
69 void addProperty( const char *sName, const char *sValue );
70
71 /**
72 * Add a named property to the current context node, converting the
73 * numerical parameter to text using standrd printf style conversion.
74 *@param sName The name of the property to add.
75 *@param nValue The numerical value to add.
76 */
77 void addProperty( const char *sName, const unsigned char nValue );
78
79 /**
80 * Add a named property to the current context node, converting the
81 * numerical parameter to text using standrd printf style conversion.
82 *@param sName The name of the property to add.
83 *@param nValue The numerical value to add.
84 */
85 void addProperty( const char *sName, const char nValue );
86
87 /**
88 * Add a named property to the current context node, converting the
89 * numerical parameter to text using standrd printf style conversion.
90 *@param sName The name of the property to add.
91 *@param nValue The numerical value to add.
92 */
93 void addProperty( const char *sName, const unsigned short nValue );
94
95 /**
96 * Add a named property to the current context node, converting the
97 * numerical parameter to text using standrd printf style conversion.
98 *@param sName The name of the property to add.
99 *@param nValue The numerical value to add.
100 */
101 void addProperty( const char *sName, const short nValue );
102
103 /**
104 * Add a named property to the current context node, converting the
105 * numerical parameter to text using standrd printf style conversion.
106 *@param sName The name of the property to add.
107 *@param nValue The numerical value to add.
108 */
109 void addProperty( const char *sName, const unsigned long nValue );
110
111 /**
112 * Add a named property to the current context node, converting the
113 * numerical parameter to text using standrd printf style conversion.
114 *@param sName The name of the property to add.
115 *@param nValue The numerical value to add.
116 */
117 void addProperty( const char *sName, const long nValue );
118
119 /**
120 * Add a named property to the current context node, converting the
121 * numerical parameter to text using standrd printf style conversion.
122 *@param sName The name of the property to add.
123 *@param nValue The numerical value to add.
124 */
125 void addProperty( const char *sName, const int nValue );
126
127 /**
128 * Add a named property to the current context node, converting the
129 * numerical parameter to text using standrd printf style conversion.
130 *@param sName The name of the property to add.
131 *@param dValue The numerical value to add.
132 */
133 void addProperty( const char *sName, const double dValue );
134
135 /**
136 * The XmlDocuemnt is considered completed if the root node has been closed.
137 * Once an XmlDocument has been completed, you can no longer perform
138 * operations on it.
139 *@return True if completed, false if still in progress.
140 */
141 bool isCompleted();
142
143 /**
144 * Get a pointer to the root object of this XmlDocument.
145 *@returns A pointer to an internally owned XmlNode. Do not delete this
146 * XmlNode.
147 */
148 XmlNode *getRoot();
149
150 /**
151 * Get the current context node, which could be the same as the root node.
152 *@returns A pointer to an internally owned XmlNode. Do not delete this
153 * XmlNode.
154 */
155 XmlNode *getCurrent();
156
157private:
158 XmlNode *pRoot; /**< The root node. */
159 XmlNode *pCurrent; /**< The current node. */
160 bool bCompleted; /**< Is it completed? */
161};
162
163#endif
diff --git a/src/xmlfilereader.cpp b/src/xmlfilereader.cpp
new file mode 100644
index 0000000..216c08a
--- /dev/null
+++ b/src/xmlfilereader.cpp
@@ -0,0 +1,63 @@
1#include "xmlfilereader.h"
2#include <string.h>
3
4XmlFileReader::XmlFileReader( const char *sFile, bool bStrip )
5 : XmlReader( bStrip )
6{
7 fh = fopen( sFile, "rt" );
8
9 if( fh == NULL )
10 {
11 reportError("Couldn't open file.");
12 //nError = 1;
13 }
14 else
15 {
16 char buf[50];
17 fgets( buf, 50, fh );
18
19 if( !strcmp( buf, "<?xml version=\"1.0\"?>\n" ) )
20 {
21 buildDoc();
22 }
23 }
24}
25
26XmlFileReader::~XmlFileReader()
27{
28}
29
30char XmlFileReader::getChar( int nIndex )
31{
32 // Make sure we always have a little data left in the buffer
33 if( fbDataIn.getLength() <= nIndex+1 && fh )
34 {
35 int nBytes = fbDataIn.getCapacity()-1;
36 char *buf = new char[nBytes];
37 int nRead = fread( buf, 1, nBytes, fh );
38 fbDataIn.appendData( buf, nRead );
39 delete[] buf;
40
41 if( nRead < nBytes )
42 {
43 fclose( fh );
44 fh = NULL;
45 }
46 }
47 if( fbDataIn.getLength() >= nIndex+1 )
48 {
49 return fbDataIn.getData()[nIndex];
50 }
51 else
52 {
53 return '\0';
54 }
55}
56
57void XmlFileReader::usedChar()
58{
59 if( fbDataIn.getLength() > 0 )
60 {
61 fbDataIn.usedData( 1 );
62 }
63}
diff --git a/src/xmlfilereader.h b/src/xmlfilereader.h
new file mode 100644
index 0000000..3e996e6
--- /dev/null
+++ b/src/xmlfilereader.h
@@ -0,0 +1,47 @@
1#ifndef XMLFILEREADER
2#define XMLFILEREADER
3
4#include <stdio.h>
5#include "xmlreader.h"
6#include "flexbuf.h"
7
8/**
9 * Takes care of reading in xml formatted data from a file. This could/should
10 * be made more arbitrary in the future so that we can read the data from any
11 * source. This is actually made quite simple already since all data read in
12 * is handled by one single helper function and then palced into a FlexBuf for
13 * easy access by the other functions. The FlexBuf also allows for block
14 * reading from disk, which improves speed by a noticable amount.
15 * <br>
16 * There are also some extra features implemented that allow you to break the
17 * standard XML reader specs and eliminate leading and trailing whitespace in
18 * all read content. This is useful in situations where you allow additional
19 * whitespace in the files to make them easily human readable. The resturned
20 * content will be NULL in sitautions where all content between nodes was
21 * stripped.
22 *@author Mike Buland
23 */
24class XmlFileReader : public XmlReader
25{
26public:
27 /**
28 * Construct an XmlReader around an xml file on your file system.
29 *@param sFile The file to read.
30 *@param bStrip Set to true to strip out leading and trailing whitespace in
31 * node contents.
32 */
33 XmlFileReader( const char *sFile, bool bStrip=false );
34
35 /**
36 * Destroy the reader and cleanup.
37 */
38 ~XmlFileReader();
39
40private:
41 char getChar( int nIndex = 0 );
42 void usedChar();
43 FILE *fh; /**< The file handle. */
44 FlexBuf fbDataIn; /**< The input buffer. */
45};
46
47#endif
diff --git a/src/xmlfilewriter.cpp b/src/xmlfilewriter.cpp
new file mode 100644
index 0000000..b62fb11
--- /dev/null
+++ b/src/xmlfilewriter.cpp
@@ -0,0 +1,22 @@
1#include <stdio.h>
2#include <stdlib.h>
3#include "xmlfilewriter.h"
4
5XmlFileWriter::XmlFileWriter( const char *sFileName, const char *sIndent, XmlNode *pRoot ) :
6 XmlWriter( sIndent, pRoot )
7{
8 this->sFileName = sFileName;
9 fh = fopen( sFileName, "wt");
10 fprintf( fh, "<?xml version=\"1.0\"?>\n");
11}
12
13XmlFileWriter::~XmlFileWriter()
14{
15 fclose( fh );
16}
17
18void XmlFileWriter::writeString( const char *sString )
19{
20 fprintf( fh, sString );
21}
22
diff --git a/src/xmlfilewriter.h b/src/xmlfilewriter.h
new file mode 100644
index 0000000..97b3e00
--- /dev/null
+++ b/src/xmlfilewriter.h
@@ -0,0 +1,44 @@
1#ifndef XML_FILE_WRITER
2#define XML_FILE_WRITER
3
4#include "xmlnode.h"
5#include "xmlwriter.h"
6
7/**
8 * Implements xml writing in the XML standard format. Also allows you to
9 * break that format and auto-indent your exported xml data for ease of
10 * reading. The auto-indenting will only be applied to sections that
11 * have no content of their own already. This means that except for
12 * whitespace all of your data will be preserved perfectly.
13 * You can create an XmlWriter object around a file, or access the static
14 * write function directly and just hand it a filename and a root XmlNode.
15 * When using an XmlWriter object the interface is identicle to that of
16 * the XmlDocument class, so reference that class for API info. However
17 * when the initial (or root) node is closed, and the document is finished
18 * the file will be created and written to automatically. The user can
19 * check to see if this is actually true by calling the isFinished
20 * function in the XmlDocument class.
21 *@author Mike Buland
22 */
23class XmlFileWriter : public XmlWriter
24{
25public:
26 /**
27 * Construct a file writer around a given file.
28 *@param sFileName The file to create or overwrite and write XML into.
29 *@param sIndent The indent text to use, if any.
30 */
31 XmlFileWriter( const char *sFileName, const char *sIndent=NULL, XmlNode *pRoot=NULL );
32
33 /**
34 * Destroy the writer.
35 */
36 ~XmlFileWriter();
37
38private:
39 void writeString( const char *sString );
40 std::string sFileName; /**< The filename to write to. */
41 FILE *fh; /**< The file handle to the open file. */
42};
43
44#endif
diff --git a/src/xmlnode.cpp b/src/xmlnode.cpp
new file mode 100644
index 0000000..e5c77e2
--- /dev/null
+++ b/src/xmlnode.cpp
@@ -0,0 +1,454 @@
1#include "xmlnode.h"
2#include "hashfunctionstring.h"
3
4XmlNode::XmlNode( const char *sName, XmlNode *pParent, const char *sContent ) :
5 hProperties( new HashFunctionString(), 53, false ),
6 hChildren( new HashFunctionString(), 53, true )
7{
8 this->pParent = pParent;
9 if( sName != NULL )
10 {
11 setName( sName );
12 }
13 if( sContent != NULL )
14 {
15 this->sPreContent = new std::string( sContent );
16 }
17 else
18 {
19 this->sPreContent = NULL;
20 }
21 nCurContent = 0;
22}
23
24XmlNode::~XmlNode()
25{
26 for( int j = 0; j < lChildren.getSize(); j++ )
27 {
28 delete (XmlNode *)lChildren[j];
29 }
30 for( int j = 0; j < lPropNames.getSize(); j++ )
31 {
32 delete (std::string *)lPropNames[j];
33 }
34 for( int j = 0; j < lPropValues.getSize(); j++ )
35 {
36 delete (std::string *)lPropValues[j];
37 }
38 for( int j = 0; j < lPostContent.getSize(); j++ )
39 {
40 if( lPostContent[j] != NULL )
41 {
42 delete (std::string *)lPostContent[j];
43 }
44 }
45 if( sPreContent )
46 {
47 delete sPreContent;
48 }
49}
50
51void XmlNode::setName( const char *sName )
52{
53 if( pParent )
54 {
55 if( this->sName.size() == 0 )
56 {
57 // We're not in the hash yet, so add us
58 this->sName = sName;
59 pParent->hChildren.insert( this->sName.c_str(), this );
60 }
61 else
62 {
63 // Slightly more tricky, delete us, then add us...
64 pParent->hChildren.del( this->sName.c_str() );
65 this->sName = sName;
66 pParent->hChildren.insert( this->sName.c_str(), this );
67 }
68 }
69 else
70 {
71 // If we have no parent, then just set the name string, we don't need
72 // to worry about hashing.
73 this->sName = sName;
74 }
75}
76
77void XmlNode::setContent( const char *sContent, int nIndex )
78{
79 if( nIndex == -1 )
80 {
81 nIndex = nCurContent;
82 }
83 if( nIndex == 0 )
84 {
85 if( this->sPreContent )
86 {
87 delete this->sPreContent;
88 }
89
90 this->sPreContent = new std::string( sContent );
91 }
92 else
93 {
94 nIndex--;
95 if( lPostContent[nIndex] )
96 {
97 delete (std::string *)lPostContent[nIndex];
98 }
99
100 lPostContent.setAt( nIndex, new std::string( sContent ) );
101 }
102}
103
104const char *XmlNode::getContent( int nIndex )
105{
106 if( nIndex == 0 )
107 {
108 if( sPreContent )
109 {
110 return sPreContent->c_str();
111 }
112 }
113 else
114 {
115 nIndex--;
116 if( lPostContent[nIndex] )
117 {
118 return ((std::string *)lPostContent[nIndex])->c_str();
119 }
120 }
121
122 return NULL;
123}
124
125XmlNode *XmlNode::addChild( const char *sName, const char *sContent )
126{
127 return addChild( new XmlNode( sName, this, sContent ) );
128}
129
130XmlNode *XmlNode::addChild( XmlNode *pNode )
131{
132 lChildren.append( pNode );
133 lPostContent.append( NULL );
134 nCurContent++;
135 pNode->pParent = this;
136
137 return pNode;
138}
139
140XmlNode *XmlNode::getParent()
141{
142 return pParent;
143}
144
145void XmlNode::addProperty( const char *sName, const char *sValue )
146{
147 std::string *pName = new std::string( sName );
148 std::string *pValue = new std::string( sValue );
149
150 hProperties.insert( pName->c_str(), pValue->c_str() );
151 lPropNames.append( pName );
152 lPropValues.append( pValue );
153}
154
155int XmlNode::getNumProperties()
156{
157 return lPropNames.getSize();
158}
159
160const char *XmlNode::getPropertyName( int nIndex )
161{
162 std::string *tmp = ((std::string *)lPropNames[nIndex]);
163 if( tmp == NULL )
164 return NULL;
165 return tmp->c_str();
166}
167
168const char *XmlNode::getProperty( int nIndex )
169{
170 std::string *tmp = ((std::string *)lPropValues[nIndex]);
171 if( tmp == NULL )
172 return NULL;
173 return tmp->c_str();
174}
175
176const char *XmlNode::getProperty( const char *sName )
177{
178 const char *tmp = (const char *)hProperties[sName];
179 if( tmp == NULL )
180 return NULL;
181 return tmp;
182}
183
184bool XmlNode::deleteProperty( int nIndex )
185{
186 hProperties.del( ((std::string *)lPropNames[nIndex])->c_str() );
187
188 delete (std::string *)lPropNames[nIndex];
189 delete (std::string *)lPropValues[nIndex];
190
191 lPropNames.deleteAt( nIndex );
192 lPropValues.deleteAt( nIndex );
193}
194
195bool XmlNode::hasChildren()
196{
197 return lChildren.getSize()>0;
198}
199
200int XmlNode::getNumChildren()
201{
202 return lChildren.getSize();
203}
204
205XmlNode *XmlNode::getChild( int nIndex )
206{
207 return (XmlNode *)lChildren[nIndex];
208}
209
210XmlNode *XmlNode::getChild( const char *sName, int nSkip )
211{
212 return (XmlNode *)hChildren.get( sName, nSkip );
213}
214
215const char *XmlNode::getName()
216{
217 return sName.c_str();
218}
219
220bool XmlNode::deleteNode( int nIndex, const char *sReplacementText )
221{
222 XmlNode *xRet = detatchNode( nIndex, sReplacementText );
223
224 if( xRet == NULL )
225 {
226 return false;
227 }
228 else
229 {
230 delete xRet;
231 return true;
232 }
233}
234
235XmlNode *XmlNode::detatchNode( int nIndex, const char *sReplacementText )
236{
237 if( nIndex < 0 || nIndex >= lChildren.getSize() )
238 return NULL;
239
240 // The real trick when deleteing a node isn't actually deleting it, it's
241 // reforming the content around the node that's now missing...hmmm...
242
243 if( nIndex == 0 )
244 {
245 // If the index is zero we have to deal with the pre-content
246 if( sReplacementText )
247 {
248 if( sPreContent == NULL )
249 {
250 sPreContent = new std::string( sReplacementText );
251 }
252 else
253 {
254 *sPreContent += sReplacementText;
255 }
256 }
257 if( lPostContent.getSize() > 0 )
258 {
259 if( lPostContent[0] != NULL )
260 {
261 if( sPreContent == NULL )
262 {
263 sPreContent = new std::string(
264 ((std::string *)lPostContent[0])->c_str()
265 );
266 }
267 else
268 {
269 *sPreContent +=
270 ((std::string *)lPostContent[0])->c_str();
271 }
272 }
273 delete (std::string *)lPostContent[0];
274 lPostContent.deleteAt( 0 );
275 }
276 }
277 else
278 {
279 int nCont = nIndex-1;
280 // If it's above zero we deal with the post-content only
281 if( sReplacementText )
282 {
283 if( lPostContent[nCont] == NULL )
284 {
285 lPostContent.setAt( nCont, new std::string( sReplacementText ) );
286 }
287 else
288 {
289 *((std::string *)lPostContent[nCont]) += sReplacementText;
290 }
291 }
292 if( lPostContent.getSize() > nIndex )
293 {
294 if( lPostContent[nIndex] != NULL )
295 {
296 if( lPostContent[nCont] == NULL )
297 {
298 lPostContent.setAt( nCont, new std::string(
299 ((std::string *)lPostContent[nIndex])->c_str()
300 ) );
301 }
302 else
303 {
304 *((std::string *)lPostContent[nCont]) +=
305 ((std::string *)lPostContent[nIndex])->c_str();
306 }
307 }
308 delete (std::string *)lPostContent[nIndex];
309 lPostContent.deleteAt( nIndex );
310 }
311 }
312
313 XmlNode *xRet = (XmlNode *)lChildren[nIndex];
314 hChildren.del( ((XmlNode *)lChildren[nIndex])->getName() );
315 lChildren.deleteAt( nIndex );
316
317 return xRet;
318}
319
320bool XmlNode::replaceNode( int nIndex, XmlNode *pNewNode )
321{
322 if( nIndex < 0 || nIndex >= lChildren.getSize() )
323 return false;
324
325 delete (XmlNode *)lChildren[nIndex];
326 lChildren.setAt( nIndex, pNewNode );
327 pNewNode->pParent = this;
328
329 return true;
330}
331
332XmlNode *XmlNode::getCopy()
333{
334 XmlNode *pNew = new XmlNode();
335
336 pNew->sName = sName;
337 if( sPreContent )
338 {
339 pNew->sPreContent = new std::string( sPreContent->c_str() );
340 }
341 else
342 {
343 pNew->sPreContent = NULL;
344 }
345 pNew->nCurContent = 0;
346
347 int nSize = lPostContent.getSize();
348 pNew->lPostContent.setSize( nSize );
349 for( int j = 0; j < nSize; j++ )
350 {
351 if( lPostContent[j] )
352 {
353 pNew->lPostContent.setAt(
354 j, new std::string(
355 ((std::string *)lPostContent[j])->c_str()
356 )
357 );
358 }
359 else
360 {
361 pNew->lPostContent.setAt( j, NULL );
362 }
363 }
364
365 nSize = lChildren.getSize();
366 pNew->lChildren.setSize( nSize );
367 for( int j = 0; j < nSize; j++ )
368 {
369 XmlNode *pChild = ((XmlNode *)lChildren[j])->getCopy();
370 pNew->lChildren.setAt( j, pChild );
371 pChild->pParent = this;
372 hChildren.insert( pChild->getName(), pChild );
373 }
374
375 nSize = lPropNames.getSize();
376 pNew->lPropNames.setSize( nSize );
377 pNew->lPropValues.setSize( nSize );
378 for( int j = 0; j < nSize; j++ )
379 {
380 std::string *pProp = new std::string( ((std::string *)lPropNames[j])->c_str() );
381 std::string *pVal = new std::string( ((std::string *)lPropValues[j])->c_str() );
382 pNew->lPropNames.setAt( j, pProp );
383 pNew->lPropValues.setAt( j, pVal );
384 pNew->hProperties.insert( pProp->c_str(), pVal->c_str() );
385 pNew->nCurContent++;
386 }
387
388 return pNew;
389}
390
391bool XmlNode::deleteNodeKeepChildren( int nIndex )
392{
393 // This is a tricky one...we need to do some patching to keep things all
394 // even...
395 XmlNode *xRet = (XmlNode *)lChildren[nIndex];
396
397 if( xRet == NULL )
398 {
399 return false;
400 }
401 else
402 {
403 if( getContent( nIndex ) )
404 {
405 std::string sBuf( getContent( nIndex ) );
406 sBuf += xRet->getContent( 0 );
407 setContent( sBuf.c_str(), nIndex );
408 }
409 else
410 {
411 setContent( xRet->getContent( 0 ), nIndex );
412 }
413
414 int nSize = xRet->lChildren.getSize();
415 for( int j = 0; j < nSize; j++ )
416 {
417 XmlNode *pCopy = ((XmlNode *)xRet->lChildren[j])->getCopy();
418 pCopy->pParent = this;
419 lChildren.insertBefore( pCopy, nIndex+j );
420
421 if( xRet->lPostContent[j] )
422 {
423 lPostContent.insertBefore(
424 new std::string( ((std::string *)xRet->lPostContent[j])->c_str() ),
425 nIndex+j
426 );
427 }
428 else
429 {
430 lPostContent.insertBefore( NULL, nIndex+j );
431 }
432 }
433
434 if( getContent( nIndex+nSize ) )
435 {
436 //SString sBuf( getContent( nIndex+nSize ) );
437 //sBuf.catfrom( xRet->getContent( nSize ) );
438 //setContent( sBuf, nIndex+nSize );
439 }
440 else
441 {
442 setContent( xRet->getContent( nSize ), nIndex+nSize );
443 }
444
445 deleteNode( nIndex+nSize );
446 return true;
447 }
448
449}
450
451bool XmlNode::replaceNodeWithChildren( int nIndex, XmlNode *pNewNode )
452{
453}
454
diff --git a/src/xmlnode.h b/src/xmlnode.h
new file mode 100644
index 0000000..2b01226
--- /dev/null
+++ b/src/xmlnode.h
@@ -0,0 +1,236 @@
1#ifndef XMLNODE
2#define XMLNODE
3
4#include <iostream>
5#include "linkedlist.h"
6#include "hashtable.h"
7
8/**
9 * Maintains all data pertient to an XML node, including sub-nodes and content.
10 * All child nodes can be accessed through index and through name via a hash
11 * table. This makes it very easy to gain simple and fast access to all of
12 * your data. For most applications, the memory footprint is also rather
13 * small. While XmlNode objects can be used directly to create XML structures
14 * it is highly reccomended that all operations be performed through the
15 * XmlDocument class.
16 *@author Mike Buland
17 */
18class XmlNode
19{
20public:
21 /**
22 * Construct a new XmlNode.
23 *@param sName The name of the node.
24 *@param pParent The parent node.
25 *@param sContent The initial content string.
26 */
27 XmlNode(
28 const char *sName=NULL,
29 XmlNode *pParent = NULL,
30 const char *sContent=NULL
31 );
32
33 /**
34 * Delete the node and cleanup all memory.
35 */
36 ~XmlNode();
37
38 /**
39 * Change the name of the node.
40 *@param sName The new name of the node.
41 */
42 void setName( const char *sName );
43
44 /**
45 * Construct a new node and add it as a child to this node, also return a
46 * pointer to the newly constructed node.
47 *@param sName The name of the new node.
48 *@param sContent The initial content of the new node.
49 *@returns A pointer to the newly created child node.
50 */
51 XmlNode *addChild( const char *sName, const char *sContent=NULL );
52
53 /**
54 * Add an already created XmlNode as a child to this node. The new child
55 * XmlNode's parent will be changed appropriately and the parent XmlNode
56 * will take ownership of the child.
57 *@param pChild The child XmlNode to add to this XmlNode.
58 *@returns A pointer to the child node that was just added.
59 */
60 XmlNode *addChild( XmlNode *pChild );
61
62 /**
63 * Add a new property to the XmlNode. Properties are name/value pairs.
64 *@param sName The name of the property. Specifying a name that's already
65 * in use will overwrite that property.
66 *@param sValue The textual value of the property.
67 */
68 void addProperty( const char *sName, const char *sValue );
69
70 /**
71 * Get a pointer to the parent node, if any.
72 *@returns A pointer to the node's parent, or NULL if there isn't one.
73 */
74 XmlNode *getParent();
75
76 /**
77 * Tells you if this node has children.
78 *@returns True if this node has at least one child, false otherwise.
79 */
80 bool hasChildren();
81
82 /**
83 * Tells you how many children this node has.
84 *@returns The number of children this node has.
85 */
86 int getNumChildren();
87
88 /**
89 * Get a child node at a specific index.
90 *@param nIndex The zero-based index of the child to retreive.
91 *@returns A pointer to the child, or NULL if you requested an invalid
92 * index.
93 */
94 XmlNode *getChild( int nIndex );
95
96 /**
97 * Get a child with the specified name, and possibly skip value. For an
98 * explination of skip values see the HashTable.
99 *@param sName The name of the child to find.
100 *@param nSkip The number of nodes with that name to skip.
101 *@returns A pointer to the child, or NULL if no child with that name was
102 * found.
103 */
104 XmlNode *getChild( const char *sName, int nSkip=0 );
105
106 /**
107 * Get a pointer to the name of this node. Do not change this, use setName
108 * instead.
109 *@returns A pointer to the name of this node.
110 */
111 const char *getName();
112
113 /**
114 * Set the content of this node, optionally at a specific index. Using the
115 * default of -1 will set the content after the last added node.
116 *@param sContent The content string to use.
117 *@param nIndex The index of the content.
118 */
119 void setContent( const char *sContent, int nIndex=-1 );
120
121 /**
122 * Get the content string at a given index, or zero for initial content.
123 *@param nIndex The index of the content.
124 *@returns A pointer to the content at that location.
125 */
126 const char *getContent( int nIndex = 0 );
127
128 /**
129 * Get the number of properties in this node.
130 *@returns The number of properties in this node.
131 */
132 int getNumProperties();
133
134 /**
135 * Get a property's name by index.
136 *@param nIndex The index of the property to examine.
137 *@returns A pointer to the name of the property specified, or NULL if none
138 * found.
139 */
140 const char *getPropertyName( int nIndex );
141
142 /**
143 * Get a proprty's value by index.
144 *@param nIndex The index of the property to examine.
145 *@returns A pointer to the value of the property specified, or NULL if none
146 * found.
147 */
148 const char *getProperty( int nIndex );
149
150 /**
151 * Get a propery's value by name.
152 *@param sName The name of the property to examine.
153 *@returns A pointer to the value of the property specified, or NULL if none
154 * found.
155 */
156 const char *getProperty( const char *sName );
157
158 /**
159 * Delete a property by index.
160 *@param nIndex The index of the property to delete.
161 *@returns True if the property was found and deleted, false if it wasn't
162 * found.
163 */
164 bool deleteProperty( int nIndex );
165
166 /**
167 * Delete a child node, possibly replacing it with some text. This actually
168 * fixes all content strings around the newly deleted child node.
169 *@param nIndex The index of the node to delete.
170 *@param sReplacementText The optional text to replace the node with.
171 *@returns True of the node was found, and deleted, false if it wasn't
172 * found.
173 */
174 bool deleteNode( int nIndex, const char *sReplacementText = NULL );
175
176 /**
177 * Delete a given node, but move all of it's children and content up to
178 * replace the deleted node. All of the content of the child node is
179 * spliced seamlessly into place with the parent node's content.
180 *@param nIndex The node to delete.
181 *@returns True if the node was found and deleted, false if it wasn't.
182 */
183 bool deleteNodeKeepChildren( int nIndex );
184
185 /**
186 * Detatch a given child node from this node. This effectively works just
187 * like a deleteNode, except that instead of deleting the node it is removed
188 * and returned, and all ownership is given up.
189 *@param nIndex The index of the node to detatch.
190 *@param sReplacementText The optional text to replace the detatched node
191 * with.
192 *@returns A pointer to the newly detatched node, which then passes
193 * ownership to the caller.
194 */
195 XmlNode *detatchNode( int nIndex, const char *sReplacementText = NULL );
196
197 /**
198 * Replace a given node with a different node that is not currently owned by
199 * this XmlNode or any ancestor.
200 *@param nIndex The index of the node to replace.
201 *@param pNewNode The new node to replace the old node with.
202 *@returns True if the node was found and replaced, false if it wasn't.
203 */
204 bool replaceNode( int nIndex, XmlNode *pNewNode );
205
206 /**
207 * Replace a given node with the children and content of a given node.
208 *@param nIndex The index of the node to replace.
209 *@param pNewNode The node that contains the children and content that will
210 * replace the node specified by nIndex.
211 *@returns True if the node was found and replaced, false if it wasn't.
212 */
213 bool replaceNodeWithChildren( int nIndex, XmlNode *pNewNode );
214
215 /**
216 * Get a copy of this node and all children. getCopy is recursive, so
217 * beware copying large trees of xml.
218 *@returns A newly created copy of this node and all of it's children.
219 */
220 XmlNode *getCopy();
221
222private:
223 std::string sName; /**< The name of the node. */
224 std::string *sPreContent; /**< The content that goes before any node. */
225 LinkedList lChildren; /**< The children. */
226 LinkedList lPostContent; /**< The content that comes after children. */
227 HashTable hProperties; /**< Property hashtable. */
228 HashTable hChildren; /**< Children hashtable. */
229 LinkedList lPropNames; /**< List of property names. */
230 LinkedList lPropValues; /**< List of property values. */
231 XmlNode *pParent; /**< A pointer to the parent of this node. */
232 int nCurContent; /**< The current content we're on, for using the -1 on
233 setContent. */
234};
235
236#endif
diff --git a/src/xmlreader.cpp b/src/xmlreader.cpp
new file mode 100644
index 0000000..bb24157
--- /dev/null
+++ b/src/xmlreader.cpp
@@ -0,0 +1,412 @@
1#include "xmlreader.h"
2#include <string.h>
3
4XmlReader::XmlReader( bool bStrip )
5{
6 nError = 0;
7 this->bStrip = bStrip;
8}
9
10XmlReader::~XmlReader()
11{
12}
13
14#define gcall( x ) if( x == false ) return false;
15
16bool XmlReader::isws( char chr )
17{
18 return ( chr == ' ' || chr == '\t' || chr == '\n' || chr == '\r' );
19}
20
21bool XmlReader::ws()
22{
23 while( true )
24 {
25 char chr = getChar();
26 if( isws( chr ) )
27 {
28 usedChar();
29 }
30 else
31 {
32 return true;
33 }
34 }
35 return true;
36}
37
38bool XmlReader::buildDoc()
39{
40 // take care of initial whitespace
41 gcall( ws() );
42 gcall( node() );
43
44 return true;
45}
46
47bool XmlReader::node()
48{
49 gcall( startNode() )
50
51 // At this point, we are closing the startNode
52 char chr = getChar();
53 if( chr == '>' )
54 {
55 usedChar();
56
57 // Now we process the guts of the node.
58 gcall( content() );
59 }
60 else if( chr == '/' )
61 {
62 // This is the tricky one, one more validation, then we close the node.
63 usedChar();
64 if( getChar() == '>' )
65 {
66 closeNode();
67 usedChar();
68 }
69 else
70 {
71 reportError("Close node in singleNode malformed!");
72 return false;
73 }
74 }
75 else
76 {
77 reportError("Close node expected, but not found.");
78 return false;
79 }
80
81 return true;
82}
83
84bool XmlReader::startNode()
85{
86 if( getChar() == '<' )
87 {
88 usedChar();
89
90 if( getChar() == '/' )
91 {
92 // Heh, it's actually a close node, go figure
93 FlexBuf fbName;
94 usedChar();
95 gcall( ws() );
96
97 while( true )
98 {
99 char chr = getChar();
100 if( isws( chr ) || chr == '>' )
101 {
102 // Here we actually compare the name we got to the name
103 // we already set, they have to match exactly.
104 if( !strcasecmp( getCurrent()->getName(), fbName.getData() ) )
105 {
106 closeNode();
107 break;
108 }
109 else
110 {
111 reportError("Got a mismatched node close tag.");
112 return false;
113 }
114 }
115 else
116 {
117 fbName.appendData( chr );
118 usedChar();
119 }
120 }
121
122 gcall( ws() );
123 if( getChar() == '>' )
124 {
125 // Everything is cool.
126 usedChar();
127 }
128 else
129 {
130 reportError("Got extra junk data instead of node close tag.");
131 return false;
132 }
133 }
134 else
135 {
136 // We're good, format is consistant
137 addNode();
138
139 // Skip extra whitespace
140 gcall( ws() );
141 gcall( name() );
142 gcall( ws() );
143 gcall( paramlist() );
144 gcall( ws() );
145 }
146 }
147 else
148 {
149 reportError("Expected to find node opening char, '<'.\n");
150 return false;
151 }
152
153 return true;
154}
155
156bool XmlReader::name()
157{
158 FlexBuf fbName;
159
160 while( true )
161 {
162 char chr = getChar();
163 if( isws( chr ) || chr == '>' || chr == '/' )
164 {
165 setName( fbName.getData() );
166 return true;
167 }
168 else
169 {
170 fbName.appendData( chr );
171 usedChar();
172 }
173 }
174
175 return true;
176}
177
178bool XmlReader::paramlist()
179{
180 while( true )
181 {
182 char chr = getChar();
183 if( chr == '/' || chr == '>' )
184 {
185 return true;
186 }
187 else
188 {
189 gcall( param() );
190 gcall( ws() );
191 }
192 }
193
194 return true;
195}
196
197char XmlReader::getEscape()
198{
199 // Right now, we just do # escapes...
200 if( getChar( 1 ) == '#' )
201 {
202 usedChar();
203 usedChar();
204 char buf[4];
205 int j = 0;
206 for( j = 0; getChar() != ';'; j++ )
207 {
208 buf[j] = getChar();
209 usedChar();
210 }
211 usedChar();
212 buf[j] = '\0';
213 return (char)atoi( buf );
214 }
215 else
216 {
217 return '\0';
218 }
219}
220
221bool XmlReader::param()
222{
223 FlexBuf fbName;
224 FlexBuf fbValue;
225
226 while( true )
227 {
228 char chr = getChar();
229 if( isws( chr ) || chr == '=' )
230 {
231 break;
232 }
233 else
234 {
235 fbName.appendData( chr );
236 usedChar();
237 }
238 }
239
240 gcall( ws() );
241
242 if( getChar() == '=' )
243 {
244 usedChar();
245
246 gcall( ws() );
247
248 char chr = getChar();
249 if( chr == '"' )
250 {
251 // Better quoted rhs
252 usedChar();
253
254 while( true )
255 {
256 chr = getChar();
257 if( chr == '"' )
258 {
259 usedChar();
260 addProperty( fbName.getData(), fbValue.getData() );
261 return true;
262 }
263 else
264 {
265 if( chr == '&' )
266 {
267 chr = getEscape();
268 if( chr == '\0' ) return false;
269 fbValue.appendData( chr );
270 }
271 else
272 {
273 fbValue.appendData( chr );
274 usedChar();
275 }
276 }
277 }
278 }
279 else
280 {
281 // Simple one-word rhs
282 while( true )
283 {
284 chr = getChar();
285 if( isws( chr ) || chr == '/' || chr == '>' )
286 {
287 addProperty( fbName.getData(), fbValue.getData() );
288 return true;
289 }
290 else
291 {
292 if( chr == '&' )
293 {
294 chr = getEscape();
295 if( chr == '\0' ) return false;
296 fbValue.appendData( chr );
297 }
298 else
299 {
300 fbValue.appendData( chr );
301 usedChar();
302 }
303 }
304 }
305 }
306 }
307 else
308 {
309 reportError("Expected an equals to seperate the params.");
310 return false;
311 }
312
313 return true;
314}
315
316bool XmlReader::content()
317{
318 FlexBuf fbContent;
319
320 if( bStrip ) gcall( ws() );
321
322 while( true )
323 {
324 char chr = getChar();
325 if( chr == '<' )
326 {
327 if( getChar(1) == '/' )
328 {
329 if( fbContent.getLength() > 0 )
330 {
331 if( bStrip )
332 {
333 int j;
334 for( j = fbContent.getLength()-1; isws(fbContent.getData()[j]); j-- );
335 ((char *)fbContent.getData())[j+1] = '\0';
336 }
337 setContent( fbContent.getData() );
338 }
339 usedChar();
340 usedChar();
341 gcall( ws() );
342 FlexBuf fbName;
343 while( true )
344 {
345 chr = getChar();
346 if( isws( chr ) || chr == '>' )
347 {
348 if( !strcasecmp( getCurrent()->getName(), fbName.getData() ) )
349 {
350 closeNode();
351 break;
352 }
353 else
354 {
355 reportError("Mismatched close tag found.");
356 return false;
357 }
358 }
359 else
360 {
361 fbName.appendData( chr );
362 usedChar();
363 }
364 }
365 gcall( ws() );
366 if( getChar() == '>' )
367 {
368 usedChar();
369 return true;
370 }
371 else
372 {
373 reportError("Malformed close tag.");
374 return false;
375 }
376 }
377 else
378 {
379 if( fbContent.getLength() > 0 )
380 {
381 if( bStrip )
382 {
383 int j;
384 for( j = fbContent.getLength()-1; isws(fbContent.getData()[j]); j-- );
385 ((char *)fbContent.getData())[j+1] = '\0';
386 }
387 setContent( fbContent.getData() );
388 fbContent.clearData();
389 }
390 gcall( node() );
391 }
392
393 if( bStrip ) gcall( ws() );
394 }
395 else
396 {
397 fbContent.appendData( chr );
398 usedChar();
399 }
400 }
401}
402
403void XmlReader::reportError( const char *sError )
404{
405 printf("XmlReader error: %s\n", sError );
406}
407
408int XmlReader::getError()
409{
410 return nError;
411}
412
diff --git a/src/xmlreader.h b/src/xmlreader.h
new file mode 100644
index 0000000..a8a81f0
--- /dev/null
+++ b/src/xmlreader.h
@@ -0,0 +1,133 @@
1#ifndef XMLREADER
2#define XMLREADER
3
4#include <stdio.h>
5#include "xmldocument.h"
6#include "flexbuf.h"
7
8/**
9 * Takes care of reading in xml formatted data from a file. This could/should
10 * be made more arbitrary in the future so that we can read the data from any
11 * source. This is actually made quite simple already since all data read in
12 * is handled by one single helper function and then palced into a FlexBuf for
13 * easy access by the other functions. The FlexBuf also allows for block
14 * reading from disk, which improves speed by a noticable amount.
15 * <br>
16 * There are also some extra features implemented that allow you to break the
17 * standard XML reader specs and eliminate leading and trailing whitespace in
18 * all read content. This is useful in situations where you allow additional
19 * whitespace in the files to make them easily human readable. The resturned
20 * content will be NULL in sitautions where all content between nodes was
21 * stripped.
22 *@author Mike Buland
23 */
24class XmlReader : public XmlDocument
25{
26public:
27 /**
28 * Create a standard XmlReader. The optional parameter bStrip allows you to
29 * create a reader that will strip out all leading and trailing whitespace
30 * in content, a-la html.
31 *@param bStrip Strip out leading and trailing whitespace?
32 */
33 XmlReader( bool bStrip=false );
34
35 /**
36 * Destroy this XmlReader.
37 */
38 ~XmlReader();
39
40 /**
41 * Get the error code if an error happened.
42 *@returns The error code (I don't know what they are either)
43 */
44 int getError();
45
46 /**
47 * Report an error to something, this is a really strange mechanism and
48 * should probably just be replaced with the multi-log system.
49 *@param sError The error to report.
50 */
51 void reportError( const char *sError );
52
53 /**
54 * Build a document based on some kind of input. This is called
55 * automatically by the constructor.
56 */
57 bool buildDoc();
58
59private:
60 /**
61 * This is called by the low level automoton in order to get the next
62 * character. This function should return a character at the current
63 * position plus nIndex, but does not increment the current character.
64 *@param nIndex The index of the character from the current stream position.
65 *@returns A single character at the requested position, or 0 for end of
66 * stream.
67 */
68 virtual char getChar( int nIndex = 0 ) = 0;
69
70 /**
71 * Called to increment the current stream position by a single character.
72 */
73 virtual void usedChar() = 0;
74
75 /**
76 * Automoton function: is whitespace.
77 *@param chr A character
78 *@returns True if chr is whitespace, false otherwise.
79 */
80 bool isws( char chr );
81
82 /**
83 * Automoton function: ws. Skips sections of whitespace.
84 *@returns True if everything was ok, False for end of stream.
85 */
86 bool ws();
87
88 /**
89 * Automoton function: node. Processes an XmlNode
90 *@returns True if everything was ok, False for end of stream.
91 */
92 bool node();
93
94 /**
95 * Automoton function: startNode. Processes the begining of a node.
96 *@returns True if everything was ok, False for end of stream.
97 */
98 bool startNode();
99
100 /**
101 * Automoton function: name. Processes the name of a node.
102 *@returns True if everything was ok, False for end of stream.
103 */
104 bool name();
105
106 char getEscape();
107
108 /**
109 * Automoton function: paramlist. Processes a list of node params.
110 *@returns True if everything was ok, False for end of stream.
111 */
112 bool paramlist();
113
114 /**
115 * Automoton function: param. Processes a single parameter.
116 *@returns True if everything was ok, False for end of stream.
117 */
118 bool param();
119
120 /**
121 * Automoton function: content. Processes node content.
122 *@returns True if everything was ok, False for end of stream.
123 */
124 bool content();
125
126 FlexBuf fbContent; /**< buffer for the current node's content. */
127 FlexBuf fbParamName; /**< buffer for the current param's name. */
128 FlexBuf fbParamValue; /**< buffer for the current param's value. */
129 bool bStrip; /**< Are we stripping whitespace? */
130 int nError; /**< Is there an error? */
131};
132
133#endif
diff --git a/src/xmlstringreader.cpp b/src/xmlstringreader.cpp
new file mode 100644
index 0000000..aa7174f
--- /dev/null
+++ b/src/xmlstringreader.cpp
@@ -0,0 +1,37 @@
1#include "xmlstringreader.h"
2#include <string.h>
3
4XmlStringReader::XmlStringReader( const char *sString, bool bStrip )
5 : XmlReader( bStrip )
6{
7 this->sString = sString;
8
9 nIndex = 0;
10 nLength = strlen( sString );
11
12 buildDoc();
13}
14
15XmlStringReader::~XmlStringReader()
16{
17}
18
19char XmlStringReader::getChar( int nAdd )
20{
21 if( nLength >= nIndex+nAdd+1 )
22 {
23 return sString[nIndex+nAdd];
24 }
25 else
26 {
27 return '\0';
28 }
29}
30
31void XmlStringReader::usedChar()
32{
33 if( nLength >= nIndex+1 )
34 {
35 nIndex++;
36 }
37}
diff --git a/src/xmlstringreader.h b/src/xmlstringreader.h
new file mode 100644
index 0000000..07da83c
--- /dev/null
+++ b/src/xmlstringreader.h
@@ -0,0 +1,49 @@
1#ifndef XMLSTRINGREADER
2#define XMLSTRINGREADER
3
4#include <stdio.h>
5#include "xmlreader.h"
6#include "flexbuf.h"
7
8/**
9 * Takes care of reading in xml formatted data from a file. This could/should
10 * be made more arbitrary in the future so that we can read the data from any
11 * source. This is actually made quite simple already since all data read in
12 * is handled by one single helper function and then palced into a FlexBuf for
13 * easy access by the other functions. The FlexBuf also allows for block
14 * reading from disk, which improves speed by a noticable amount.
15 * <br>
16 * There are also some extra features implemented that allow you to break the
17 * standard XML reader specs and eliminate leading and trailing whitespace in
18 * all read content. This is useful in situations where you allow additional
19 * whitespace in the files to make them easily human readable. The resturned
20 * content will be NULL in sitautions where all content between nodes was
21 * stripped.
22 *@author Mike Buland
23 */
24class XmlStringReader : public XmlReader
25{
26public:
27 /**
28 * Create a new string reader around an already created and formatted
29 * null-terminated string.
30 *@param sString A pointer to the string data that will be used. This data
31 * is not changed during processing.
32 *@param bStrip Strip out leading and trailing whitespace.
33 */
34 XmlStringReader( const char *sString, bool bStrip=false );
35
36 /**
37 * Destroy this string reader.
38 */
39 ~XmlStringReader();
40
41private:
42 char getChar( int nIndex = 0 );
43 void usedChar();
44 const char *sString; /**< Internal pointer to the input string. */
45 int nIndex; /**< Our index into the string */
46 int nLength; /**< The computed length of the string */
47};
48
49#endif
diff --git a/src/xmlstringwriter.cpp b/src/xmlstringwriter.cpp
new file mode 100644
index 0000000..adeed6a
--- /dev/null
+++ b/src/xmlstringwriter.cpp
@@ -0,0 +1,23 @@
1#include <stdio.h>
2#include <stdlib.h>
3#include "xmlstringwriter.h"
4
5XmlStringWriter::XmlStringWriter( const char *sIndent ) :
6 XmlWriter( sIndent )
7{
8}
9
10XmlStringWriter::~XmlStringWriter()
11{
12}
13
14void XmlStringWriter::writeString( const char *sString )
15{
16 sXml += sString;
17}
18
19std::string &XmlStringWriter::getString()
20{
21 return sXml;
22}
23
diff --git a/src/xmlstringwriter.h b/src/xmlstringwriter.h
new file mode 100644
index 0000000..530db3e
--- /dev/null
+++ b/src/xmlstringwriter.h
@@ -0,0 +1,50 @@
1#ifndef XML_STRING_WRITER
2#define XML_STRING_WRITER
3
4#include "xmlnode.h"
5#include "xmlwriter.h"
6
7/**
8 * Implements xml writing in the XML standard format. Also allows you to
9 * break that format and auto-indent your exported xml data for ease of
10 * reading. The auto-indenting will only be applied to sections that
11 * have no content of their own already. This means that except for
12 * whitespace all of your data will be preserved perfectly.
13 * You can create an XmlWriter object around a file, or access the static
14 * write function directly and just hand it a filename and a root XmlNode.
15 * When using an XmlWriter object the interface is identicle to that of
16 * the XmlDocument class, so reference that class for API info. However
17 * when the initial (or root) node is closed, and the document is finished
18 * the file will be created and written to automatically. The user can
19 * check to see if this is actually true by calling the isFinished
20 * function in the XmlDocument class.
21 *@author Mike Buland
22 */
23class XmlStringWriter : public XmlWriter
24{
25public:
26 /**
27 * Construct a string writer using an internal string buffer.
28 *@param sIndent Optional indent to add to each line.
29 */
30 XmlStringWriter( const char *sIndent=NULL );
31
32 /**
33 * Destroy the string writer and the internal string.
34 */
35 ~XmlStringWriter();
36
37 /**
38 * Get the string that was built. This is only valid after the document has
39 * been completed, so check isCompleted or be sure your addNode and
40 * closeNode calls match up.
41 *@returns A reference to the internal string object.
42 */
43 std::string &getString();
44
45private:
46 void writeString( const char *sString );
47 std::string sXml; /**< The string object we "write" to. */
48};
49
50#endif
diff --git a/src/xmlwriter.cpp b/src/xmlwriter.cpp
new file mode 100644
index 0000000..236939d
--- /dev/null
+++ b/src/xmlwriter.cpp
@@ -0,0 +1,173 @@
1#include <stdio.h>
2#include <stdlib.h>
3#include "xmlwriter.h"
4
5XmlWriter::XmlWriter( const char *sIndent, XmlNode *pRoot ) :
6 XmlDocument( pRoot )
7{
8 if( sIndent == NULL )
9 {
10 this->sIndent = "";
11 }
12 else
13 {
14 this->sIndent = sIndent;
15 }
16}
17
18XmlWriter::~XmlWriter()
19{
20}
21
22void XmlWriter::write()
23{
24 write( getRoot(), sIndent.c_str() );
25}
26
27void XmlWriter::write( XmlNode *pRoot, const char *sIndent )
28{
29 writeNode( pRoot, 0, sIndent );
30}
31
32void XmlWriter::closeNode()
33{
34 XmlDocument::closeNode();
35
36 if( isCompleted() )
37 {
38 write( getRoot(), sIndent.c_str() );
39 }
40}
41
42void XmlWriter::writeIndent( int nIndent, const char *sIndent )
43{
44 if( sIndent == NULL ) return;
45 for( int j = 0; j < nIndent; j++ )
46 {
47 writeString( sIndent );
48 }
49}
50
51std::string XmlWriter::escape( std::string sIn )
52{
53 std::string sOut;
54
55 std::string::const_iterator i;
56 for( i = sIn.begin(); i != sIn.end(); i++ )
57 {
58 if( ((*i >= ' ' && *i <= '9') ||
59 (*i >= 'a' && *i <= 'z') ||
60 (*i >= 'A' && *i <= 'Z') ) &&
61 (*i != '\"' && *i != '\'' )
62 )
63 {
64 sOut += *i;
65 }
66 else
67 {
68 sOut += "&#";
69 char buf[4];
70 sprintf( buf, "%d", *i );
71 sOut += buf;
72 sOut += ';';
73 }
74 }
75
76 return sOut;
77}
78
79void XmlWriter::writeNodeProps( XmlNode *pNode, int nIndent, const char *sIndent )
80{
81 for( int j = 0; j < pNode->getNumProperties(); j++ )
82 {
83 writeString(" ");
84 writeString( pNode->getPropertyName( j ) );
85 writeString("=\"");
86 writeString( escape( pNode->getProperty( j ) ).c_str() );
87 writeString("\"");
88 }
89}
90
91void XmlWriter::writeNode( XmlNode *pNode, int nIndent, const char *sIndent )
92{
93 if( pNode->hasChildren() )
94 {
95 writeIndent( nIndent, sIndent );
96 writeString("<");
97 writeString( pNode->getName() );
98 writeNodeProps( pNode, nIndent, sIndent );
99 if( sIndent )
100 writeString(">\n");
101 else
102 writeString(">");
103
104 if( pNode->getContent( 0 ) )
105 {
106 writeIndent( nIndent+1, sIndent );
107 if( sIndent )
108 {
109 writeString( pNode->getContent( 0 ) );
110 writeString("\n");
111 }
112 else
113 writeString( pNode->getContent( 0 ) );
114 }
115
116 int nNumChildren = pNode->getNumChildren();
117 for( int j = 0; j < nNumChildren; j++ )
118 {
119 writeNode( pNode->getChild( j ), nIndent+1, sIndent );
120 if( pNode->getContent( j+1 ) )
121 {
122 writeIndent( nIndent+1, sIndent );
123 if( sIndent )
124 {
125 writeString( pNode->getContent( j+1 ) );
126 writeString("\n");
127 }
128 else
129 writeString( pNode->getContent( j+1 ) );
130 }
131 }
132
133 writeIndent( nIndent, sIndent );
134 if( sIndent )
135 {
136 writeString("</");
137 writeString( pNode->getName() );
138 writeString(">\n");
139 }
140 else
141 {
142 writeString("</");
143 writeString( pNode->getName() );
144 writeString(">");
145 }
146 }
147 else if( pNode->getContent() )
148 {
149 writeIndent( nIndent, sIndent );
150 writeString("<");
151 writeString( pNode->getName() );
152 writeNodeProps( pNode, nIndent, sIndent );
153 writeString(">");
154 writeString( pNode->getContent() );
155 writeString("</");
156 writeString( pNode->getName() );
157 writeString(">");
158 if( sIndent )
159 writeString("\n");
160 }
161 else
162 {
163 writeIndent( nIndent, sIndent );
164 writeString("<");
165 writeString( pNode->getName() );
166 writeNodeProps( pNode, nIndent, sIndent );
167 if( sIndent )
168 writeString("/>\n");
169 else
170 writeString("/>");
171 }
172}
173
diff --git a/src/xmlwriter.h b/src/xmlwriter.h
new file mode 100644
index 0000000..5bc3f0a
--- /dev/null
+++ b/src/xmlwriter.h
@@ -0,0 +1,96 @@
1#ifndef XMLWRITER
2#define XMLWRITER
3
4#include "xmlnode.h"
5#include "xmldocument.h"
6
7/**
8 * Implements xml writing in the XML standard format. Also allows you to
9 * break that format and auto-indent your exported xml data for ease of
10 * reading. The auto-indenting will only be applied to sections that
11 * have no content of their own already. This means that except for
12 * whitespace all of your data will be preserved perfectly.
13 * You can create an XmlWriter object around a file, or access the static
14 * write function directly and just hand it a filename and a root XmlNode.
15 * When using an XmlWriter object the interface is identicle to that of
16 * the XmlDocument class, so reference that class for API info. However
17 * when the initial (or root) node is closed, and the document is finished
18 * the file will be created and written to automatically. The user can
19 * check to see if this is actually true by calling the isFinished
20 * function in the XmlDocument class.
21 *@author Mike Buland
22 */
23class XmlWriter : public XmlDocument
24{
25public:
26 /**
27 * Construct a standard XmlWriter.
28 *@param sIndent Set this to something other than NULL to include it as an
29 * indent before each node in the output that doesn't already have content.
30 * If you are using the whitespace stripping option in the XmlReader and set
31 * this to a tab or some spaces it will never effect the content of your
32 * file.
33 */
34 XmlWriter( const char *sIndent=NULL, XmlNode *pRoot=NULL );
35
36 /**
37 * Destroy the writer.
38 */
39 ~XmlWriter();
40
41 /**
42 * This override of the parent class closeNode function calls the parent
43 * class, but also triggers a write operation when the final node is closed.
44 * This means that by checking the isCompleted() function the user may also
45 * check to see if their file has been written or not.
46 */
47 void closeNode();
48
49 void write();
50
51private:
52 std::string sIndent; /**< The indent string */
53
54 std::string escape( std::string sIn );
55
56 /**
57 * Write the file.
58 *@param pNode The root node
59 *@param sIndent The indent text.
60 */
61 void write( XmlNode *pNode, const char *sIndent=NULL );
62
63 /**
64 * Write a node in the file, including children.
65 *@param pNode The node to write.
66 *@param nIndent The indent level (the number of times to include sIndent)
67 *@param sIndent The indent text.
68 */
69 void writeNode( XmlNode *pNode, int nIndent, const char *sIndent );
70
71 /**
72 * Write the properties of a node.
73 *@param pNode The node who's properties to write.
74 *@param nIndent The indent level of the containing node
75 *@param sIndent The indent text.
76 */
77 void writeNodeProps( XmlNode *pNode, int nIndent, const char *sIndent );
78
79 /**
80 * Called to write the actual indent.
81 *@param nIndent The indent level.
82 *@param sIndent The indent text.
83 */
84 void writeIndent( int nIndent, const char *sIndent );
85
86 /**
87 * This is the function that must be overridden in order to use this class.
88 * It must write the null-terminated string sString, minus the mull,
89 * verbatum to it's output device. Adding extra characters for any reason
90 * will break the XML formatting.
91 *@param sString The string data to write to the output.
92 */
93 virtual void writeString( const char *sString ) = 0;
94};
95
96#endif