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

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

Some new reports.
General bugfixes and new features.

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