source: trunk/tools/database/kHtmlPC.cpp@ 3830

Last change on this file since 3830 was 2818, checked in by bird, 26 years ago

Read more of the function header into the database.
Stateupd is changed to do this and the database is expanded with new fields.
The sample is partly updated.

File size: 99.4 KB
Line 
1/* $Id: kHtmlPC.cpp,v 1.3 2000-02-18 12:42:07 bird Exp $ */
2/*
3 * kHtmlPC - Special-purpose HTML/SQL preprocessor.
4 *
5 * Copyright (c) 1999 knut st. osmundsen
6 *
7 */
8
9/*******************************************************************************
10* Defined Constants *
11*******************************************************************************/
12#define ErrorDescCase(errorcode) case errorcode: return formatMessage(#errorcode)
13#define TAG_PARAMETER_LIST \
14 const kTag &tag, \
15 kLIFO<kFileEntry> &lifoFile, \
16 kLIFO<kFileEntry> &lifoUsed, \
17 kLIFO<kVariableEntry> &lifoVar, \
18 kLIFO2<kSqlEntry, kVariableEntry> &lifoSql, \
19 kFileEntry * &pCurFile, \
20 int &i, \
21 FILE *phLog
22
23#define TAG_PARAMETERS tag, lifoFile, lifoUsed, lifoVar, lifoSql, pCurFile, i, phLog
24#define TAG_PARAMETER_LIST_UNREF \
25 int f = phLog || i || pCurFile || &lifoFile || \
26 &lifoUsed || &lifoVar || &lifoSql || &tag; \
27 f = f
28
29#ifdef __EMX__
30 #define max(a,b) (((a) > (b)) ? (a) : (b))
31 #define min(a,b) (((a) < (b)) ? (a) : (b))
32 #define _System
33#endif
34
35#define TRUE 1
36#define FALSE 0
37#define CCHMAXPATH 260
38
39
40/*******************************************************************************
41* Structures and Typedefs *
42*******************************************************************************/
43typedef unsigned long BOOL;
44typedef struct _POINTL
45{
46 long x;
47 long y;
48} POINTL, *PPOINTL;
49
50
51/*******************************************************************************
52* Header Files *
53*******************************************************************************/
54#include <stdio.h>
55#include <stdlib.h>
56#include <string.h>
57#include <signal.h>
58#include <malloc.h>
59#ifdef __EMX__
60 #include <math.h>
61 #undef _NAN
62 static double _NAN = (0.0/0.0);
63#else
64 #include <float.h>
65#endif
66#include <assert.h>
67/* gifdraw */
68extern "C"
69{
70 #include "gd/gd.h"
71 #include "gd/gdfontg.h"
72 #include "gd/gdfontl.h"
73 #include "gd/gdfonts.h"
74 /* Y wrappers; LB = origo at Left Botton corner */
75 #define gdImageLineLB(im,x1,y1,x2,y2,cl) gdImageLine(im,x1,gdImageSY(im)-(y1),x2,gdImageSY(im)-(y2),cl)
76 #define gdImageStringLB(im,f,x,y,s,cl) gdImageString(im,f,x,gdImageSY(im)-(y),s,cl)
77 #define gdImageStringUpLB(im,f,x,y,s,cl) gdImageStringUp(im,f,x,gdImageSY(im)-(y),s,cl)
78}
79
80#include "kLIFO.h"
81#include "kList.h"
82#include "kHtmlPC.h"
83#include "db.h"
84
85/*@Struct***********************************************************************
86* Structures and Typedefs *
87*******************************************************************************/
88typedef struct _GraphCallBackParameters
89{
90 kGraph *pGraph;
91 kGraphDataSet *pDataSet;
92 kGraphData *pData;
93 kGraph::enmType enmTypeCd;
94 long lSeqNbr;
95 char *pszLegend;
96 char *pszColor;
97} GRAPHCALLBACKPARAM, *PGRAPHCALLBACKPARAM;
98
99
100
101/*******************************************************************************
102* Global Variables *
103*******************************************************************************/
104static FILE *phLog = NULL;
105
106
107/*******************************************************************************
108* Internal Functions *
109*******************************************************************************/
110extern "C" void handler(int sig);
111static void syntax(void);
112static void openLog(void);
113static void closeLog(void);
114static unsigned long processFile(const char *pszFilenamem, const POPTIONS pOptions);
115static unsigned long tagEndkSql(TAG_PARAMETER_LIST);
116static unsigned long tagkSql(TAG_PARAMETER_LIST);
117static unsigned long tagkTemplate(TAG_PARAMETER_LIST, FILE * &phFile, const POPTIONS pOptions);
118static unsigned long tagkInclude(TAG_PARAMETER_LIST);
119static unsigned long tagkGraph(TAG_PARAMETER_LIST, const POPTIONS pOptions);
120static unsigned long tagkDefine(TAG_PARAMETER_LIST);
121static unsigned long tagkUndef(TAG_PARAMETER_LIST);
122static unsigned long tagkIf(TAG_PARAMETER_LIST);
123static unsigned long tagkElse(TAG_PARAMETER_LIST);
124static unsigned long tagkEndif(TAG_PARAMETER_LIST);
125
126static BOOL rebuildFileLIFO(kLIFO<kFileEntry> &lifoFile, kLIFO<kFileEntry> &lifoUsed, const kFileEntry *pFileEntryTop);
127static char *dupeString(const char *psz);
128#if 0
129inline char upcase(char ch);
130static char *stristr(const char *pszStr, const char *pszSubStr);
131static char *trim(char *psz);
132static char *ltrim(char *psz);
133static const char *ltrim(const char *psz);
134static char *ltrimL(char *psz);
135#endif
136static const char *ltrimL(const char *psz);
137static long _System dbFetchCallBack(const char *pszValue, const char *pszFieldName, void *pvUser);
138long _System dbDataSetCallBack(const char *pszValue, const char *pszFieldName, void *pvUser);
139long _System dbGraphCallBack(const char *pszValue, const char *pszFieldName, void *pvUser);
140
141
142/**
143 * Main function.
144 * @returns Number of errors
145 * @param argc Argument count.
146 * @param argv Argument array.
147 */
148int main(int argc, char **argv)
149{
150 int argi;
151 BOOL fFatal = FALSE;
152 unsigned long ulRc = 0;
153 char *pszHost = "localhost";
154 char *pszDatabase = "Odin32";
155 char *pszUser = "root";
156 char *pszPasswd = "";
157 char szPathName[CCHMAXPATH];
158 OPTIONS options = {"."};
159
160
161 /* signal handler */
162 if (SIG_ERR == signal(SIGBREAK, handler)
163 || SIG_ERR == signal(SIGTERM, handler)
164 || SIG_ERR == signal(SIGINT, handler)
165 )
166 fprintf(stderr, "Error installing signalhandler...");
167
168
169 /**************************************************************************
170 * parse arguments.
171 * options: -h or -? help
172 * -b:<basepath> Basepath for output files.
173 * -d:<dbname> Database name
174 * -p:<passwd> Password
175 * -u:<user> Userid
176 * -h:<host> Hostname/IP-address
177 **************************************************************************/
178 if (argc == 1)
179 syntax();
180 argi = 1;
181 while (argi < argc && !fFatal)
182 {
183 if(argv[argi][0] == '-' || argv[argi][0] == '/')
184 {
185 switch (argv[argi][1])
186 {
187 case 'b':
188 case 'B':
189 printf("%s\n", argv[argi]);
190 if (argv[argi][2] == ':')
191 {
192 if (_fullpath(&szPathName[0], &argv[argi][3], sizeof(szPathName)) != NULL)
193 options.pszBaseDir = &szPathName[0];
194 else
195 {
196 fprintf(stderr, "error: basepath '%s' don't exists\n", &argv[argi][3]);
197 fFatal = TRUE;
198 }
199 }
200 else
201 {
202 fprintf(stderr, "error: option '-b:' requires a directory name.\n");
203 fFatal = TRUE;
204 }
205 break;
206
207 case 'd':
208 case 'D':
209 if (argv[argi][2] == ':')
210 pszDatabase = &argv[argi][3];
211 else
212 {
213 fprintf(stderr, "error: option '-d:' requires database name.\n");
214 fFatal = TRUE;
215 }
216 break;
217
218 case 'h':
219 case 'H':
220 if (argv[argi][2] == ':')
221 {
222 pszHost = &argv[argi][3];
223 break;
224 }
225 case '?':
226 syntax();
227 return 0;
228
229 case 'p':
230 case 'P':
231 if (argv[argi][2] == ':')
232 pszPasswd = &argv[argi][3];
233 else
234 {
235 fprintf(stderr, "error: option '-p:' requires password.\n");
236 fFatal = TRUE;
237 }
238 break;
239
240 case 'u':
241 case 'U':
242 if (argv[argi][2] == ':')
243 pszUser = &argv[argi][3];
244 else
245 {
246 fprintf(stderr, "error: option '-u:' requires userid.\n");
247 fFatal = TRUE;
248 }
249 break;
250
251 /*
252 options.fSomeOption = argv[argi][2] != '-';
253 break;
254 */
255
256 default:
257 fprintf(stderr, "incorrect parameter. (argi=%d, argv[argi]=%s)\n", argi, argv[argi]);
258 fFatal = TRUE;
259 break;
260 }
261 }
262 else
263 {
264 if (phLog == NULL)
265 openLog();
266 if (dbConnect(pszHost, pszUser, pszPasswd, pszDatabase))
267 {
268 ulRc += processFile(argv[argi], &options);
269 dbDisconnect();
270 }
271 else
272 fprintf(phLog, "Error connecting to database.\n");
273 }
274 argi++;
275 }
276
277 /* close the log */
278 closeLog();
279
280 /* warn if error during processing. */
281 if (!fFatal)
282 fprintf(stderr, "kHTMLPreCompiler compleated with %ld error%s and %ld warning%s.\n",
283 ulRc & 0x0000ffffUL, (ulRc & 0x0000ffffUL) != 1 ? "s" : "",
284 ulRc >> 16, (ulRc >> 16) != 1 ? "s" : ""
285 );
286 return (int)(ulRc & 0x0000ffff);
287}
288
289
290/**
291 * signal handler....
292 * @param sig
293 * @remark Needs to flush files before termination. (debugging purpose)
294 */
295void handler(int sig)
296{
297 fprintf(stderr, "\n\t!signal %d!\n", sig);
298 flushall();
299 dbDisconnect();
300 exit(-1);
301}
302
303/**
304 * Display syntax.
305 */
306static void syntax(void)
307{
308 printf("\n"
309 "kHtmlPC v%01d.%02d - General-purpose HTML precompiler.\n"
310 "-------------------------------------------------\n"
311 "syntax: kHtmlPc.exe [-h|-?] [options] [file1 [file2 [..]]\n"
312 "\n"
313 " -h or -? Syntax help. (this)\n"
314 " -h:<hostname> Database server hostname. default: localhost\n"
315 " -u:<username> Username on the server. default: root\n"
316 " -p:<password> Password. default: <empty>\n"
317 " -d:<database> Database to use. default: Odin32\n"
318 "\n"
319 "\n"
320 "Copyright (c) 1999 knut st. osmundsen (knut.stange.osmundsen@pmsc.no)",
321 VER_MAJOR, VER_MINOR
322 );
323}
324
325
326/**
327 * Opens log file. Currently this we don't use a logfile, stderr is our logfile.
328 */
329static void openLog(void)
330{
331 #if 0
332 if (phLog == NULL)
333 {
334 phLog = fopen("APIImport.Log", "w");
335 if (phLog == NULL)
336 {
337 fprintf(stderr,"error occured while opening log file - will use stderr instead.\n");
338 phLog = stderr;
339 }
340 }
341 #else
342 phLog = stderr;
343 #endif
344}
345
346
347/**
348 * Closes the log.
349 */
350static void closeLog(void)
351{
352 if (phLog != stderr && phLog != NULL)
353 fclose(phLog);
354}
355
356
357/**
358 * Preprocesses a file.
359 * @returns high word Number of warnings.
360 * low word Number of errors;
361 * @param pszFilename Pointer to filename.
362 * @param pOption Pointer to the option struct.
363 * @remark Big function!
364 */
365static unsigned long processFile(const char *pszFilename, const POPTIONS pOptions)
366{
367 unsigned long ulRc = 0;
368 unsigned long ulRc2;
369 FILE *phFile = NULL; /* Current output file. */
370 kLIFO<kFileEntry> lifoFile, lifoUsed;
371 kLIFO<kVariableEntry> lifoVar; /* tags not implemented yet... */
372 kLIFO2<kSqlEntry, kVariableEntry> lifoSql;
373 kFileEntry *pCurFile;
374 const kVariableEntry *pVariable = NULL;
375 const char *psz;
376 char szVariable[81];
377
378 /* open initial file */
379 try
380 {
381 pCurFile = new kFileEntry(pszFilename);
382 }
383 catch (int errorcode)
384 {
385 fprintf(phLog, "error opening initial file. errorcode = %d\n", errorcode);
386 return 0x00000001;
387 }
388
389 /* loop on pCurFile */
390 while (pCurFile != NULL)
391 {
392 /* loop on curFile->pszCurrent */
393 while (pCurFile->pszCurrent != NULL && *pCurFile->pszCurrent != '\0')
394 {
395 /*********/
396 /* !kTag */
397 /*********/
398 if (pCurFile->pszCurrent[0] == '<' && pCurFile->pszCurrent[1] == '!')
399 { /* find end, replace variables and switch tag type. */
400 char szTag[1024];
401 BOOL fQuote = FALSE;
402 int i = 0, j = 0;
403
404 /* copy tag and insert variables */
405 while ((fQuote || pCurFile->pszCurrent[i] != '>') && pCurFile->pszCurrent[i] != '\0'
406 && j < (int)sizeof(szTag))
407 {
408 fQuote = fQuote ? pCurFile->pszCurrent[i] != '"' : pCurFile->pszCurrent[i] == '"';
409 /* variable? */
410 if (pCurFile->pszCurrent[i] == '$' && pCurFile->pszCurrent[i+1] == '(')
411 {
412 psz = pCurFile->pszCurrent + i + 2;
413 while (*psz != ')' && *psz != '\0' && (psz - &pCurFile->pszCurrent[i]) < (int)(sizeof(szVariable)-1))
414 psz++;
415 if (*psz == ')')
416 {
417 strncpy(&szVariable[0], pCurFile->pszCurrent + i + 2, psz - &pCurFile->pszCurrent[i] - 2);
418 szVariable[psz - &pCurFile->pszCurrent[i] - 2] = '\0';
419
420 pVariable = lifoSql.findSub(&szVariable[0]);
421 if (pVariable == NULL)
422 pVariable = lifoVar.find(&szVariable[0]);
423
424 if (pVariable != NULL)
425 {
426 if(j + strlen(pVariable->getValue()) < sizeof(szTag))
427 { /* copy variable value */
428 strcpy(&szTag[j], pVariable->getValue());
429 j += strlen(&szTag[j]) - 1;
430 i += psz - &pCurFile->pszCurrent[i];
431 }
432 else
433 { /* warning - complain, skip and continue */
434 fprintf(phLog, "%s(%ld) : warning: Variable '%s' out of space, increase the size of szTag.\n",
435 pCurFile->getFilename(), pCurFile->getLineNumber()+1, &szVariable[0]);
436 szTag[j] = pCurFile->pszCurrent[i];
437 }
438 }
439 else
440 { /* warning - complain, skip and continue */
441 fprintf(phLog, "%s(%ld) : warning: Variable '%s' not defined.\n",
442 pCurFile->getFilename(), pCurFile->getLineNumber()+1, &szVariable[0]);
443 szTag[j] = pCurFile->pszCurrent[i];
444 }
445 }
446 else
447 { /* error - complain, skip and continue. */
448 fprintf(phLog, "%s(%ld) : error: Missing left parenthese on variable expression\n",
449 pCurFile->getFilename(), pCurFile->getLineNumber()+1);
450 szTag[j] = pCurFile->pszCurrent[i];
451 }
452 }
453 else
454 szTag[j] = pCurFile->pszCurrent[i];
455 /* next */
456 i++;
457 j++;
458 }
459
460
461 /* copy ok? */
462 if (j < (int)sizeof(szTag) && pCurFile->pszCurrent[i] == '>')
463 {
464 szTag[j++] = '>';
465 szTag[j] = '\0';
466 i++;
467 try
468 {
469 /* try create tag object */
470 kTag tag(&szTag[0]);
471
472 /* check for warning */
473 if (tag.queryWarning() != NULL)
474 fprintf(phLog, "%s(%ld) : warning: tag warning - %s.\n",
475 pCurFile->getFilename(), pCurFile->getLineNumber()+1, tag.queryWarning());
476
477
478 /**************/
479 /**************/
480 /* tag switch */
481 /**************/
482 /**************/
483 ulRc2 = 0;
484 if (tag.isTag("!kSql")) /* !kSql */
485 ulRc2 = tagkSql(TAG_PARAMETERS);
486 else if (tag.isTag("!/kSql")) /* !/kSql */
487 ulRc2 = tagEndkSql(TAG_PARAMETERS);
488 else if (tag.isTag("!kTemplate")) /* !kTemplate */
489 ulRc2 = tagkTemplate(TAG_PARAMETERS, phFile, pOptions);
490 else if (tag.isTag("!kInclude")) /* !kInclude */
491 ulRc2 = tagkInclude(TAG_PARAMETERS);
492 else if (tag.isTag("!kGraph")) /* !kGraph */
493 ulRc2 = tagkGraph(TAG_PARAMETERS, pOptions);
494 else if (tag.isTag("!kDefine")) /* !kDefine */
495 ulRc2 = tagkDefine(TAG_PARAMETERS);
496 else if (tag.isTag("!kUndef")) /* !kUndef */
497 ulRc2 = tagkUndef(TAG_PARAMETERS);
498 else if (tag.isTag("!kIf")) /* !kIf */
499 ulRc2 = tagkIf(TAG_PARAMETERS);
500 else if (tag.isTag("!kElse")) /* !kElse */
501 ulRc2 = tagkElse(TAG_PARAMETERS);
502 else if (tag.isTag("!kEndif")) /* !kEndif */
503 ulRc2 = tagkEndif(TAG_PARAMETERS);
504 else
505 {
506 if (phFile != NULL)
507 fputc(*pCurFile->pszCurrent, phFile); /* if this proove to be very slow, we'll have to buffer writes better. */
508 pCurFile->pszCurrent++;
509 }
510
511 /* if error occurred - skip and continue. */
512 if ((ulRc2 & 0x0000ffff) != 0)
513 {
514 if (phFile != NULL)
515 fputc(*pCurFile->pszCurrent, phFile); /* if this proove to be very slow, we'll have to buffer writes better. */
516 pCurFile->pszCurrent++;
517 }
518 ulRc += ulRc2;
519 }
520 catch (kError::enmErrors enmErrorCd)
521 {
522 fprintf(phLog, "%s(%ld) : error: tag error - %s.\n",
523 pCurFile->getFilename(), pCurFile->getLineNumber()+1, kError::queryDescription(enmErrorCd));
524 if (phFile != NULL)
525 fputc(*pCurFile->pszCurrent, phFile); /* if this proove to be very slow, we'll have to buffer writes better. */
526 pCurFile->pszCurrent++;
527 ulRc += 0x00000001;
528 }
529 }
530 else
531 {
532 fprintf(phLog, "%s(%ld) : error: error occurred extracting tag.\n\tPossible reasons: unbalanced quotes, missing '>',...\n",
533 pCurFile->getFilename(), pCurFile->getLineNumber()+1);
534 if (phFile != NULL)
535 fputc(*pCurFile->pszCurrent, phFile); /* if this proove to be very slow, we'll have to buffer writes better. */
536 pCurFile->pszCurrent++;
537 ulRc += 0x00000001;
538 }
539
540 }
541 /*************/
542 /*************/
543 /* VARIABLE */
544 /*************/
545 /*************/
546 else if (pCurFile->pszCurrent[0] == '$' && pCurFile->pszCurrent[1] == '(')
547 {
548 psz = pCurFile->pszCurrent + 2;
549 while (*psz != ')' && *psz != '\0' && (psz - pCurFile->pszCurrent) < (int)(sizeof(szVariable)-1))
550 psz++;
551 if (*psz == ')')
552 {
553 strncpy(&szVariable[0], pCurFile->pszCurrent+2, psz - pCurFile->pszCurrent-2);
554 szVariable[psz - pCurFile->pszCurrent - 2] = '\0';
555
556 pVariable = lifoSql.findSub(&szVariable[0]);
557 if (pVariable == NULL)
558 pVariable = lifoVar.find(&szVariable[0]);
559
560 if (pVariable != NULL)
561 {
562 /* write variable */
563 if (phFile != NULL)
564 fwrite(pVariable->getValue(), strlen(pVariable->getValue()), 1, phFile);
565 }
566 else
567 { /* warning - complain, skip and continue */
568 fprintf(phLog, "%s(%ld) : warning: Variable '%s' not defined.\n",
569 pCurFile->getFilename(), pCurFile->getLineNumber()+1, &szVariable[0]);
570 if (phFile != NULL)
571 fwrite(pCurFile->pszCurrent, psz - pCurFile->pszCurrent + 1, 1, phFile);
572 ulRc += 0x00010000;
573 }
574 pCurFile->pszCurrent = psz + 1;
575 }
576 else
577 { /* error - complain, skip and continue. */
578 fprintf(phLog, "%s(%ld) : error: Missing left parenthese on variable expression\n",
579 pCurFile->getFilename(), pCurFile->getLineNumber()+1);
580 if (phFile != NULL)
581 fputc(*pCurFile->pszCurrent, phFile); /* if this proove to be very slow, we'll have to buffer writes better. */
582 pCurFile->pszCurrent++;
583 ulRc += 0x00000001;
584 }
585 }
586 else
587 { /* next */
588 if (phFile != NULL)
589 fputc(*pCurFile->pszCurrent, phFile); /* if this proove to be very slow, we'll have to buffer writes better. */
590 pCurFile->pszCurrent++;
591 }
592 }
593
594
595 /* resume processing of parent file */
596 kFileEntry *pFE = lifoFile.pop();
597 if (pFE != NULL)
598 {
599 assert(pFE == pCurFile->queryParent());
600 pFE->pszCurrent = pCurFile->queryParentPointer();
601 }
602 lifoUsed.push(pCurFile);
603 pCurFile = pFE;
604 }
605
606 /* close file */
607 if (phFile != NULL)
608 fclose(phFile);
609
610 return ulRc;
611}
612
613
614/**
615 * Tag function - kSql.
616 * @returns low word: number of errors
617 * high word: number of warnings
618 * @remark See TAG_PARAMETER_LIST for parameters.
619 * Use TAG_PARAMETERS when calling this function.
620 */
621static unsigned long tagkSql(TAG_PARAMETER_LIST)
622{
623 unsigned long ulRc = 0;
624
625 try
626 {
627 lifoSql.push(new kSqlEntry(tag, &pCurFile->pszCurrent[i], pCurFile));
628 pCurFile->pszCurrent += i;
629 }
630 catch (kError::enmErrors enmErrorCd)
631 {
632 fprintf(phLog, "%s(%ld) : error: kSql tag - %s (errorcode=%d)\n",
633 pCurFile->getFilename(), pCurFile->getLineNumber()+1,
634 kError::queryDescription(enmErrorCd), enmErrorCd);
635 /* fake entry */
636 if (enmErrorCd != kError::error_invalid_tag)
637 lifoSql.push(new kSqlEntry());
638 TAG_PARAMETER_LIST_UNREF;
639 }
640
641 return ulRc;
642}
643
644
645/**
646 * Tag function - /kSql.
647 * @returns low word: number of errors
648 * high word: number of warnings
649 * @remark See TAG_PARAMETER_LIST for parameters.
650 * Use TAG_PARAMETERS when calling this function.
651 */
652static unsigned long tagEndkSql(TAG_PARAMETER_LIST)
653{
654 unsigned long ulRc = 0;
655 kSqlEntry *pCurSql;
656
657 pCurSql = lifoSql.pop();
658 if (pCurSql != NULL)
659 {
660 if (!pCurSql->eof())
661 {
662 if (pCurSql->fetch())
663 {
664 if (pCurSql->queryFileEntry() != NULL)
665 {
666 /* three cases: current file, lifoFile or lifoUsed */
667 if (pCurSql->queryFileEntry() == pCurFile)
668 { /* 1. current file */
669 pCurFile->pszCurrent = pCurSql->queryBackTrackPos();
670 }
671 else if (lifoFile.exists(pCurSql->queryFileEntry()))
672 { /* 2. lifoFile */
673 lifoFile.popPush(pCurSql->queryFileEntry(), lifoUsed);
674 pCurFile = lifoFile.pop();
675 if (pCurFile != NULL)
676 pCurFile->pszCurrent = pCurSql->queryBackTrackPos();
677 else
678 {
679 fprintf(phLog, "fatal internal error(%s, %ld, %s, %d): kSqlEntry - pCurFile == NULL\n",
680 pCurFile->getFilename(), pCurFile->getLineNumber()+1, __FILE__, __LINE__);
681 return (unsigned long)~0;
682 }
683 }
684 else if (lifoUsed.exists(pCurSql->queryFileEntry()))
685 { /* 3. lifoUsed */
686 lifoFile.popPush(NULL, lifoUsed);
687 assert(lifoFile.isEmpty());
688 if (rebuildFileLIFO(lifoFile, lifoUsed, pCurSql->queryFileEntry()))
689 {
690 pCurFile = lifoFile.pop(); assert(pCurFile == pCurSql->queryFileEntry());
691 pCurFile->pszCurrent = pCurSql->queryBackTrackPos();
692 }
693 else
694 {
695 fprintf(phLog, "fatal internal error(%s, %ld, %s, %d): rebuildLIFO failed\n",
696 pCurFile->getFilename(), pCurFile->getLineNumber()+1, __FILE__, __LINE__);
697 return (unsigned long)~0;
698 }
699 }
700 else
701 {
702 fprintf(phLog, "internal error(%s, %ld, %s, %d): pFileEntry == NULL\n",
703 pCurFile->getFilename(), pCurFile->getLineNumber()+1, __FILE__, __LINE__);
704 pCurSql = lifoSql.pop(); /* neutralize the push below */
705 }
706
707 lifoSql.push(pCurSql);
708 pCurSql = NULL;
709 }
710 else
711 {
712 fprintf(phLog, "internal error(%s, %ld, %s, %d): kSqlEntry - pFileEntry == NULL\n",
713 pCurFile->getFilename(), pCurFile->getLineNumber()+1, __FILE__, __LINE__);
714 pCurFile->pszCurrent += i;
715 }
716 }
717 else
718 {
719 fprintf(phLog, "%s(%ld) : error: sql error, %s\n",
720 pCurFile->getFilename(), pCurFile->getLineNumber()+1,
721 pCurSql->querySqlLastError());
722 pCurFile->pszCurrent += i;
723 }
724 }
725
726 /* finished processing this tag? then delete it! */
727 if (pCurSql != NULL)
728 {
729 delete pCurSql;
730 pCurFile->pszCurrent += i;
731 }
732 }
733 else
734 {
735 fprintf(phLog, "%s(%ld) : error: unexpected '/kSql' tag.\n",
736 pCurFile->getFilename(), pCurFile->getLineNumber()+1);
737 pCurFile->pszCurrent += i;
738 TAG_PARAMETER_LIST_UNREF;
739 }
740
741 return ulRc;
742}
743
744
745/**
746 * Tag function - kTemplate.
747 * @returns low word: number of errors
748 * high word: number of warnings
749 * @param phFile Reference to current output file.
750 * @param pOptions Pointer to the options struct.
751 * @remark See TAG_PARAMETER_LIST for parameters.
752 * Use TAG_PARAMETERS when calling this function.
753 */
754static unsigned long tagkTemplate(TAG_PARAMETER_LIST, FILE * &phFile, const POPTIONS pOptions)
755{
756 unsigned long ulRc = 0;
757 const char *pszFilename;
758
759 /* verify parameters */
760 if (tag.getParameterCount() > 1 && tag.getParameterCount() == 0)
761 {
762 fprintf(phLog, "%s(%ld) : warning: kTemplate - incorrect number of parameters.\n",
763 pCurFile->getFilename(), pCurFile->getLineNumber()+1);
764 ulRc += 0x00010000;
765 }
766
767 pszFilename = tag.queryParameter("filename");
768 if (pszFilename != NULL)
769 {
770 char szFullFileName[CCHMAXPATH];
771 if (phFile != NULL)
772 fclose(phFile);
773 if (strlen(pszFilename) + strlen(pOptions->pszBaseDir) + 1 + 1 < sizeof(szFullFileName))
774 {
775 sprintf(&szFullFileName[0], "%s\\%s", pOptions->pszBaseDir, pszFilename);
776 phFile = fopen(&szFullFileName[0], "wb");
777 if (phFile != NULL)
778 fprintf(phLog, "%s(%ld) : info: new output file, '%s'.\n",
779 pCurFile->getFilename(), pCurFile->getLineNumber()+1, &szFullFileName[0]);
780 else
781 {
782 fprintf(phLog, "%s(%ld) : error: kTemplate - error opening output file '%s'.\n",
783 pCurFile->getFilename(), pCurFile->getLineNumber()+1, &szFullFileName[0]);
784 ulRc += 0x00000001;
785 }
786 }
787 else
788 {
789 fprintf(phLog, "%s(%ld) : error: kTemplate - filename and base dir is too long! '%s\\%s'.\n",
790 pCurFile->getFilename(), pCurFile->getLineNumber()+1, pOptions->pszBaseDir, pszFilename);
791 ulRc += 0x00000001;
792 }
793 }
794 else
795 {
796 fprintf(phLog, "%s(%ld) : error: kTemplate - filename is missing.\n",
797 pCurFile->getFilename(), pCurFile->getLineNumber()+1);
798 ulRc += 0x00000001;
799 TAG_PARAMETER_LIST_UNREF;
800 }
801
802 pCurFile->pszCurrent += i;
803
804 return ulRc;
805}
806
807
808/**
809 * Tag function - kInclude.
810 * @returns low word: number of errors
811 * high word: number of warnings
812 * @remark See TAG_PARAMETER_LIST for parameters.
813 * Use TAG_PARAMETERS when calling this function.
814 */
815static unsigned long tagkInclude(TAG_PARAMETER_LIST)
816{
817 unsigned long ulRc = 0;
818
819 try
820 {
821 lifoFile.push(pCurFile);
822 pCurFile = new kFileEntry(tag, pCurFile->pszCurrent+i, pCurFile);
823 }
824 catch (kError::enmErrors enmErrorCd)
825 {
826 fprintf(phLog, "%s(%ld) : error: kInclude - %s\n",
827 pCurFile->getFilename(), pCurFile->getLineNumber()+1,
828 kError::queryDescription(enmErrorCd));
829 pCurFile = lifoFile.pop();
830 ulRc = 0x00000001;
831 TAG_PARAMETER_LIST_UNREF;
832 }
833
834 return ulRc;
835}
836
837
838/**
839 * Tag function - kGraph.
840 * @returns low word: number of errors
841 * high word: number of warnings
842 * @remark See TAG_PARAMETER_LIST for parameters.
843 * Use TAG_PARAMETERS when calling this function.
844 */
845static unsigned long tagkGraph(TAG_PARAMETER_LIST, const POPTIONS pOptions)
846{
847 unsigned long ulRc = 0;
848 /**
849 * Tag description:
850 * <!kGraph filename="graph.gif" type=<lines|...> [subtype=<normal|...>]
851 * data="sql x,y,color,legend from...."
852 * data="sql x,y,color,legend from...."
853 * data="sql x,y,color,legend from...."
854 * ...
855 * >
856 * [] = optional
857 */
858 try
859 {
860 kGraph graph(tag, pOptions->pszBaseDir);
861 graph.showWarnings(phLog, pCurFile);
862 graph.save();
863 }
864 catch (kError::enmErrors emnErrorCd)
865 {
866 fprintf(phLog, "%s(%ld) : error: kGraph - %s.\n",
867 pCurFile->getFilename(), pCurFile->getLineNumber()+1,
868 kError::queryDescription(emnErrorCd));
869 ulRc = 0x00000001;
870 TAG_PARAMETER_LIST_UNREF;
871 }
872 pCurFile->pszCurrent += i;
873
874 return ulRc;
875}
876
877
878/**
879 * Tag function - kDefine.
880 * @returns low word: number of errors
881 * high word: number of warnings
882 * @remark See TAG_PARAMETER_LIST for parameters.
883 * Use TAG_PARAMETERS when calling this function.
884 */
885static unsigned long tagkDefine(TAG_PARAMETER_LIST)
886{
887 unsigned long ulRc = 0;
888
889 fprintf(phLog, "%s(%ld) : error: kDefine - This tag is not implemented yet.\n",
890 pCurFile->getFilename(), pCurFile->getLineNumber()+1);
891 ulRc = 0x00000001;
892 TAG_PARAMETER_LIST_UNREF;
893
894 return ulRc;
895}
896
897
898/**
899 * Tag function - kUndef.
900 * @returns low word: number of errors
901 * high word: number of warnings
902 * @remark See TAG_PARAMETER_LIST for parameters.
903 * Use TAG_PARAMETERS when calling this function.
904 */
905static unsigned long tagkUndef(TAG_PARAMETER_LIST)
906{
907 unsigned long ulRc = 0;
908
909 fprintf(phLog, "%s(%ld) : error: kUndef - This tag is not implemented yet.\n",
910 pCurFile->getFilename(), pCurFile->getLineNumber()+1);
911 ulRc = 0x00000001;
912 TAG_PARAMETER_LIST_UNREF;
913
914 return ulRc;
915}
916
917
918/**
919 * Tag function - kIf.
920 * @returns low word: number of errors
921 * high word: number of warnings
922 * @remark See TAG_PARAMETER_LIST for parameters.
923 * Use TAG_PARAMETERS when calling this function.
924 */
925static unsigned long tagkIf(TAG_PARAMETER_LIST)
926{
927 unsigned long ulRc = 0;
928
929 fprintf(phLog, "%s(%ld) : error: kIf - This tag is not implemented yet.\n",
930 pCurFile->getFilename(), pCurFile->getLineNumber()+1);
931 ulRc = 0x00000001;
932 TAG_PARAMETER_LIST_UNREF;
933
934 return ulRc;
935}
936
937
938/**
939 * Tag function - kElse.
940 * @returns low word: number of errors
941 * high word: number of warnings
942 * @remark See TAG_PARAMETER_LIST for parameters.
943 * Use TAG_PARAMETERS when calling this function.
944 */
945static unsigned long tagkElse(TAG_PARAMETER_LIST)
946{
947 unsigned long ulRc = 0;
948
949 fprintf(phLog, "%s(%ld) : error: kElse - This tag is not implemented yet.\n",
950 pCurFile->getFilename(), pCurFile->getLineNumber()+1);
951 ulRc = 0x00000001;
952 TAG_PARAMETER_LIST_UNREF;
953
954 return ulRc;
955}
956
957
958/**
959 * Tag function - kEndif.
960 * @returns low word: number of errors
961 * high word: number of warnings
962 * @remark See TAG_PARAMETER_LIST for parameters.
963 * Use TAG_PARAMETERS when calling this function.
964 */
965static unsigned long tagkEndif(TAG_PARAMETER_LIST)
966{
967 unsigned long ulRc = 0;
968
969 fprintf(phLog, "%s(%ld) : error: kEndif - This tag is not implemented yet.\n",
970 pCurFile->getFilename(), pCurFile->getLineNumber()+1);
971 ulRc = 0x00000001;
972 TAG_PARAMETER_LIST_UNREF;
973
974 return ulRc;
975}
976
977
978#if 0
979/**
980 * Tag function - k.
981 * @returns low word: number of errors
982 * high word: number of warnings
983 * @remark See TAG_PARAMETER_LIST for parameters.
984 * Use TAG_PARAMETERS when calling this function.
985 */
986static unsigned long tagk(TAG_PARAMETER_LIST)
987{
988 unsigned long ulRc = 0;
989
990 TAG_PARAMETER_LIST_UNREF;
991
992 return ulRc;
993}
994#endif
995
996
997/**
998 * Rebuild a fileentry based on a given file entry.
999 * @returns success indicator. TRUE / FALSE.
1000 * @param lifoFile Target.
1001 * @param lifoUsed Source.
1002 * @param pFileEntryTop New to file entry.
1003 * @remark Recusive.
1004 */
1005static BOOL rebuildFileLIFO(kLIFO<kFileEntry> &lifoFile, kLIFO<kFileEntry> &lifoUsed, const kFileEntry *pFileEntryTop)
1006{
1007 /* base case */
1008 if (pFileEntryTop == NULL)
1009 return TRUE;
1010 /* general case */
1011 pFileEntryTop = lifoUsed.get(pFileEntryTop);
1012 if (pFileEntryTop != NULL && rebuildFileLIFO(lifoFile, lifoUsed, pFileEntryTop->queryParent()))
1013 lifoFile.push((kFileEntry*)pFileEntryTop);
1014 else
1015 return FALSE;
1016 return TRUE;
1017}
1018
1019
1020/**
1021 * Duplicates a string.
1022 * @returns Pointer to stringcopy. Remeber to delete this!
1023 * @param psz Pointer to string to duplicate.
1024 */
1025static char *dupeString(const char *psz)
1026{
1027 char *pszDupe;
1028 if (psz == NULL)
1029 return NULL;
1030 pszDupe = new char[strlen(psz)+1];
1031 return strcpy(pszDupe, psz);
1032}
1033
1034
1035#if 0 // not used
1036/**
1037 * Upcases a char.
1038 * @returns Upper case of the char given in ch.
1039 * @param ch Char to capitalize.
1040 */
1041inline char upcase(char ch)
1042{
1043 return ch >= 'a' && ch <= 'z' ? (char)(ch - ('a' - 'A')) : ch;
1044}
1045
1046
1047/**
1048 * Searches for a substring in a string.
1049 * @returns Pointer to start of substring when found, NULL when not found.
1050 * @param pszStr String to be searched.
1051 * @param pszSubStr String to be searched.
1052 * @remark Depends on the upcase function.
1053 */
1054static char *stristr(const char *pszStr, const char *pszSubStr)
1055{
1056 int cchSubStr = strlen(pszSubStr);
1057 int i = 0;
1058
1059 while (*pszStr != '\0' && i < cchSubStr)
1060 {
1061 i = 0;
1062 while (i < cchSubStr && pszStr[i] != '\0' &&
1063 (upcase(pszStr[i]) == upcase(pszSubStr[i])))
1064 i++;
1065 pszStr++;
1066 }
1067
1068 return (char*)(*pszStr != '\0' ? pszStr - 1 : NULL);
1069}
1070
1071
1072/**
1073 * Trims a string, that is removing blank spaces at start and end.
1074 * @returns Pointer to first non-blank char.
1075 * @param psz Pointer to string.
1076 * @result Blank at end of string is removed. ('\0' is moved to the left.)
1077 */
1078static char *trim(char *psz)
1079{
1080 int i;
1081 if (psz == NULL)
1082 return NULL;
1083 while (*psz == ' ')
1084 psz++;
1085 i = strlen(psz) - 1;
1086 while (i >= 0 && psz[i] == ' ')
1087 i--;
1088 psz[i+1] = '\0';
1089 return psz;
1090}
1091
1092
1093/**
1094 * Trims left side of a string; that is removing blank spaces at start.
1095 * @returns Pointer to first non-blank char.
1096 * @param psz Pointer to string.
1097 * @remark non-const edtion.
1098 */
1099static char *ltrim(char *psz)
1100{
1101 while (*psz == ' ')
1102 psz++;
1103 return psz;
1104}
1105
1106
1107/**
1108 * Trims left side of a string; that is removing blank spaces.
1109 * @returns Pointer to first non-blank char.
1110 * @param psz Pointer to string.
1111 * @remark const edtion.
1112 */
1113static const char *ltrim(const char *psz)
1114{
1115 while (*psz == ' ')
1116 psz++;
1117 return psz;
1118}
1119
1120
1121/**
1122 * Trims left side of a string; that is removing blank spaces, new lines and tabs.
1123 * @returns Pointer to first non-blank char.
1124 * @param psz Pointer to string.
1125 * @remark non-const edtion.
1126 */
1127static char *ltrimL(char *psz)
1128{
1129 while (*psz == ' ' || *psz == '\t' || *psz == '\r' || *psz == '\n')
1130 psz++;
1131 return psz;
1132}
1133#endif
1134
1135/**
1136 * Trims left side of a string; that is removing blank spaces, new lines and tabs.
1137 * @returns Pointer to first non-blank char.
1138 * @param psz Pointer to string.
1139 * @remark const edtion.
1140 */
1141static const char *ltrimL(const char *psz)
1142{
1143 while (*psz == ' ' || *psz == '\t' || *psz == '\r' || *psz == '\n')
1144 psz++;
1145 return psz;
1146}
1147
1148
1149/**
1150 * Formats a raw message to a readable message.
1151 * @returns Pointer to a static data field containing the formatted message.
1152 * @param pszMsg message to format.
1153 */
1154const char *kError::formatMessage(const char *pszMsg)
1155{
1156 static char szData[256];
1157 int i = 0;
1158
1159 /* skip first word */
1160 while (*pszMsg != '_' && *pszMsg != '\0')
1161 pszMsg++;
1162 if (pszMsg != '\0')
1163 {
1164 pszMsg++;
1165 do
1166 {
1167 if (*pszMsg == '_')
1168 szData[i++] = ' ';
1169 else
1170 szData[i++] = *pszMsg;
1171 } while (*pszMsg++ != '\0');
1172
1173 strcat(&szData[0], ".");
1174 }
1175 else
1176 szData[0] = '\0';
1177
1178 return &szData[0];
1179}
1180
1181
1182/**
1183 * Give a description of an error code.
1184 * @returns "Read only"/const string. Never NULL.
1185 * @param emnError Error code.
1186 */
1187const char *kError::queryDescription(kError::enmErrors enmError)
1188{
1189 switch (enmError)
1190 {
1191 case error_sql_failed:
1192 return kSqlEntry::querySqlLastError();
1193
1194 ErrorDescCase(no_error );
1195 ErrorDescCase(error_unexpected_end_of_string );
1196 ErrorDescCase(error_unexpected_eot );
1197 ErrorDescCase(error_invalid_tag_start_char );
1198 ErrorDescCase(error_invalid_tagname );
1199 ErrorDescCase(error_invalid_tag );
1200 ErrorDescCase(error_no_filename );
1201 ErrorDescCase(error_unexpected_eof );
1202 ErrorDescCase(error_unbalanced_quotes );
1203 ErrorDescCase(error_eot_not_found );
1204 ErrorDescCase(error_opening_file );
1205 ErrorDescCase(error_determing_file_size );
1206 ErrorDescCase(error_new_failed );
1207 ErrorDescCase(error_reading_file );
1208 ErrorDescCase(error_incorrect_number_of_parameters );
1209 ErrorDescCase(error_missing_sql );
1210 ErrorDescCase(error_invalid_sql_tag );
1211 ErrorDescCase(error_opening_output_file );
1212 ErrorDescCase(error_graph_to_small );
1213 ErrorDescCase(error_graph_type_must_be_specified_before_data );
1214 ErrorDescCase(error_data_param_is_missing_value );
1215 ErrorDescCase(error_type_param_is_missing_value );
1216 ErrorDescCase(error_invalid_type );
1217 ErrorDescCase(error_filename_param_is_missing_value );
1218 ErrorDescCase(error_width_cx_param_is_missing_value );
1219 ErrorDescCase(error_height_cy_param_is_missing_value );
1220 ErrorDescCase(error_graph_type_is_missing );
1221 ErrorDescCase(error_graph_subtype_is_invalid );
1222 ErrorDescCase(error_filename_is_missing );
1223 ErrorDescCase(error_no_data );
1224 ErrorDescCase(error_invalid_dimentions );
1225 ErrorDescCase(error_data_param_is_missing_sql_statement );
1226 ErrorDescCase(error_xstart_param_is_missing_value );
1227 ErrorDescCase(error_xend_param_is_missing_value );
1228 ErrorDescCase(error_ystart_param_is_missing_value );
1229 ErrorDescCase(error_yend_param_is_missing_value );
1230
1231 ErrorDescCase(warning_illegal_string_char );
1232 ErrorDescCase(warning_too_many_parameters );
1233 ErrorDescCase(warning_unexpected_end_of_string );
1234 ErrorDescCase(warning_unexpected_eot );
1235 ErrorDescCase(warning_failed_to_open_background_image );
1236 ErrorDescCase(warning_failed_to_load_background_image );
1237 ErrorDescCase(warning_invalid_sub_type );
1238 ErrorDescCase(warning_title_param_is_missing_value );
1239 ErrorDescCase(warning_titlex_param_is_missing_value );
1240 ErrorDescCase(warning_titley_param_is_missing_value );
1241 ErrorDescCase(warning_invalid_parameter );
1242 ErrorDescCase(warning_legend_is_not_implemented_yet );
1243 ErrorDescCase(warning_failed_to_convert_date );
1244 ErrorDescCase(warning_invalid_color_value );
1245 ErrorDescCase(warning_to_many_fields_in_data_sql );
1246 ErrorDescCase(warning_pie_not_implemented_yet );
1247 ErrorDescCase(warning_negative_values_are_not_supported_yet );
1248 ErrorDescCase(warning_background_param_is_missing_value );
1249 ErrorDescCase(warning_backgroundcolor_is_invalid );
1250 ErrorDescCase(warning_backgroundcolor_param_is_missing_value );
1251 ErrorDescCase(warning_foregroundcolor_is_invalid );
1252 ErrorDescCase(warning_foregroundcolor_param_is_missing_value );
1253 ErrorDescCase(warning_axiscolor_is_invalid );
1254 ErrorDescCase(warning_axiscolor_param_is_missing_value );
1255 }
1256 return "unknown error";
1257}
1258
1259
1260/**
1261 * Query for a warning.
1262 * @returns Const pointer to warning description. NULL if no warning.
1263 */
1264const char *kTag::queryWarning(void)
1265{
1266 return enmWarning != kError::no_error ? kError::queryDescription(enmWarning) : NULL;
1267}
1268
1269
1270/**
1271 * Anayses an tag: get tagname and parameters (if any).
1272 * @param pszTag Pointer to tag buffer.
1273 * @sketch
1274 * @remark Throws kError::emnErrors on error.
1275 */
1276void kTag::analyseTag(const char *pszTag) throw(kError::enmErrors)
1277{
1278 char *psz = &szTag[0];
1279
1280 /* skip blanks at start */
1281 while (*pszTag == ' ')
1282 pszTag++;
1283
1284 /* tagname */
1285 if (*pszTag++ != '<')
1286 throw(kError::error_invalid_tag_start_char);
1287 pszTag = copyTag(psz, pszTag);
1288 pszTagname = psz;
1289 psz += strlen(psz) + 1;
1290
1291 /* parameters */
1292 if (strncmp(pszTagname, "!--", 3) != 0) /* ignore comments */
1293 {
1294 try
1295 {
1296 pszTag = ltrimL(pszTag);
1297 while (*pszTag != '>' && *pszTag != '\0' && cParameters < TAG_MAX_PARAMETERS)
1298 {
1299 /* parametername */
1300 pszTag = copyParameterName(psz, pszTag);
1301 apszParameters[cParameters] = psz;
1302 psz += strlen(psz) + 1;
1303
1304 /* parametervalue */
1305 apszValues[cParameters] = NULL;
1306 pszTag = ltrimL(pszTag);
1307 if (*pszTag == '=')
1308 {
1309 pszTag = ltrimL(pszTag + 1);
1310 if (*pszTag != '\"')
1311 pszTag = copyParameterValue1(psz, pszTag);
1312 else
1313 pszTag = copyParameterValue2(psz, pszTag);
1314 apszValues[cParameters] = psz;
1315 psz += strlen(psz) + 1;
1316 }
1317
1318 /* parameter finished - next */
1319 cParameters++;
1320 pszTag = ltrimL(pszTag);
1321 }
1322
1323 /* check for warnings */
1324 if (*pszTag != '>' && cParameters >= TAG_MAX_PARAMETERS)
1325 throw(kError::warning_too_many_parameters);
1326 if (*pszTag != '>')
1327 throw(kError::warning_unexpected_eot);
1328 }
1329 catch (kError::enmErrors enmErrorCd)
1330 {
1331 /* no fatal error --> warning */
1332 enmWarning = enmErrorCd;
1333 }
1334 }
1335}
1336
1337
1338/**
1339 * Copy a tagname.
1340 * @returns Pointer to first char after the tag. (pszFrom)
1341 * @param pszTo Pointer to target.
1342 * @param pszFrom Pointer to value string.
1343 * @remark Throws kError::emnErrors on error, fatal for this object.
1344 */
1345const char *kTag::copyTag(char *pszTo, const char *pszFrom) throw(kError::enmErrors)
1346{
1347 const char *psz = pszTo;
1348
1349 /* checks for errors */
1350 if (*pszFrom == '>')
1351 throw(kError::error_unexpected_eot);
1352
1353 /* skip to start of tagname */
1354 while (*pszFrom == ' ')
1355 pszFrom++;
1356
1357 /* copy string */
1358 while (
1359 (*pszFrom >= 'a' && *pszFrom <= 'z') ||
1360 (*pszFrom >= 'A' && *pszFrom <= 'Z') ||
1361 (*pszFrom >= '0' && *pszFrom <= '9') ||
1362 *pszFrom == '/' ||
1363 *pszFrom == '!' || *pszFrom == '-' //<!--> and <!k...>
1364 )
1365 *pszTo++ = *pszFrom++;
1366 *pszTo = '\0';
1367
1368 /* checks for errors */
1369 if (*pszFrom == '\0')
1370 throw(kError::error_unexpected_eot);
1371 if (*pszFrom != '>' && *pszFrom != ' ' && strncmp("!--", psz, 3) != 0) //<!--aasdfv-->
1372 throw(kError::error_invalid_tagname);
1373
1374 return pszFrom;
1375}
1376
1377
1378/**
1379 * Copy a
1380 * @returns Pointer to first char after the tag. (pszFrom)
1381 * @param pszTo Pointer to target.
1382 * @param pszFrom Pointer to value string.
1383 * @remark Throws kError::emnErrors on error, fatal for this object.
1384 */
1385const char *kTag::copyParameterName(char *pszTo, const char *pszFrom) throw(kError::enmErrors)
1386{
1387 /* checks for errors */
1388 if (*pszFrom == '>')
1389 throw(kError::warning_unexpected_eot);
1390
1391 /* skip to start of parameter name */
1392 while (*pszFrom == ' ')
1393 pszFrom++;
1394
1395 /* copy string */
1396 while (
1397 (*pszFrom >= 'a' && *pszFrom <= 'z') ||
1398 (*pszFrom >= 'A' && *pszFrom <= 'Z') ||
1399 (*pszFrom >= '0' && *pszFrom <= '9')
1400 )
1401 *pszTo++ = *pszFrom++;
1402 *pszTo = '\0';
1403
1404 /* checks for errors */
1405 if (*pszFrom == '\0')
1406 throw(kError::warning_unexpected_eot);
1407
1408 return pszFrom;
1409}
1410
1411
1412/**
1413 * Copy a simple parameter value. A simple parameter value is not enclosed in "".
1414 * @returns Pointer to first char after the value. (pszFrom)
1415 * @param pszTo Pointer to target.
1416 * @param pszFrom Pointer to value string.
1417 * @remark Throws kError::emnErrors on warning, do not continue analysis.
1418 */
1419const char *kTag::copyParameterValue1(char *pszTo, const char *pszFrom) throw(kError::enmErrors)
1420{
1421 /* skip to start of parameter value */
1422 while (*pszFrom == ' ')
1423 pszFrom++;
1424
1425 /* copy string */
1426 while (
1427 (*pszFrom >= 'a' && *pszFrom <= 'z') ||
1428 (*pszFrom >= 'A' && *pszFrom <= 'Z') ||
1429 (*pszFrom >= '0' && *pszFrom <= '9') ||
1430 *pszFrom == '.' || *pszFrom == ','
1431 )
1432 *pszTo++ = *pszFrom++;
1433 *pszTo = '\0';
1434
1435 /* check for warnings */
1436 if (*pszFrom == '\0')
1437 throw(kError::warning_unexpected_eot);
1438 if (*pszFrom != ' ' && *pszFrom != '>' && *pszFrom != '\n' && *pszFrom != '\r')
1439 throw(kError::warning_illegal_string_char);
1440
1441 return pszFrom;
1442}
1443
1444
1445/**
1446 * Copy an enclosed parameter value. Enclosed in "".
1447 * @returns Pointer to first char after the value. (pszFrom)
1448 * @param pszTo Pointer to target.
1449 * @param pszFrom Pointer to value string.
1450 * @remark Throws kError::emnErrors on warning, do not continue analysis.
1451 */
1452const char *kTag::copyParameterValue2(char *pszTo, const char *pszFrom) throw(kError::enmErrors)
1453{
1454 /* skip to start " */
1455 while (*pszFrom != '\"' && *pszFrom != '>' && *pszFrom != '\0')
1456 pszFrom++;
1457 if (*pszFrom != '\"')
1458 throw(kError::error_unexpected_end_of_string);
1459 pszFrom++;
1460
1461 /* copy string */
1462 while (*pszFrom != '\"' /*&& *pszFrom != '>'*/ && *pszFrom != '\0')
1463 *pszTo++ = *pszFrom++;
1464 *pszTo = '\0';
1465
1466 /* check for warnings */
1467 if (*pszFrom != '\"')
1468 throw(kError::warning_unexpected_end_of_string);
1469
1470 return pszFrom + 1;
1471}
1472
1473
1474/**
1475 * Is this the correct tag?
1476 * @returns TRUE pszTagname matches this->pszTagname, else FALSE.
1477 * @param pszTagname Name to match.
1478 */
1479BOOL kTag::isTag(const char *pszTagname) const
1480{
1481 if (pszTagname == NULL)
1482 return FALSE;
1483 return stricmp(pszTagname, this->pszTagname) == 0;
1484}
1485
1486
1487/**
1488 * Constructor.
1489 * @param pszTag Pointer to tag to analyse.
1490 * @remark Throws kError::emnErrors on warning, do not continue analysis.
1491 */
1492kTag::kTag(const char *pszTag) throw(kError::enmErrors)
1493 : pszTagname(NULL), cParameters(0), enmWarning(kError::no_error)
1494{
1495 analyseTag(pszTag);
1496}
1497
1498
1499/**
1500 * Destructor.
1501 */
1502kTag::~kTag(void)
1503{
1504}
1505
1506
1507/**
1508 * Gets the tag name.
1509 * @returns Const pointer to tag name. Not NULL.
1510 */
1511const char *kTag::getTagname(void) const
1512{
1513 return pszTagname;
1514}
1515
1516
1517/**
1518 * Gets the number of parameters found for this tag.
1519 * @returns Number of parameters. -1 on error (should never occur!).
1520 */
1521unsigned long kTag::getParameterCount(void) const
1522{
1523 return cParameters;
1524}
1525
1526
1527/**
1528 * Seek for the value of a given parameter.
1529 * @returns "Readonly"/const pointer to value. NULL if no value or parameter not found.
1530 * @param pszParameter Pointer to parameter name.
1531 * @remark Parameters are case insensitive!
1532 */
1533const char *kTag::queryParameter(const char *pszParameter) const
1534{
1535 unsigned int i = 0;
1536 while (i < cParameters && stricmp(apszParameters[i], pszParameter) != 0)
1537 i++;
1538 return i < cParameters ? apszValues[i]: NULL;
1539}
1540
1541
1542/**
1543 * Checks if a parameter exists.
1544 * @returns TRUE: exists, FALSE: does not exist.
1545 * @param pszParameter Pointer to parameter name.
1546 * @remark Parameters are case insensitive!
1547 */
1548BOOL kTag::queryExists(const char *pszParameter) const
1549{
1550 unsigned int i = 0;
1551 while (i < cParameters && stricmp(apszParameters[i], pszParameter) != 0)
1552 i++;
1553 return i < cParameters;
1554}
1555
1556
1557/**
1558 * Gets the parameter name for the given parameter number.
1559 * @returns Pointer to "readonly"/const parametername. NULL if outof range.
1560 * @param ulOrdinal Parameter number. 0-based.
1561 */
1562const char *kTag::queryParameterName(unsigned long ulOrdinal) const
1563{
1564 return ulOrdinal < cParameters ? apszParameters[ulOrdinal] : NULL;
1565}
1566
1567
1568/**
1569 * Gets the value for the given parameter number.
1570 * @returns Pointer to "readonly"/const parameter value. NULL if outof range or no value.
1571 * @param ulOrdinal Parameter number. 0-based.
1572 * @remark Return value NULL doesn't have to be an error, the parameter may not have a value!
1573 */
1574const char *kTag::queryParameterValue(unsigned long ulOrdinal) const
1575{
1576 return ulOrdinal < cParameters ? apszValues[ulOrdinal] : NULL;
1577}
1578
1579
1580/**
1581 * Opens and reads a file into memory.
1582 * @param pszFilename Pointer to input filename.
1583 * @remark throws error code (enum).
1584 */
1585void kFileEntry::openAndReadFile(const char *pszFilename) throw (kError::enmErrors)
1586{
1587 FILE *phFile;
1588
1589 phFile = fopen(pszFilename, "rb");
1590 if (phFile == NULL)
1591 throw(kError::error_opening_file);
1592 if (fseek(phFile, 0, SEEK_END) == 0 &&
1593 (cbFile = ftell(phFile)) != 0 &&
1594 fseek(phFile, 0, SEEK_SET) == 0
1595 )
1596 {
1597 pszFile = new char[cbFile+1];
1598 if (pszFile != NULL)
1599 {
1600 memset(pszFile, 0, (size_t)(cbFile+1));
1601 if (fread(pszFile, cbFile, 1, phFile) != 1)
1602 {
1603 fclose(phFile);
1604 throw(kError::error_reading_file);
1605 }
1606 fclose(phFile);
1607 }
1608 else
1609 {
1610 fclose(phFile);
1611 throw(kError::error_new_failed);
1612 }
1613 }
1614 else
1615 {
1616 fclose(phFile);
1617 throw(kError::error_determing_file_size);
1618 }
1619}
1620
1621
1622/**
1623 * Creates a file entry object.
1624 * @param pszFilename Pointer input filename.
1625 * @sketch Open file.
1626 * Find file length.
1627 * Read file into memory.
1628 * @remark throws error code enum.
1629 */
1630kFileEntry::kFileEntry(const char *pszFilename) throw (kError::enmErrors) : pParent(NULL), pszParent(NULL)
1631{
1632 openAndReadFile(pszFilename);
1633 strcpy(&szFilename[0], pszFilename);
1634 pszCurrent = pszFile;
1635}
1636
1637
1638/**
1639 * Create a file entry object with parent from a include statement.
1640 * @parma tag Reference to tag object.
1641 * @param pszParent Current pointer in parent file.
1642 * @param pParent Pointer to current file entry.
1643 * @sketch Open and read file into memory.
1644 * Copy filename.
1645 * Set current pointer.
1646 * @remark throws error code enum.
1647 * TODO - use not fully implemented.
1648 */
1649kFileEntry::kFileEntry(const kTag &tag, const char *pszParent, const kFileEntry *pParent) throw (kError::enmErrors)
1650 : pParent(pParent), pszParent(pszParent)
1651{
1652 const char *pszFilename;
1653
1654 /* verify tag */
1655 if (stricmp(tag.getTagname(), "!kInclude"))
1656 throw(kError::error_invalid_tag);
1657
1658 /* verify parameters */
1659 if (tag.getParameterCount() != 1)
1660 throw(kError::error_incorrect_number_of_parameters);
1661
1662 /* get filename */
1663 pszFilename = tag.queryParameter("filename");
1664 if (pszFilename == NULL)
1665 throw(kError::error_no_filename);
1666
1667 /* copy filename */
1668 strcpy(&szFilename[0], pszFilename);
1669
1670 /* open and read file */
1671 openAndReadFile(&szFilename[0]);
1672 pszCurrent = pszFile;
1673}
1674
1675
1676/**
1677 * Destroys the file entry object.
1678 */
1679kFileEntry::~kFileEntry()
1680{
1681 delete pszFile;
1682 pszCurrent = NULL;
1683
1684}
1685
1686
1687/**
1688 * Get linenumber.
1689 * @returns linenumber, -1 on error
1690 * @remark 0 based.
1691 */
1692long kFileEntry::getLineNumber(void) const
1693{
1694 long cLines = 0;
1695 char *psz = pszFile;
1696
1697 while (psz < pszCurrent && *psz != '\0')
1698 {
1699 if (*psz == '\n')
1700 cLines++;
1701 psz++;
1702 }
1703
1704 return cLines;
1705}
1706
1707
1708/**
1709 * Creates a variable entry object.
1710 */
1711kVariableEntry::kVariableEntry(const char *pszName, const char *pszValue)
1712{
1713 this->pszName = dupeString(pszName);
1714 this->pszValue = dupeString(pszValue);
1715}
1716
1717
1718/**
1719 * Destroys the variable entry object.
1720 */
1721kVariableEntry::~kVariableEntry()
1722{
1723 if (pszName != NULL)
1724 delete pszName;
1725 if (pszValue != NULL)
1726 delete pszValue;
1727}
1728
1729
1730/**
1731 * Executes the sql query present in pszSql.
1732 * @remark Throws error code.
1733 */
1734void kSqlEntry::dbExecuteQuery(void) throw (kError::enmErrors)
1735{
1736 pres = ::dbExecuteQuery(pszSql);
1737 if (pres != NULL)
1738 {
1739 cRows = dbQueryResultRows(pres);
1740 dbFetch();
1741 }
1742 else
1743 throw(kError::error_sql_failed);
1744}
1745
1746
1747/**
1748 * Fetch next row of results.
1749 * @returns success indicator. TRUE / FASLE.
1750 * @result lifoVariables are destroyed.
1751 */
1752BOOL kSqlEntry::dbFetch(void)
1753{
1754 BOOL fRc = FALSE;
1755
1756 lifoVariables.destroy();
1757
1758 if (cRows != 0)
1759 {
1760 cRows--;
1761 fRc = ::dbFetch(pres, dbFetchCallBack, (void*)&lifoVariables);
1762 }
1763
1764 return fRc;
1765}
1766
1767
1768/**
1769 * Callback function used when receiving data from a dbFetch call.
1770 * This function creates a new kVariableEntry from the input, and pushes it
1771 * onto the variable lifo.
1772 * @returns 0 success, -1 error.
1773 * @param pszValue Field value. Used as variable value.
1774 * @param pszFieldName Field name. Used as variable name.
1775 * @param pvUser Pointer to user defined data, here a variable lifo.
1776 */
1777static long _System dbFetchCallBack(const char *pszValue, const char *pszFieldName, void *pvUser)
1778{
1779 kLIFO<kVariableEntry> *plifoVar = (kLIFO<kVariableEntry> *) pvUser;
1780 kVariableEntry *pVarEntry;
1781
1782 if (pszFieldName == NULL)
1783 return -1;
1784 if (pszValue == NULL)
1785 pszValue = "-NULL-";
1786 pVarEntry = new kVariableEntry(pszFieldName, pszValue);
1787 if (pVarEntry != NULL)
1788 plifoVar->push(pVarEntry);
1789 else
1790 return -1;
1791 return 0;
1792}
1793
1794
1795/**
1796 * Frees the database result.
1797 * @remark Called during destruction of object.
1798 */
1799void kSqlEntry::dbFreeResult(void)
1800{
1801 if (pres != NULL)
1802 {
1803 ::dbFreeResult(pres);
1804 pres = NULL;
1805 }
1806}
1807
1808
1809/**
1810 * Creates a dummy sql entry.
1811 */
1812kSqlEntry::kSqlEntry() :
1813 pszSql(NULL), pres(NULL), cRows(0), pFileEntry(pFileEntry), pszCurrent(pszCurrent)
1814{
1815}
1816
1817
1818/**
1819 * Interpret the <!kSql ...> tag and creates an sql entry object from it.
1820 * @param pszCurrent Current pointer into the file entry object.
1821 * @param pFileEntry Pointer to current file entry object.
1822 * @remark throws error code.
1823 */
1824kSqlEntry::kSqlEntry(const kTag &tag, const char *pszCurrent, const kFileEntry *pFileEntry) throw (kError::enmErrors)
1825 : pszSql(NULL), pres(NULL), cRows(0), pFileEntry(pFileEntry), pszCurrent(pszCurrent)
1826{
1827 const char *pszSql;
1828
1829 /* verify tag */
1830 if (!tag.isTag("!kSql"))
1831 throw(kError::error_invalid_tag);
1832 if (!tag.queryExists("sql"))
1833 throw(kError::error_missing_sql);
1834 if (tag.getParameterCount() > 1)
1835 throw(kError::error_incorrect_number_of_parameters);
1836
1837 /* get sql */
1838 pszSql = tag.queryParameter("sql");
1839 if (pszSql == NULL || strlen(pszSql) == 0)
1840 throw(kError::error_invalid_sql_tag);
1841
1842 this->pszSql = dupeString(pszSql);
1843 if (this->pszSql == NULL)
1844 throw(kError::error_new_failed);
1845
1846 /* execute sql */
1847 dbExecuteQuery();
1848}
1849
1850
1851/**
1852 * Destructor.
1853 */
1854kSqlEntry::~kSqlEntry()
1855{
1856 if (pszSql != NULL)
1857 delete pszSql;
1858 dbFreeResult();
1859}
1860
1861
1862/**
1863 * Equal operator. Comapres this object with a key string.
1864 * @returns TRUE if equal, FALSE if not equal.
1865 * @param psz Pointer to key string.
1866 */
1867BOOL kSqlEntry::operator ==(const char *psz) const
1868{
1869 return lifoVariables.find(psz) != NULL;
1870}
1871
1872
1873/**
1874 * Fetches the next set of data.
1875 * @returns Success indicator. TRUE / FALSE.
1876 */
1877BOOL kSqlEntry::fetch(void)
1878{
1879 return dbFetch();
1880}
1881
1882
1883/**
1884 * Finds a kVariableEntry matching a given key.
1885 * @returns Const pointer to variable node or NULL (if not found).
1886 * @param pszKey Key to match.
1887 */
1888const kVariableEntry *kSqlEntry::find(const char *pszKey) const
1889{
1890 return lifoVariables.find(pszKey);
1891}
1892
1893
1894/**
1895 * Checks if a node exists.
1896 * @returns TRUE - node exists, FALSE - node does not exist.
1897 * @param pVariableEntry Pointer to node/entry.
1898 */
1899BOOL kSqlEntry::exists(const kVariableEntry *pVariableEntry) const
1900{
1901 return lifoVariables.exists(pVariableEntry);
1902}
1903
1904
1905/**
1906 * Gets last sql error.
1907 * @returns Pointer to readonly string describing the error.
1908 */
1909const char *kSqlEntry::querySqlLastError(void)
1910{
1911 return dbGetLastErrorDesc();
1912}
1913
1914
1915/**
1916 * Finds an item in the sublist of the nodes.. Matching a given key.
1917 * @returns Pointer to subnode if found.
1918 * @param pszKey Key to match.
1919 * @remark uses the const EntryEntry *find(const char *pszKey) const; function.
1920 */
1921template <class kEntry, class kEntryEntry>
1922const kEntryEntry *kLIFO2<kEntry, kEntryEntry>::findSub(const char *pszKey) const
1923{
1924 const kEntry *pE = pTop;
1925 const kEntryEntry *pEE = NULL;
1926
1927 while (pE != NULL && (pEE = pE->find(pszKey)) == NULL)
1928 pE = (const kEntry*)pE->getNext();
1929
1930 return pEE;
1931}
1932
1933
1934/**
1935 * Checks if an item exists.
1936 * @returns TRUE - exists, FALSE - doesn't exists
1937 * @param pEntryEntry Pointer to sub not which is to exists.
1938 * @remark uses the BOOL exist(const char *pszKey) const; function.
1939 */
1940template <class kEntry, class kEntryEntry>
1941BOOL kLIFO2<kEntry, kEntryEntry>::existsSub(const kEntryEntry *pEntryEntry) const
1942{
1943 kEntry *pE = pTop;
1944 BOOL f= FALSE;
1945
1946 while (pE != NULL && !(f = pE->exists(pEntryEntry)))
1947 pE = (kEntry *)pE->getNext();
1948
1949 return f;
1950}
1951
1952
1953/**
1954 * BOOL operator.
1955 */
1956BOOL kGraphData::operator==(const kGraphData &entry) const
1957{
1958 return rdX == entry.rdX;
1959}
1960
1961
1962/**
1963 * BOOL operator.
1964 */
1965BOOL kGraphData::operator!=(const kGraphData &entry) const
1966{
1967 return rdX != entry.rdX;
1968}
1969
1970
1971/**
1972 * BOOL operator.
1973 */
1974BOOL kGraphData::operator< (const kGraphData &entry) const
1975{
1976 return rdX < entry.rdX;
1977}
1978
1979
1980/**
1981 * BOOL operator.
1982 */
1983BOOL kGraphData::operator<=(const kGraphData &entry) const
1984{
1985 return rdX <= entry.rdX;
1986}
1987
1988
1989/**
1990 * BOOL operator.
1991 */
1992BOOL kGraphData::operator> (const kGraphData &entry) const
1993{
1994 return rdX > entry.rdX;
1995}
1996
1997
1998/**
1999 * BOOL operator.
2000 */
2001BOOL kGraphData::operator>=(const kGraphData &entry) const
2002{
2003 return rdX >= entry.rdX;
2004}
2005
2006
2007/**
2008 * Adds a warning to the list of warnings.
2009 * @param enmErrorCd Warning code.
2010 */
2011void kGraphDataSet::addWarning(kError::enmErrors enmErrorCd)
2012{
2013 if (plistWarnings != NULL)
2014 plistWarnings->insert(new kWarningEntry(enmErrorCd));
2015 else
2016 assert(!"internal error");
2017}
2018
2019
2020/**
2021 * Constructs an empty dataset.
2022 * @remark Throws kError::enmErrors
2023 */
2024kGraphDataSet::kGraphDataSet() throw(kError::enmErrors)
2025 : rdCount(_NAN), ulColor(0xffffffffUL), clrColor(-1), pszLegend(NULL),
2026 fXDate(FALSE), plistWarnings(NULL)
2027{
2028}
2029
2030
2031/**
2032 * Constructs a dataset from a sql statement and a given graph type.
2033 * @param pszSql Sql-statement which gives us the data.
2034 * @param enmTypeCd This is the integer value of the graph type.
2035 * @param plistWarnings Pointer to warning list.
2036 * @remark Throws kError::enmErrors
2037 */
2038kGraphDataSet::kGraphDataSet(const char *pszSql, int enmTypeCd,
2039 kSortedList<kWarningEntry> *plistWarnings) throw(kError::enmErrors)
2040 : rdCount(_NAN), ulColor(0xffffffffUL), clrColor(-1), pszLegend(NULL),
2041 fXDate(FALSE), plistWarnings(plistWarnings)
2042{
2043 void *pres;
2044 if (pszSql == NULL || strlen(pszSql) == 0) throw(kError::error_data_param_is_missing_sql_statement);
2045 if (enmTypeCd == kGraph::unknown) throw(kError::error_graph_type_must_be_specified_before_data);
2046
2047 switch (enmTypeCd)
2048 {
2049 case kGraph::lines:
2050 {
2051 pres = ::dbExecuteQuery(pszSql);
2052 if (pres != NULL)
2053 {
2054 GRAPHCALLBACKPARAM Param = {NULL, this, NULL, (kGraph::enmType)enmTypeCd, 0};
2055 while (::dbFetch(pres, dbDataSetCallBack, (void*)&Param))
2056 Param.lSeqNbr = 0;
2057 }
2058 else
2059 throw(kError::error_sql_failed);
2060 break;
2061 }
2062
2063 case kGraph::pie:
2064 addWarning(kError::warning_pie_not_implemented_yet);
2065
2066 default:
2067 break;
2068 }
2069}
2070
2071
2072/**
2073 * Callback function used when receiving data.
2074 * @returns 0 success, -1 error.
2075 * @param pszValue Field value. Used as variable value.
2076 * @param pszFieldName Field name. Used as variable name.
2077 * @param pvUser Pointer to user defined data, here a variable lifo.
2078 */
2079long _System dbDataSetCallBack(const char *pszValue, const char *pszFieldName, void *pvUser)
2080{
2081 PGRAPHCALLBACKPARAM pParam = (PGRAPHCALLBACKPARAM)pvUser;
2082
2083 assert(pParam != NULL);
2084
2085 switch (pParam->enmTypeCd)
2086 {
2087 case kGraph::lines:
2088 {
2089 switch (pParam->lSeqNbr)
2090 {
2091 case 0:
2092 pParam->pData = new kGraphData;
2093 /* date test */
2094 if (strlen(pszValue) == (4+1+2+1+2) && pszValue[4] == '-' && pszValue[7] == '-')
2095 {
2096 strcpy(&pParam->pData->szXDate[0], pszValue);
2097 pParam->pData->rdX = kGraph::dateToDbl(&pParam->pData->szXDate[0]);
2098 pParam->pDataSet->fXDate = TRUE;
2099 }
2100 else
2101 {
2102 pParam->pData->szXDate[0] = '\0';
2103 pParam->pData->rdX = atol(pszValue);
2104 }
2105 pParam->pDataSet->listData.insert(pParam->pData);
2106 break;
2107
2108 case 1:
2109 pParam->pData->rdY = atol(pszValue);
2110 break;
2111
2112 case 2:
2113 if (pszFieldName == NULL || stricmp(pszFieldName, "legend") != 0)
2114 pParam->pDataSet->setColor(pszValue);
2115 else
2116 pParam->pDataSet->setLegend(pszValue);
2117 break;
2118
2119 case 3:
2120 if (pszFieldName == NULL || stricmp(pszFieldName, "color") != 0)
2121 pParam->pDataSet->setLegend(pszValue);
2122 else
2123 pParam->pDataSet->setColor(pszValue);
2124 break;
2125
2126 default:
2127 pParam->pDataSet->addWarning(kError::warning_to_many_fields_in_data_sql);
2128 }
2129 break;
2130 }
2131
2132 case kGraph::pie:
2133 default:
2134 break;
2135 }
2136
2137 pParam->lSeqNbr++;
2138 return 0;
2139}
2140
2141
2142/**
2143 * Destructor.
2144 */
2145kGraphDataSet::~kGraphDataSet(void)
2146{
2147 if (pszLegend != NULL) delete pszLegend;
2148}
2149
2150
2151/**
2152 * Find max X value in set.
2153 * @returns max X value. 0.0 if empty set.
2154 */
2155double kGraphDataSet::maxX(void) const
2156{
2157 double rdRet = _NAN;
2158 kGraphData *p = listData.getFirst();
2159
2160 while (p != NULL)
2161 {
2162 if (rdRet < p->rdX || rdRet != rdRet)
2163 rdRet = p->rdX;
2164 p = (kGraphData*)p->getNext();
2165 }
2166
2167 return rdRet == rdRet ? rdRet : 0.0;
2168}
2169
2170
2171/**
2172 * Find min X value in set.
2173 * @returns min X value. 0.0 if empty set.
2174 */
2175double kGraphDataSet::minX(void) const
2176{
2177 double rdRet = _NAN;
2178 kGraphData *p = listData.getFirst();
2179
2180 while (p != NULL)
2181 {
2182 if (rdRet > p->rdX || rdRet != rdRet)
2183 rdRet = p->rdX;
2184 p = (kGraphData*)p->getNext();
2185 }
2186
2187 return rdRet == rdRet ? rdRet : 0.0;
2188}
2189
2190
2191/**
2192 * Find max Y value in set.
2193 * @returns max Y value. 0.0 if empty set.
2194 */
2195double kGraphDataSet::maxY(void) const
2196{
2197 double rdRet = _NAN;
2198 kGraphData *p = listData.getFirst();
2199
2200 while (p != NULL)
2201 {
2202 if (rdRet < p->rdY || rdRet != rdRet)
2203 rdRet = p->rdY;
2204 p = (kGraphData*)p->getNext();
2205 }
2206
2207 return rdRet == rdRet ? rdRet : 0.0;
2208}
2209
2210
2211/**
2212 * Find min Y value in set.
2213 * @returns min Y value. 0.0 if empty set.
2214 */
2215double kGraphDataSet::minY(void) const
2216{
2217 double rdRet = _NAN;
2218 kGraphData *p = listData.getFirst();
2219
2220 while (p != NULL)
2221 {
2222 if (rdRet > p->rdY || rdRet != rdRet)
2223 rdRet = p->rdY;
2224 p = (kGraphData*)p->getNext();
2225 }
2226
2227 return rdRet == rdRet ? rdRet : 0.0;
2228}
2229
2230
2231/**
2232 * Sets the ulColor data member. This
2233 * @param pszColor Pointer to color string. That is "#RRGGBB".
2234 */
2235void kGraphDataSet::setColor(const char *pszColor)
2236{
2237 if (ulColor != 0xffffffffUL)
2238 return;
2239
2240 if (*pszColor == '#')
2241 {
2242 ulColor = 0;
2243 for (int i = 1; i < 7; i++)
2244 {
2245 ulColor = ulColor << 4;
2246 if (pszColor[i] >= '0' && pszColor[i] <= '9')
2247 ulColor |= pszColor[i] - '0';
2248 else if (pszColor[i] >= 'A' && pszColor[i] <= 'F')
2249 ulColor |= pszColor[i] - 'A' + 0xa;
2250 else if (pszColor[i] >= 'a' && pszColor[i] <= 'f')
2251 ulColor |= pszColor[i] - 'a' + 0xa;
2252 else
2253 addWarning(kError::warning_invalid_color_value);
2254 }
2255 }
2256 else
2257 addWarning(kError::warning_invalid_color_value);
2258}
2259
2260
2261/**
2262 * Allocates color and set the clrColor datamember.
2263 * @param pGraph Pointer to gifdraw image structure.
2264 */
2265void kGraphDataSet::setColor(gdImagePtr pGraph)
2266{
2267 if (clrColor == -1)
2268 clrColor = gdImageColorAllocate(pGraph,
2269 (int)(0xFF & (ulColor >> 16)),
2270 (int)(0xFF & (ulColor >> 8)),
2271 (int)(0xFF & ulColor));
2272}
2273
2274
2275/**
2276 * Sets the legend string.
2277 * @param pszLegend Legend.
2278 */
2279void kGraphDataSet::setLegend(const char *pszLegend)
2280{
2281 if (this->pszLegend == NULL)
2282 this->pszLegend = dupeString(pszLegend);
2283}
2284
2285
2286/**
2287 * Analyses the tag and executes SQL statements.
2288 * @param tag Tag.
2289 * @param pszBaseDir Base output directory.
2290 * @remark Throws kError::enmErrors
2291 */
2292void kGraph::analyseTag(const kTag &tag, const char *pszBaseDir) throw(kError::enmErrors)
2293{
2294 int cArgs, i;
2295 const char *pszName;
2296 const char *pszValue;
2297 /*const char *psz;
2298 long l;
2299 unsigned long ul;*/
2300
2301 /* tagname */
2302 if (stricmp(tag.getTagname(), "!kGraph") != 0)
2303 throw(kError::error_invalid_tagname);
2304
2305 /* parameters */
2306 i = 0;
2307 cArgs = (int)tag.getParameterCount();
2308 while (i < cArgs)
2309 {
2310 pszName = tag.queryParameterName(i);
2311 pszValue = tag.queryParameterValue(i);
2312
2313 if (stricmp(pszName, "data") == 0)
2314 {
2315 if ((int)enmTypeCd == unknown) throw(kError::error_graph_type_must_be_specified_before_data);
2316 if (pszValue == NULL || strlen(pszValue) == 0) throw(kError::error_data_param_is_missing_value);
2317 #if 0
2318 listDataSets.insert(new kGraphDataSet(pszValue, enmTypeCd, &listWarnings));
2319 #else
2320 fetchData(pszValue);
2321 #endif
2322 }
2323 else if (stricmp(pszName, "type") == 0)
2324 {
2325 if (pszValue == NULL) throw(kError::error_type_param_is_missing_value);
2326 if (stricmp(pszValue, "lines") == 0)
2327 enmTypeCd = lines;
2328 else if (stricmp(pszValue, "pie") == 0)
2329 enmTypeCd = pie;
2330 else
2331 throw(kError::error_invalid_type);
2332
2333 }
2334 else if (stricmp(pszName, "subtype") == 0)
2335 {
2336 if (pszValue != NULL && stricmp(pszValue, "normal") == 0)
2337 enmSubTypeCd = normal;
2338 else
2339 addWarning(kError::warning_invalid_sub_type);
2340 }
2341 else if (stricmp(pszName, "filename") == 0)
2342 {
2343 if (pszValue == NULL || strlen(pszValue) == 0) throw(kError::error_filename_param_is_missing_value);
2344 pszFilename = new char[strlen(pszValue) + 2 + strlen(pszBaseDir)];
2345 if (pszFilename == NULL) throw(kError::error_filename_param_is_missing_value);
2346 sprintf(pszFilename, "%s\\%s", pszBaseDir, pszValue);
2347 }
2348 else if (stricmp(pszName, "title") == 0)
2349 {
2350 if (pszValue != NULL)
2351 pszTitle = dupeString(pszValue);
2352 else
2353 addWarning(kError::warning_title_param_is_missing_value);
2354 }
2355 else if (stricmp(pszName, "titlex") == 0)
2356 {
2357 if (pszValue != NULL)
2358 pszTitleX = dupeString(pszValue);
2359 else
2360 addWarning(kError::warning_titlex_param_is_missing_value);
2361 }
2362 else if (stricmp(pszName, "titley") == 0)
2363 {
2364 if (pszValue != NULL)
2365 pszTitleY = dupeString(pszValue);
2366 else
2367 addWarning(kError::warning_titley_param_is_missing_value);
2368 }
2369 else if (stricmp(pszName, "legend") == 0)
2370 {
2371 fLegend = pszValue == NULL || stricmp(pszValue, "yes") == 0;
2372 }
2373 else if (stricmp(pszName, "width") == 0 || stricmp(pszName, "cx") == 0)
2374 {
2375 if (pszValue == NULL || strlen(pszValue) == 0) throw(kError::error_width_cx_param_is_missing_value);
2376 cX = atol(pszValue);
2377 if (cX < 100)
2378 throw(kError::error_graph_to_small);
2379 }
2380 else if (stricmp(pszName, "height") == 0 || stricmp(pszName, "cy") == 0)
2381 {
2382 if (pszValue == NULL || strlen(pszValue) == 0) throw(kError::error_height_cy_param_is_missing_value);
2383 cY = atol(pszValue);
2384 if (cY < 100)
2385 throw(kError::error_graph_to_small);
2386 }
2387 else if (stricmp(pszName, "startx") == 0 || stricmp(pszName, "minx") == 0)
2388 {
2389 if (pszValue == NULL || strlen(pszValue) == 0) throw(kError::error_xstart_param_is_missing_value);
2390 if (isDate(pszValue))
2391 rdStartX = dateToDbl(pszValue);
2392 else
2393 rdStartX = (double)atol(pszValue); /* TODO: real value not supported */
2394 }
2395 else if (stricmp(pszName, "endx") == 0 || stricmp(pszName, "maxx") == 0)
2396 {
2397 if (pszValue == NULL || strlen(pszValue) == 0) throw(kError::error_xend_param_is_missing_value);
2398 if (isDate(pszValue))
2399 rdEndX = dateToDbl(pszValue);
2400 else
2401 rdEndX = (double)atol(pszValue); /* TODO: real value not supported */
2402 }
2403 else if (stricmp(pszName, "starty") == 0 || stricmp(pszName, "miny") == 0)
2404 {
2405 if (pszValue == NULL || strlen(pszValue) == 0) throw(kError::error_ystart_param_is_missing_value);
2406 rdStartY = (double)atol(pszValue); /* TODO: real value not supported */
2407 }
2408 else if (stricmp(pszName, "endy") == 0 || stricmp(pszName, "maxy") == 0)
2409 {
2410 if (pszValue == NULL || strlen(pszValue) == 0) throw(kError::error_yend_param_is_missing_value);
2411 rdEndY = (double)atol(pszValue); /* TODO: real value not supported */
2412 }
2413 else if (stricmp(pszName, "background") == 0)
2414 {
2415 if (pszValue != NULL)
2416 {
2417 if (pszValue[0] == '#')
2418 ulBGColor = readColor(pszValue);
2419 else
2420 pszBackground = dupeString(pszValue);
2421 }
2422 else
2423 addWarning(kError::warning_background_param_is_missing_value);
2424 }
2425 else if (stricmp(pszName, "backgroundcolor") == 0)
2426 {
2427 if (pszValue != NULL)
2428 {
2429 if (pszValue[0] == '#')
2430 ulBGColor = readColor(pszValue);
2431 else
2432 addWarning(kError::warning_backgroundcolor_is_invalid);
2433 }
2434 else
2435 addWarning(kError::warning_backgroundcolor_param_is_missing_value);
2436 }
2437 else if (stricmp(pszName, "foregroundcolor") == 0)
2438 {
2439 if (pszValue != NULL)
2440 {
2441 if (pszValue[0] == '#')
2442 ulFGColor = readColor(pszValue);
2443 else
2444 addWarning(kError::warning_foregroundcolor_is_invalid);
2445 }
2446 else
2447 addWarning(kError::warning_foregroundcolor_param_is_missing_value);
2448 }
2449 else if (stricmp(pszName, "axiscolor") == 0)
2450 {
2451 if (pszValue != NULL)
2452 {
2453 if (pszValue[0] == '#')
2454 ulAxisColor = readColor(pszValue);
2455 else
2456 addWarning(kError::warning_axiscolor_is_invalid);
2457 }
2458 else
2459 addWarning(kError::warning_axiscolor_param_is_missing_value);
2460 }
2461 else
2462 addWarning(kError::warning_invalid_parameter);
2463
2464 /* next */
2465 i++;
2466 }
2467
2468 /* check that required parameters are present */
2469 if ((int)enmTypeCd == unknown) throw(kError::error_graph_type_is_missing);
2470 if ((int)enmSubTypeCd != normal) throw(kError::error_graph_subtype_is_invalid);
2471 if (pszFilename == NULL) throw(kError::error_filename_is_missing);
2472 if (listDataSets.getFirst() == NULL) throw(kError::error_no_data);
2473 if (cX < 50 || cY < 40) throw(kError::error_invalid_dimentions);
2474
2475 #if 0
2476 /* Check for date X values */
2477 fXDate = listDataSets.getFirst()->getXDate();
2478 #endif
2479}
2480
2481
2482/**
2483 * Fetches data for multiple data sets.
2484 * @param pszSql Pointer to sql statement.
2485 */
2486void kGraph::fetchData(const char *pszSql) throw(kError::enmErrors)
2487{
2488 void *pres;
2489
2490 switch (enmTypeCd)
2491 {
2492 case kGraph::lines:
2493 {
2494 pres = ::dbExecuteQuery(pszSql);
2495 if (pres != NULL)
2496 {
2497 kGraphDataSet *pDataSet = NULL;
2498 GRAPHCALLBACKPARAM Param = {this, NULL, NULL, enmTypeCd, 0, NULL, NULL};
2499 while (::dbFetch(pres, dbGraphCallBack, (void*)&Param))
2500 {
2501 /* find correct dataset: legend present - allways; else first time only.*/
2502 if (Param.pszLegend != NULL || pDataSet == NULL)
2503 pDataSet = findOrCreateDataSet(Param.pszLegend);
2504 assert(pDataSet != NULL);
2505
2506 pDataSet->setLegend(Param.pszLegend);
2507 pDataSet->setColor(Param.pszColor);
2508 pDataSet->listData.insert(Param.pData);
2509
2510 /* next */
2511 Param.lSeqNbr = 0;
2512 Param.pData = NULL;
2513 if (Param.pszColor != NULL)
2514 {
2515 delete Param.pszColor;
2516 Param.pszColor = NULL;
2517 }
2518 if (Param.pszLegend != NULL)
2519 {
2520 delete Param.pszLegend;
2521 Param.pszLegend = NULL;
2522 }
2523 }
2524 if (Param.pData != NULL) delete Param.pData;
2525 if (Param.pszColor != NULL) delete Param.pszColor;
2526 if (Param.pszLegend != NULL) delete Param.pszLegend;
2527 }
2528 else
2529 throw(kError::error_sql_failed);
2530 break;
2531 }
2532
2533 case kGraph::pie:
2534 addWarning(kError::warning_pie_not_implemented_yet);
2535
2536 default:
2537 break;
2538 }
2539}
2540
2541
2542
2543/**
2544 * Callback function used when receiving data.
2545 * @returns 0 success, -1 error.
2546 * @param pszValue Field value. Used as variable value.
2547 * @param pszFieldName Field name. Used as variable name.
2548 * @param pvUser Pointer to user defined data, here a variable lifo.
2549 */
2550long _System dbGraphCallBack(const char *pszValue, const char *pszFieldName, void *pvUser)
2551{
2552 PGRAPHCALLBACKPARAM pParam = (PGRAPHCALLBACKPARAM)pvUser;
2553
2554 assert(pParam != NULL);
2555
2556 switch (pParam->enmTypeCd)
2557 {
2558 case kGraph::lines:
2559 {
2560 switch (pParam->lSeqNbr)
2561 {
2562 case 0:
2563 pParam->pData = new kGraphData;
2564 /* date test */
2565 if (strlen(pszValue) == (4+1+2+1+2) && pszValue[4] == '-' && pszValue[7] == '-')
2566 {
2567 strcpy(&pParam->pData->szXDate[0], pszValue);
2568 pParam->pData->rdX = kGraph::dateToDbl(&pParam->pData->szXDate[0]);
2569 pParam->pGraph->fXDate = TRUE;
2570 }
2571 else
2572 {
2573 pParam->pData->szXDate[0] = '\0';
2574 pParam->pData->rdX = atol(pszValue);
2575 }
2576 break;
2577
2578 case 1:
2579 pParam->pData->rdY = atol(pszValue);
2580 break;
2581
2582 case 2:
2583 if (pszFieldName == NULL || stricmp(pszFieldName, "legend") != 0)
2584 pParam->pszColor = dupeString(pszValue);
2585 else
2586 pParam->pszLegend = dupeString(pszValue);
2587 break;
2588
2589 case 3:
2590 if (pszFieldName == NULL || stricmp(pszFieldName, "color") != 0)
2591 pParam->pszLegend = dupeString(pszValue);
2592 else
2593 pParam->pszColor = dupeString(pszValue);
2594 break;
2595
2596 default:
2597 pParam->pGraph->addWarning(kError::warning_to_many_fields_in_data_sql);
2598 }
2599 break;
2600 }
2601
2602 case kGraph::pie:
2603 default:
2604 break;
2605 }
2606
2607 pParam->lSeqNbr++;
2608 return 0;
2609}
2610
2611
2612/**
2613 * Finds or creates an dataset for the given legend (id).
2614 * @returns Pointer to DataSet. (NULL only when new failes.)
2615 * @param pszLegend DataSet identifier. NULL is allowed.
2616 */
2617kGraphDataSet *kGraph::findOrCreateDataSet(const char *pszLegend) throw(kError::enmErrors)
2618{
2619 kGraphDataSet *p;
2620
2621 if (pszLegend != NULL)
2622 {
2623 p = listDataSets.getFirst();
2624 while (p != NULL && *p != pszLegend)
2625 p = (kGraphDataSet *)p->getNext();
2626 }
2627 else
2628 p = NULL;
2629
2630
2631 if (p == NULL)
2632 listDataSets.insert(p = new kGraphDataSet());
2633
2634 return p;
2635}
2636
2637
2638/**
2639 * Draw the base structure of the graph.
2640 */
2641void kGraph::createBaseGraph(void) throw(kError::enmErrors)
2642{
2643 POINTL ptl;
2644 char szMax[32];
2645
2646 pGraph = gdImageCreate(cX, cY);
2647
2648 /* background? */
2649 if (pszBackground != NULL)
2650 {
2651 gdImagePtr pBackground;
2652 FILE *phFile;
2653 phFile = fopen(pszBackground, "rb");
2654 if (phFile)
2655 {
2656 pBackground = gdImageCreateFromGif(phFile);
2657 if (pBackground != NULL)
2658 {
2659 gdImageCopyResized(pGraph, pBackground,
2660 0, 0, //destination low left corner
2661 0, 0, //source low left corner
2662 cX, cY, //destination width and height
2663 gdImageSX(pBackground), //source width
2664 gdImageSY(pBackground) //source height
2665 );
2666 gdImageDestroy(pBackground);
2667 }
2668 else
2669 addWarning(kError::warning_failed_to_load_background_image);
2670 fclose(phFile);
2671 }
2672 else
2673 addWarning(kError::warning_failed_to_open_background_image);
2674 }
2675
2676 /* calc base coordinates */
2677 ptlOrigo.x = max(2 + (fXDate ? 5 : 0)*6, (long)(cX*0.05));
2678 _itoa((int)maxY(), &szMax[0], 10);
2679 ptlOrigo.y = max((long)(2 + strlen(&szMax[0])*6), (long)(cY*0.05));
2680 if (pszTitleX) ptlOrigo.y += 16;
2681 if (pszTitleY) ptlOrigo.x += 16;
2682 ptlYEnd.x = ptlOrigo.x;
2683 ptlYEnd.y = cY - ptlOrigo.y - (pszTitle ? max(20, (long)(cY*0.10)) : 0);
2684 ptlXEnd.x = cX - ptlOrigo.x;
2685 ptlXEnd.y = ptlOrigo.y;
2686
2687 if (fLegend)
2688 {
2689 addWarning(kError::warning_legend_is_not_implemented_yet);
2690 ptlXEnd.x -= (long)(cX * 0.20);
2691 }
2692
2693 /* allocate default colors */
2694 setColors();
2695
2696 /* draw axsis */
2697 if (pszTitleX)
2698 gdImageStringLB(pGraph, gdFontGiant,
2699 (int)(ptlOrigo.x + (ptlXEnd.x - ptlOrigo.x)/2.0 - strlen(pszTitleX)*9/2.0),
2700 max(2, (int)(cY*0.01))+ 16,
2701 pszTitleX, clrForeground);
2702 if (pszTitleY)
2703 gdImageStringUpLB(pGraph, gdFontGiant,
2704 max(2, (int)(cX*0.01)),
2705 (int)(ptlOrigo.y + (ptlYEnd.y - ptlOrigo.y)/2.0 - strlen(pszTitleY)*9/2.0),
2706 pszTitleY, clrForeground);
2707 if (pszTitle)
2708 {
2709 ptl.x = (long)(cX/2.0 - strlen(pszTitle)*9/2.0);
2710 ptl.y = cY - max(5, (int)(cY*0.02));
2711 gdImageStringLB(pGraph, gdFontGiant, (int)ptl.x, (int)ptl.y, pszTitle, clrForeground);
2712 gdImageLineLB(pGraph, (int)ptl.x - 3, (int)ptl.y - 18,
2713 (int)ptl.x + strlen(pszTitle)*9 + 5, (int)ptl.y - 18, clrForeground);
2714 }
2715
2716
2717 gdImageLineLB(pGraph, (int)ptlOrigo.x, (int)ptlOrigo.y-2, (int)ptlYEnd.x, (int)ptlYEnd.y+3, clrAxis);
2718 gdImageLineLB(pGraph, (int)ptlYEnd.x+2, (int)ptlYEnd.y, (int)ptlYEnd.x, (int)ptlYEnd.y+3, clrAxis);
2719 gdImageLineLB(pGraph, (int)ptlYEnd.x-2, (int)ptlYEnd.y, (int)ptlYEnd.x, (int)ptlYEnd.y+3, clrAxis);
2720
2721 gdImageLineLB(pGraph, (int)ptlOrigo.x-2, (int)ptlOrigo.y, (int)ptlXEnd.x+3, (int)ptlXEnd.y, clrAxis);
2722 gdImageLineLB(pGraph, (int)ptlXEnd.x, (int)ptlXEnd.y+2, (int)ptlXEnd.x+3, (int)ptlXEnd.y, clrAxis);
2723 gdImageLineLB(pGraph, (int)ptlXEnd.x, (int)ptlXEnd.y-2, (int)ptlXEnd.x+3, (int)ptlXEnd.y, clrAxis);
2724
2725
2726 /* scale - x */
2727 char szData[20];
2728 double rdMin, rdMax, rd, rdD;
2729 int cTags;
2730
2731 rdMax = maxX();
2732 rdMin = minX();
2733
2734 if (fXDate)
2735 {
2736 if (!kGraph::dblToDate(rdMin, &szData[0]))
2737 addWarning(kError::warning_failed_to_convert_date);
2738 }
2739 else
2740 sprintf(&szData[0], "%1.*f", rdMax > 10 ? 0 : 1, rdMin);
2741 gdImageStringLB(pGraph, gdFontSmall,
2742 (int)(ptlOrigo.x - strlen(&szData[0])*6/2.0),
2743 (int)(ptlOrigo.y - 3),
2744 &szData[0], clrForeground);
2745 if (fXDate)
2746 {
2747 if (!kGraph::dblToDate(rdMax, &szData[0]))
2748 addWarning(kError::warning_failed_to_convert_date);
2749 }
2750 else
2751 sprintf(&szData[0], "%1.*f", rdMax > 10 ? 0 : 1, rdMax);
2752 gdImageStringLB(pGraph, gdFontSmall,
2753 (int)(ptlXEnd.x - strlen(&szData[0])*6/2.0),
2754 (int)(ptlOrigo.y - 3),
2755 &szData[0], clrForeground);
2756
2757 rd = strlen(&szData[0])*6 + 20; //length of on scale tag
2758 cTags = (int)((ptlXEnd.x - ptlOrigo.x) / rd); //max number of tags
2759 if (cTags > 0)
2760 {
2761 rdD = rdMax - rdMin;
2762 if (rd > 10)
2763 rdD = (int)(rdD/cTags);
2764 else
2765 rdD = ((int)(rdD/cTags*10))/10;
2766 rd = rdMin + rdD;
2767 while (rdD > 0.0 && (rd + rdD) <= rdMax)
2768 {
2769 int x = (int)(ptlOrigo.x + (ptlXEnd.x - ptlOrigo.x)/(rdMax-rdMin)*(rd - rdMin));
2770 if (fXDate)
2771 {
2772 if (!kGraph::dblToDate(rd, &szData[0]))
2773 addWarning(kError::warning_failed_to_convert_date);
2774 }
2775 else
2776 sprintf(&szData[0], "%1.*f", rdMax > 10 ? 0 : 1, rd);
2777 gdImageStringLB(pGraph, gdFontSmall, x - (int)(strlen(&szData[0])*6/2.0), (int)(ptlOrigo.y - 3),
2778 &szData[0], clrForeground);
2779 gdImageLineLB(pGraph, x, (int)(ptlOrigo.y - 1), x, (int)(ptlOrigo.y + 1), clrAxis);
2780
2781 /* next */
2782 rd += rdD;
2783 }
2784 }
2785
2786 /* scale - y */
2787 rdMax = maxY();
2788 rdMin = minY();
2789
2790 sprintf(&szData[0], "%1.*f", rdMax > 10 ? 0 : 1, rdMin);
2791 gdImageStringLB(pGraph, gdFontSmall,
2792 (int)(ptlOrigo.x - 3 - strlen(&szData[0])*6),
2793 (int)(ptlOrigo.y + 8/2 + 2),
2794 &szData[0], clrForeground);
2795 sprintf(&szData[0], "%1.*f", rdMax > 10 ? 0 : 1, rdMax);
2796 gdImageStringLB(pGraph, gdFontSmall,
2797 (int)(ptlOrigo.x - 3 - strlen(&szData[0])*6),
2798 (int)(ptlYEnd.y + 8/2),
2799 &szData[0], clrForeground);
2800
2801 rd = 14 + 10; //length of on scale tag
2802 cTags = (int)((ptlYEnd.y - ptlOrigo.y) / rd); //max number of tags
2803 if (cTags > 0)
2804 {
2805 rdD = rdMax - rdMin;
2806 if (rd > 10)
2807 rdD = (int)(rdD/cTags);
2808 else
2809 rdD = ((int)(rdD/cTags*10))/10;
2810 rd = rdMin + rdD;
2811 while (rdD > 0.0 && (rd + rdD) <= rdMax)
2812 {
2813 int y = (int)(ptlOrigo.y + (ptlYEnd.y - ptlOrigo.y)/(rdMax-rdMin)*(rd - rdMin));
2814 sprintf(&szData[0], "%1.*f", rdMax > 10 ? 0 : 1, rd);
2815 gdImageStringLB(pGraph, gdFontSmall,
2816 (int)(ptlOrigo.x - 3 - strlen(&szData[0])*6),
2817 y + 8/2,
2818 &szData[0], clrForeground);
2819 gdImageLineLB(pGraph, (int)(ptlOrigo.x - 1), y, (int)(ptlOrigo.x + 1), y, clrAxis);
2820
2821 /* next */
2822 rd += rdD;
2823 }
2824 }
2825}
2826
2827
2828/**
2829 * Draw all lines.
2830 */
2831void kGraph::drawLines(void) throw(kError::enmErrors)
2832{
2833 kGraphDataSet *pDataSet;
2834
2835 assert((int)enmTypeCd == (int)lines);
2836
2837 pDataSet = listDataSets.getFirst();
2838 while (pDataSet != NULL)
2839 {
2840 pDataSet->setColor(pGraph);
2841 drawLine(pDataSet);
2842
2843 /* next */
2844 pDataSet = (kGraphDataSet *)pDataSet->getNext();
2845 }
2846}
2847
2848
2849/**
2850 * Draws a line for the given data set.
2851 * @param pDataSet Pointer to the dataset for the line.
2852 * @remark not implemented.
2853 */
2854void kGraph::drawLine(const kGraphDataSet *pDataSet) throw(kError::enmErrors)
2855{
2856 BOOL fFirstTime = TRUE;
2857 int xPrev = 0; /* don't have to initialized, but it removes gcc warning (release) */
2858 int yPrev = 0; /* don't have to initialized, but it removes gcc warning (release) */
2859 double rdMinX = minX();
2860 double rdMinY = minY();
2861 double rdDX = maxX() - rdMinX;
2862 double rdDY = maxY() - rdMinY;
2863
2864 kGraphData *p = pDataSet->listData.getFirst();
2865
2866 if (rdDX != 0.0 && rdDY != 0.0)
2867 {
2868 while (p != NULL)
2869 {
2870 int x, y;
2871
2872 x = (int)(ptlOrigo.x + ((ptlXEnd.x - ptlOrigo.x)/rdDX * (p->rdX - rdMinX)));
2873 y = (int)(ptlOrigo.y + ((ptlYEnd.y - ptlOrigo.y)/rdDY * (p->rdY - rdMinY)));
2874 if (fFirstTime)
2875 {
2876 xPrev = x;
2877 yPrev = y;
2878 fFirstTime = FALSE;
2879 }
2880 gdImageLineLB(pGraph, xPrev, yPrev, x, y, pDataSet->getColor());
2881
2882 /* next */
2883 p = (kGraphData*)p->getNext();
2884 xPrev = x;
2885 yPrev = y;
2886 }
2887 }
2888}
2889
2890
2891/**
2892 * Draws the legend.
2893 * @remark not implemented.
2894 */
2895void kGraph::drawLegend(void) throw(kError::enmErrors)
2896{
2897 if (fLegend == FALSE)
2898 return;
2899
2900 /* TODO */
2901}
2902
2903
2904/**
2905 * Converts an ISO date to days.
2906 * @returns Returns days after Christ, year 0.
2907 * @param pszDate Pointer to ISO date string.
2908 * @remark pszDate should be an ISO date, if not an exception may occur.
2909 * Use isDate to check it is an ISO date.
2910 */
2911double kGraph::dateToDbl(const char *pszDate)
2912{
2913 double rdRet = dbDateToDaysAfterChrist(pszDate);
2914 return rdRet != -1 ? rdRet : _NAN;
2915}
2916
2917
2918/**
2919 * Converts days to an ISO date.
2920 * @returns Success indicator. TRUE / FALSE.
2921 * @param rdDate Date in double, (really days from year 0).
2922 * @param pszDate Pointer to result buffer. Will hold ISO date string on successful return.
2923 */
2924BOOL kGraph::dblToDate(double rdDate, char *pszDate)
2925{
2926 if (rdDate != rdDate)
2927 return FALSE;
2928 return dbDaysAfterChristToDate((signed long)rdDate, pszDate);
2929}
2930
2931
2932/**
2933 * Checks if the given text string is an ISO date.
2934 * @returns TRUE - if date, else FALSE.
2935 * @param pszDate Possible ISO date string.
2936 */
2937BOOL kGraph::isDate(const char *pszDate)
2938{
2939 return strlen(pszDate) == 10 && pszDate[4] == '-' && pszDate[7] == '-';
2940}
2941
2942
2943/**
2944 * frees all storage.
2945 * @remark Called only at destruction. (That is destructor or when contruction failes.)
2946 */
2947void kGraph::destroy(void)
2948{
2949 if (pGraph != NULL) gdImageDestroy(pGraph);
2950 if (pszFilename != NULL) delete pszFilename;
2951 if (pszTitle != NULL) delete pszTitle;
2952 if (pszTitleX != NULL) delete pszTitleX;
2953 if (pszTitleY != NULL) delete pszTitleY;
2954 if (pszBackground != NULL) delete pszBackground;
2955}
2956
2957
2958/**
2959 * Adds a warning to the list of warnings.
2960 * @param enmErrorCd Warning code.
2961 */
2962void kGraph::addWarning(kError::enmErrors enmErrorCd)
2963{
2964 listWarnings.insert(new kWarningEntry(enmErrorCd));
2965}
2966
2967
2968/**
2969 * Finds the max X value in all data sets.
2970 * @returns max X value. 0.0 if no sets
2971 */
2972double kGraph::maxX(void)
2973{
2974 if (rdMaxX != rdMaxX)
2975 {
2976 /* first time */
2977 if (rdEndX != rdEndX)
2978 { /* not spesified by user */
2979 kGraphDataSet *p = listDataSets.getFirst();
2980
2981 while (p != NULL)
2982 {
2983 double rd = p->maxX();
2984 if (rdMaxX < rd || rdMaxX != rdMaxX)
2985 rdMaxX = rd;
2986 p = (kGraphDataSet*)p->getNext();
2987 }
2988
2989 rdMaxX = rdMaxX == rdMaxX ? rdMaxX : 0.0;
2990 }
2991 else /* spesified by user */
2992 rdMaxX = rdEndX;
2993 }
2994
2995 return rdMaxX;
2996}
2997
2998
2999/**
3000 * Finds the min X value in all data sets.
3001 * @returns min X value. 0.0 if no sets
3002 * @remark Addjusts value if close to 0.
3003 */
3004double kGraph::minX(void)
3005{
3006 if (rdMinX != rdMinX)
3007 {
3008 /* first time */
3009 if (rdStartX != rdStartX)
3010 { /* not spesified by user */
3011 kGraphDataSet *p = listDataSets.getFirst();
3012
3013 while (p != NULL)
3014 {
3015 double rd = p->minX();
3016 if (rdMinX > rd || rdMinX != rdMinX)
3017 rdMinX = rd;
3018 p = (kGraphDataSet*)p->getNext();
3019 }
3020
3021 /* addjustment */
3022 if (rdMinX != rdMinX)
3023 rdMinX = fXDate ? maxX() - 1.0 : 0.0;
3024 else if (rdMinX >= 0.0)
3025 rdMinX = fXDate ? rdMinX : 0.0;
3026 else
3027 {
3028 /* TODO negative values are not supported yet. */
3029 addWarning(kError::warning_negative_values_are_not_supported_yet);
3030 rdMinX = 0.0;
3031 }
3032 }
3033 else /* spesified by user */
3034 rdMinX = rdStartX;
3035 }
3036
3037 return rdMinX;
3038}
3039
3040
3041/**
3042 * Finds the max Y value in all data sets.
3043 * @returns max Y value. 0.0 if no sets
3044 */
3045double kGraph::maxY(void)
3046{
3047 if (rdMaxY != rdMaxY)
3048 {
3049 /* first time */
3050 if (rdEndY != rdEndY)
3051 { /* not spesified by user */
3052 kGraphDataSet *p = listDataSets.getFirst();
3053
3054 while (p != NULL)
3055 {
3056 double rd = p->maxY();
3057 if (rdMaxY < rd || rdMaxY != rdMaxY)
3058 rdMaxY = rd;
3059 p = (kGraphDataSet*)p->getNext();
3060 }
3061
3062 rdMaxY = rdMaxY == rdMaxY ? rdMaxY : 0.0;
3063 }
3064 else /* spesified by user */
3065 rdMaxY = rdEndY;
3066 }
3067
3068 return rdMaxY;
3069}
3070
3071
3072/**
3073 * Finds the min Y value in all data sets.
3074 * @returns min Y value. 0.0 if no sets
3075 * @remark Addjusts value if close to 0.
3076 */
3077double kGraph::minY(void)
3078{
3079 if (rdMinY != rdMinY)
3080 {
3081 /* first time */
3082 if (rdStartY != rdStartY)
3083 { /* not spesified by user */
3084 kGraphDataSet *p = listDataSets.getFirst();
3085
3086 while (p != NULL)
3087 {
3088 double rd = p->minY();
3089 if (rdMinY > rd || rdMinY != rdMinY)
3090 rdMinY = rd;
3091 p = (kGraphDataSet*)p->getNext();
3092 }
3093
3094 /* addjustment */
3095 if (rdMinY != rdMinY)
3096 rdMinY = 0.0;
3097 else if (rdMinY >= 0.0)
3098 rdMinY = 0.0;
3099 else
3100 { /* TODO negative values are not supported yet. */
3101 addWarning(kError::warning_negative_values_are_not_supported_yet);
3102 rdMinY = 0.0;
3103 }
3104 }
3105 else /* spesified by user */
3106 rdMinY = rdStartY;
3107 }
3108
3109 return rdMinY;
3110}
3111
3112
3113/**
3114 * Reads a color parameter.
3115 * @returns 24Bit RGB color value.
3116 * @param pszColor Pointer to color string. That is "#RRGGBB".
3117 */
3118unsigned long kGraph::readColor(const char *pszColor)
3119{
3120 unsigned long ulColor = 0xffffff;
3121
3122 if (*pszColor == '#')
3123 {
3124 ulColor = 0;
3125 for (int i = 1; i < 7; i++)
3126 {
3127 ulColor = ulColor << 4;
3128 if (pszColor[i] >= '0' && pszColor[i] <= '9')
3129 ulColor |= pszColor[i] - '0';
3130 else if (pszColor[i] >= 'A' && pszColor[i] <= 'F')
3131 ulColor |= pszColor[i] - 'A' + 0xa;
3132 else if (pszColor[i] >= 'a' && pszColor[i] <= 'f')
3133 ulColor |= pszColor[i] - 'a' + 0xa;
3134 else
3135 addWarning(kError::warning_invalid_color_value);
3136 }
3137 }
3138 else
3139 addWarning(kError::warning_invalid_color_value);
3140
3141 return ulColor;
3142}
3143
3144
3145/**
3146 * Allocates a color in the image.
3147 * @returns GifDraw Color.
3148 * @param ulColor 24Bit RGB color value.
3149 */
3150int kGraph::setColor(unsigned long ulColor)
3151{
3152 return gdImageColorAllocate(pGraph,
3153 (int)(0xFF & (ulColor >> 16)),
3154 (int)(0xFF & (ulColor >> 8)),
3155 (int)(0xFF & ulColor));
3156}
3157
3158
3159/**
3160 * Allocates/sets default colors.
3161 */
3162void kGraph::setColors(void)
3163{
3164 clrBackground = setColor(ulBGColor != ~0UL ? ulBGColor : 0x00000000UL); /* vitally important that this is done first! */
3165 clrForeground = setColor(ulFGColor != ~0UL ? ulFGColor : 0x0000FF00UL);
3166 clrAxis = setColor(ulAxisColor != ~0UL ? ulAxisColor : 0x00808080UL);
3167}
3168
3169
3170/**
3171 * Constructor.
3172 * @remark Throws kError::enmError on error.
3173 */
3174kGraph::kGraph(const kTag &tag, const char *pszBaseDir) throw(kError::enmErrors)
3175 : pGraph(NULL), enmTypeCd(unknown), enmSubTypeCd(normal),
3176 pszFilename(NULL), pszTitle(NULL), pszTitleX(NULL),
3177 pszTitleY(NULL), pszBackground(NULL), fLegend(FALSE),
3178 cX(~0L), cY(~0L),
3179 rdStartX(_NAN), rdEndX(_NAN), rdStartY(_NAN), rdEndY(_NAN),
3180 fXDate(FALSE),
3181 ulBGColor(~0UL), ulFGColor(~0UL), ulAxisColor(~0UL),
3182 rdMaxX(_NAN), rdMinX(_NAN), rdMaxY(_NAN), rdMinY(_NAN)
3183{
3184 try
3185 {
3186 analyseTag(tag, pszBaseDir);
3187 createBaseGraph();
3188 if (fLegend)
3189 drawLegend();
3190 drawLines();
3191 }
3192 catch (kError::enmErrors enmErrorCd)
3193 {
3194 destroy();
3195 throw(enmErrorCd);
3196 }
3197}
3198
3199
3200/**
3201 * Destructor.
3202 * @remark calls destroy().
3203 */
3204kGraph::~kGraph(void)
3205{
3206 destroy();
3207}
3208
3209
3210/**
3211 * Saves the graph to the filename specified in the tag.
3212 * 2
3213 * @returns Errorcode.
3214 */
3215kError::enmErrors kGraph::save(void)
3216{
3217 FILE *phFileOut;
3218
3219 assert(pGraph != NULL);
3220 assert(pszFilename != NULL);
3221
3222 phFileOut = fopen(pszFilename, "wb");
3223 if (phFileOut != NULL)
3224 {
3225 gdImageGif(pGraph, phFileOut);
3226 fclose(phFileOut);
3227 }
3228 else
3229 return kError::error_opening_output_file;
3230 return kError::no_error;
3231}
3232
3233
3234/**
3235 * Prints warnings.
3236 * @returns Number of warnings.
3237 * @param phLog Pointer to log file handle.
3238 */
3239unsigned long kGraph::showWarnings(FILE *phLog, const kFileEntry *pCurFile)
3240{
3241 unsigned long cWarnings = 0;
3242 kWarningEntry *p = listWarnings.getFirst();
3243
3244 while (p != NULL)
3245 {
3246 cWarnings++;
3247 fprintf(phLog, "%s(%ld) : warning: kGraph - %s\n",
3248 pCurFile->getFilename(), pCurFile->getLineNumber()+1,
3249 kError::queryDescription(p->getWarningCode()));
3250 p = (kWarningEntry*)p->getNext();
3251 }
3252
3253 return cWarnings;
3254}
3255
3256
3257/* include template code */
3258#include "kLIFO.cpp"
3259#include "kList.cpp"
3260
3261#ifdef __EMX__
3262template class kLIFO<kSqlEntry>;
3263#endif
3264
Note: See TracBrowser for help on using the repository browser.