Code

reduce compiler warnings. Many small fixes. -- Mike Slifcak <slif@bellsouth.net>
[rrdtool.git] / src / rrd_cgi.c
1 /*****************************************************************************
2  * RRDtool 1.1.x  Copyright Tobias Oetiker, 1997 - 2004
3  *****************************************************************************
4  * rrd_cgi.c  RRD Web Page Generator
5  *****************************************************************************/
7 #include "rrd_tool.h"
8 #include <cgi.h>
9 #include <time.h>
12 #define MEMBLK 1024
13 /*#define DEBUG_PARSER
14 #define DEBUG_VARS*/
16 /* global variable for libcgi */
17 s_cgi *cgiArg;
19 /* in arg[0] find tags beginning with arg[1] call arg[2] on them
20    and replace by result of arg[2] call */
21 int parse(char **, long, char *, char *(*)(long , const char **));
23 /**************************************************/
24 /* tag replacers ... they are called from parse   */
25 /* through function pointers                      */
26 /**************************************************/
28 /* return cgi var named arg[0] */ 
29 char* cgiget(long , const char **);
31 /* return a quoted cgi var named arg[0] */ 
32 char* cgigetq(long , const char **);
34 /* return a quoted and sanitized cgi variable */
35 char* cgigetqp(long , const char **);
37 /* call rrd_graph and insert appropriate image tag */
38 char* drawgraph(long, char **);
40 /* return PRINT functions from last rrd_graph call */
41 char* drawprint(long, const char **);
43 /* pretty-print the <last></last> value for some.rrd via strftime() */
44 char* printtimelast(long, const char **);
46 /* pretty-print current time */
47 char* printtimenow(long, const char **);
49 /* set an environment variable */
50 char* rrdsetenv(long, const char **);
52 /* get an environment variable */
53 char* rrdgetenv(long, const char **);
55 /* include the named file at this point */
56 char* includefile(long, const char **);
58 /* for how long is the output of the cgi valid ? */
59 char* rrdgoodfor(long, const char **);
61 char* rrdstrip(char *buf);
62 char* scanargs(char *line, int *argc, char ***args);
64 /* format at-time specified times using strftime */
65 char* printstrftime(long, const char**);
67 /** HTTP protocol needs special format, and GMT time **/
68 char *http_time(time_t *);
70 /* return a pointer to newly allocated copy of this string */
71 char *stralloc(const char *);
74 /* rrd interface to the variable functions {put,get}var() */
75 char* rrdgetvar(long argc, const char **args);
76 char* rrdsetvar(long argc, const char **args);
77 char* rrdsetvarconst(long argc, const char **args);
80 /* variable store: put/get key-value pairs */
81 static int   initvar();
82 static void  donevar();
83 static const char* getvar(const char* varname);
84 static const char* putvar(const char* name, const char* value, int is_const);
86 /* key value pair that makes up an entry in the variable store */
87 typedef struct
88 {
89         int is_const;           /* const variable or not */
90         const char* name;       /* variable name */
91         const char* value;      /* variable value */
92 } vardata;
94 /* the variable heap: 
95    start with a heapsize of 10 variables */
96 #define INIT_VARSTORE_SIZE      10
97 static vardata* varheap    = NULL;
98 static size_t varheap_size = 0;
100 /* allocate and initialize variable heap */
101 static int
102 initvar()
104         varheap = (vardata*)malloc(sizeof(vardata) * INIT_VARSTORE_SIZE);
105         if (varheap == NULL) {
106                 fprintf(stderr, "ERROR: unable to initialize variable store\n");
107                 return -1;
108         }
109         memset(varheap, 0, sizeof(vardata) * INIT_VARSTORE_SIZE);
110         varheap_size = INIT_VARSTORE_SIZE;
111         return 0;
114 /* cleanup: free allocated memory */
115 static void
116 donevar()
118         int i;
119         if (varheap) {
120                 for (i=0; i<(int)varheap_size; i++) {
121                         if (varheap[i].name) {
122                                 free((char*)varheap[i].name);
123                         }
124                         if (varheap[i].value) {
125                                 free((char*)varheap[i].value);
126                         }
127                 }
128                 free(varheap);
129         }
132 /* Get a variable from the variable store.
133    Return NULL in case the requested variable was not found. */
134 static const char*
135 getvar(const char* name)
137         int i;
138         for (i=0; i<(int)varheap_size && varheap[i].name; i++) {
139                 if (0 == strcmp(name, varheap[i].name)) {
140 #ifdef          DEBUG_VARS
141                         printf("<!-- getvar(%s) -> %s -->\n", name, varheap[i].value);
142 #endif
143                         return varheap[i].value;
144                 }
145         }
146 #ifdef DEBUG_VARS
147         printf("<!-- getvar(%s) -> Not found-->\n", name);
148 #endif
149         return NULL;
152 /* Put a variable into the variable store. If a variable by that
153    name exists, it's value is overwritten with the new value unless it was
154    marked as 'const' (initialized by RRD::SETCONSTVAR).
155    Returns a copy the newly allocated value on success, NULL on error. */
156 static const char*
157 putvar(const char* name, const char* value, int is_const)
159         int i;
160         for (i=0; i < (int)varheap_size && varheap[i].name; i++) {
161                 if (0 == strcmp(name, varheap[i].name)) {
162                         /* overwrite existing entry */
163                         if (varheap[i].is_const) {
164 #ifdef                  DEBUG_VARS
165                                 printf("<!-- setver(%s, %s): not assigning: "
166                                                 "const variable -->\n", name, value);
167 #                               endif
168                                 return varheap[i].value;
169                         }
170 #ifdef          DEBUG_VARS
171                         printf("<!-- setvar(%s, %s): overwriting old value (%s) -->\n",
172                                         name, value, varheap[i].value);
173 #endif
174                         /* make it possible to promote a variable to readonly */
175                         varheap[i].is_const = is_const;
176                         free((char*)varheap[i].value);
177                         varheap[i].value = stralloc(value);
178                         return varheap[i].value;
179                 }
180         }
182         /* no existing variable found by that name, add it */
183         if (i == (int)varheap_size) {
184                 /* ran out of heap: resize heap to double size */
185                 size_t new_size = varheap_size * 2;
186                 varheap = (vardata*)(realloc(varheap, sizeof(vardata) * new_size));
187                 if (!varheap) {
188                         fprintf(stderr, "ERROR: Unable to realloc variable heap\n");
189                         return NULL;
190                 }
191                 /* initialize newly allocated memory */;
192                 memset(&varheap[varheap_size], 0, sizeof(vardata) * varheap_size);
193                 varheap_size = new_size;
194         }
195         varheap[i].is_const = is_const;
196         varheap[i].name  = stralloc(name);
197         varheap[i].value = stralloc(value);
199 #ifdef          DEBUG_VARS
200         printf("<!-- setvar(%s, %s): adding new variable -->\n", name, value);
201 #endif
202         return varheap[i].value;
205 /* expand those RRD:* directives that can be used recursivly */
206 static char*
207 rrd_expand_vars(char* buffer)
209         int i;
211 #ifdef DEBUG_PARSER
212         printf("expanding variables in '%s'\n", buffer);
213 #endif
215         for (i=0; buffer[i]; i++) {
216                 if (buffer[i] != '<')
217                         continue;
218                 parse(&buffer, i, "<RRD::CV", cgiget);
219                 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
220                 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
221                 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);    
222                 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);    
223         }
224         return buffer;
227 static long goodfor=0;
228 static char **calcpr=NULL;
229 static void calfree (void){
230   if (calcpr) {
231     long i;
232     for(i=0;calcpr[i];i++){
233       if (calcpr[i]){
234               free(calcpr[i]);
235       }
236     } 
237     if (calcpr) {
238             free(calcpr);
239     }
240   }
243 /* create freeable version of the string */
244 char * stralloc(const char *str){
245   char* nstr;
246   if (!str) {
247           return NULL;
248   }
249   nstr = malloc((strlen(str)+1));
250   strcpy(nstr,str);
251   return(nstr);
254 int main(int argc, char *argv[]) {
255         long length;
256         char *buffer;
257         char *server_url = NULL;
258         long i;
259         long filter=0;
260 #ifdef MUST_DISABLE_SIGFPE
261         signal(SIGFPE,SIG_IGN);
262 #endif
263 #ifdef MUST_DISABLE_FPMASK
264         fpsetmask(0);
265 #endif
266         /* what do we get for cmdline arguments?
267         for (i=0;i<argc;i++)
268         printf("%d-'%s'\n",i,argv[i]); */
269         while (1) {
270                 static struct option long_options[] = {
271                         { "filter", no_argument, 0, 'f' },
272                         { 0, 0, 0, 0}
273                 };
274                 int option_index = 0;
275                 int opt;
276                 opt = getopt_long(argc, argv, "f", long_options, &option_index);
277                 if (opt == EOF) {
278                         break;
279                 }
281                 switch(opt) {
282                 case 'f':
283                                 filter=1;
284                         break;
285                 case '?':
286                         printf("unknown commandline option '%s'\n",argv[optind-1]);
287                         return -1;
288                 }
289         }
291         if (!filter) {
292                 cgiDebug(0,0);
293                 cgiArg = cgiInit();
294                 server_url = getenv("SERVER_URL");
295         }
297         /* make sure we have one extra argument, 
298            if there are others, we do not care Apache gives several */
300         /* if ( (optind != argc-2 
301            && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL) 
302            && optind != argc-1) { */
304         if ( optind >= argc ) { 
305                 fprintf(stderr, "ERROR: expected a filename\n");
306                 exit(1);
307         } else {
308                 length = readfile(argv[optind], &buffer, 1);
309         }
311         if(rrd_test_error()) {
312                 fprintf(stderr, "ERROR: %s\n",rrd_get_error());
313                 exit(1);
314         }
316         /* initialize variable heap */
317         initvar();
319         /* expand rrd directives in buffer recursivly */
320         for (i=0; buffer[i]; i++) {
321                 if (buffer[i] != '<')
322                         continue;
323                 if (!filter) {
324                         parse(&buffer, i, "<RRD::CV", cgiget);
325                         parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
326                         parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
327                         parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
328                 }
329                 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
330                 parse(&buffer, i, "<RRD::GOODFOR", rrdgoodfor);
331                 parse(&buffer, i, "<RRD::GRAPH", drawgraph);
332                 parse(&buffer, i, "<RRD::INCLUDE", includefile);
333                 parse(&buffer, i, "<RRD::PRINT", drawprint);
334                 parse(&buffer, i, "<RRD::SETCONSTVAR", rrdsetvarconst);
335                 parse(&buffer, i, "<RRD::SETENV", rrdsetenv);
336                 parse(&buffer, i, "<RRD::SETVAR", rrdsetvar);
337                 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
338                 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
339                 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
340         }
342         if (!filter) {
343                 printf ("Content-Type: text/html\n" 
344                                 "Content-Length: %d\n", 
345                                 strlen(buffer));
347                 if (labs(goodfor) > 0) {
348                         time_t now;
349                         now = time(NULL);
350                         printf("Last-Modified: %s\n", http_time(&now));
351                         now += labs(goodfor);
352                         printf("Expires: %s\n", http_time(&now));
353                         if (goodfor < 0) {
354                                 printf("Refresh: %ld\n", labs(goodfor));
355                         }
356                 }
357                 printf("\n");
358         }
360         /* output result */
361         printf("%s", buffer);
363         /* cleanup */
364         calfree();
365         if (buffer){
366                 free(buffer);
367         }
368         donevar();
369         exit(0);
372 /* remove occurrences of .. this is a general measure to make
373    paths which came in via cgi do not go UP ... */
375 char* rrdsetenv(long argc, const char **args) {
376         if (argc >= 2) {
377                 char *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2));
378                 if (xyz == NULL) {
379                         return stralloc("[ERROR: allocating setenv buffer]");
380                 };
381                 sprintf(xyz, "%s=%s", args[0], args[1]);
382                 if(putenv(xyz) == -1) {
383                         free(xyz);
384                         return stralloc("[ERROR: failed to do putenv]");
385                 };
386         }
387         return stralloc("[ERROR: setenv failed because not enough "
388                                         "arguments were defined]");
391 /* rrd interface to the variable function putvar() */
392 char*
393 rrdsetvar(long argc, const char **args)
395         if (argc >= 2)
396         {
397                 const char* result = putvar(args[0], args[1], 0 /* not const */);
398                 if (result) {
399                         /* setvar does not return the value set */
400                         return stralloc("");
401                 }
402                 return stralloc("[ERROR: putvar failed]");
403         }
404         return stralloc("[ERROR: putvar failed because not enough arguments "
405                                         "were defined]");
408 /* rrd interface to the variable function putvar() */
409 char*
410 rrdsetvarconst(long argc, const char **args)
412         if (argc >= 2)
413         {
414                 const char* result = putvar(args[0], args[1], 1 /* const */);
415                 if (result) {
416                         /* setvar does not return the value set */
417                         return stralloc("");
418                 }
419                 return stralloc("[ERROR: putvar failed]");
420         }
421         return stralloc("[ERROR: putvar failed because not enough arguments "
422                                         "were defined]");
425 char* rrdgetenv(long argc, const char **args) {
426         char buf[128];
427         const char* envvar;
428         if (argc != 1) {
429                 return stralloc("[ERROR: getenv failed because it did not "
430                                                 "get 1 argument only]");
431         };
432         envvar = getenv(args[0]);
433         if (envvar) {
434                 return stralloc(envvar);
435         } else {
436                 snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
437                 return stralloc(buf);
438         }
441 char* rrdgetvar(long argc, const char **args) {
442         char buf[128];
443         const char* value;
444         if (argc != 1) {
445                 return stralloc("[ERROR: getvar failed because it did not "
446                                                 "get 1 argument only]");
447         };
448         value = getvar(args[0]);
449         if (value) {
450                 return stralloc(value);
451         } else {
452                 snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
453                 return stralloc(buf);
454         }
457 char* rrdgoodfor(long argc, const char **args){
458   if (argc == 1) {
459       goodfor = atol(args[0]);
460   } else {
461     return stralloc("[ERROR: goodfor expected 1 argument]");
462   }
463    
464   if (goodfor == 0){
465      return stralloc("[ERROR: goodfor value must not be 0]");
466   }
467    
468   return stralloc("");
471 /* Format start or end times using strftime.  We always need both the
472  * start and end times, because, either might be relative to the other.
473  * */
474 #define MAX_STRFTIME_SIZE 256
475 char* printstrftime(long argc, const char **args){
476         struct  rrd_time_value start_tv, end_tv;
477         char    *parsetime_error = NULL;
478         char    formatted[MAX_STRFTIME_SIZE];
479         struct tm *the_tm;
480         time_t  start_tmp, end_tmp;
482         /* Make sure that we were given the right number of args */
483         if( argc != 4) {
484                 rrd_set_error( "wrong number of args %d", argc);
485                 return (char *) -1;
486         }
488         /* Init start and end time */
489         parsetime("end-24h", &start_tv);
490         parsetime("now", &end_tv);
492         /* Parse the start and end times we were given */
493         if( (parsetime_error = parsetime( args[1], &start_tv))) {
494                 rrd_set_error( "start time: %s", parsetime_error);
495                 return (char *) -1;
496         }
497         if( (parsetime_error = parsetime( args[2], &end_tv))) {
498                 rrd_set_error( "end time: %s", parsetime_error);
499                 return (char *) -1;
500         }
501         if( proc_start_end( &start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
502                 return (char *) -1;
503         }
505         /* Do we do the start or end */
506         if( strcasecmp( args[0], "START") == 0) {
507                 the_tm = localtime( &start_tmp);
508         }
509         else if( strcasecmp( args[0], "END") == 0) {
510                 the_tm = localtime( &end_tmp);
511         }
512         else {
513                 rrd_set_error( "start/end not found in '%s'", args[0]);
514                 return (char *) -1;
515         }
517         /* now format it */
518         if( strftime( formatted, MAX_STRFTIME_SIZE, args[3], the_tm)) {
519                 return( stralloc( formatted));
520         }
521         else {
522                 rrd_set_error( "strftime failed");
523                 return (char *) -1;
524         }
527 char* includefile(long argc, const char **args){
528   char *buffer;
529   if (argc >= 1) {
530       readfile(args[0], &buffer, 0);
531       if (rrd_test_error()) {
532                 char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE));
533           sprintf(err, "[ERROR: %s]",rrd_get_error());
534           rrd_clear_error();
535           return err;
536       } else {
537        return buffer;
538       }
539   }
540   else
541   {
542       return stralloc("[ERROR: No Inclue file defined]");
543   }
546 /* make a copy of buf and replace open/close brackets with '_' */
547 char* rrdstrip(char *buf) {
548   char* p;
549   if (buf == NULL) {
550           return NULL;
551   }
552   /* make a copy of the buffer */
553   buf = stralloc(buf);
554   if (buf == NULL) {
555           return NULL;
556   }
558   p = buf;
559   while (*p) {
560           if (*p == '<' || *p == '>') {
561                   *p = '_';
562           }
563           p++;
564   }
565   return buf;
568 char* cgigetq(long argc, const char **args){
569   if (argc>= 1){
570     char *buf = rrdstrip(cgiGetValue(cgiArg,args[0]));
571     char *buf2;
572     char *c,*d;
573     int  qc=0;
574     if (buf==NULL) return NULL;
576     for(c=buf;*c != '\0';c++)
577       if (*c == '"') qc++;
578     if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) {
579         perror("Malloc Buffer");
580         exit(1);
581     };
582     c=buf;
583     d=buf2;
584     *(d++) = '"';
585     while(*c != '\0'){
586         if (*c == '"') {
587             *(d++) = '"';
588             *(d++) = '\'';
589             *(d++) = '"';
590             *(d++) = '\'';
591         } 
592         *(d++) = *(c++);
593     }
594     *(d++) = '"';
595     *(d) = '\0';
596     free(buf);
597     return buf2;
598   }
600   return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
603 /* remove occurrences of .. this is a general measure to make
604    paths which came in via cgi do not go UP ... */
606 char* cgigetqp(long argc, const char **args){
607   if (argc>= 1) {
608     char *buf = rrdstrip(cgiGetValue(cgiArg,args[0]));
609     char *buf2;
610     char *c,*d;
611     int  qc=0;
613     if (buf==NULL) 
614                 return NULL;
616     for(c=buf;*c != '\0';c++) {
617         if (*c == '"') {
618                         qc++;
619                 }
620         }
622     if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) {
623                 perror("Malloc Buffer");
624                 exit(1);
625     };
627     c=buf;
628     d=buf2;
630     *(d++) = '"';
631     while (*c != '\0') {
632                 if (*c == '"') {
633                         *(d++) = '"';
634                         *(d++) = '\'';
635                         *(d++) = '"';
636                         *(d++) = '\'';
637                 }
638                 if(*c == '/') {
639                         *(d++) = '_';
640                         c++;
641                 } else {
642                         if (*c=='.' && *(c+1) == '.') {
643                                 c += 2;
644                                 *(d++) = '_'; *(d++) ='_';      
645                         } else {
646                                 *(d++) = *(c++);
647                         }
648                 }
649     }
650     *(d++) = '"';
651     *(d) = '\0';
652     free(buf);
653     return buf2;
654   }
655   return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
659 char* cgiget(long argc, const char **args){
660   if (argc>= 1)
661     return rrdstrip(cgiGetValue(cgiArg,args[0]));
662   else
663     return stralloc("[ERROR: not enough arguments for RRD::CV]");
668 char* drawgraph(long argc, char **args){
669   int i,xsize, ysize;
670   for(i=0;i<argc;i++)
671     if(strcmp(args[i],"--imginfo")==0 || strcmp(args[i],"-g")==0) break;
672   if(i==argc) {
673     args[argc++] = "--imginfo";
674     args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
675   }
676   optind=0; /* reset gnu getopt */
677   opterr=0; /* reset gnu getopt */
678   calfree();
679   if( rrd_graph(argc+1, args-1, &calcpr, &xsize, &ysize,NULL) != -1 ) {
680     return stralloc(calcpr[0]);
681   } else {
682     if (rrd_test_error()) {
683       char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE)*sizeof(char));
684       sprintf(err, "[ERROR: %s]",rrd_get_error());
685       rrd_clear_error();
686       calfree();
687       return err;
688     }
689   }
690   return NULL;
693 char* drawprint(long argc, const char **args){
694   if (argc==1 && calcpr){
695     long i=0;
696     while (calcpr[i] != NULL) i++; /*determine number lines in calcpr*/
697     if (atol(args[0])<i-1)
698       return stralloc(calcpr[atol(args[0])+1]);    
699   }
700   return stralloc("[ERROR: RRD::PRINT argument error]");
703 char* printtimelast(long argc, const char **args) {
704   time_t last;
705   struct tm tm_last;
706   char *buf;
707   if ( argc == 2 ) {
708     buf = malloc(255);
709     if (buf == NULL){   
710         return stralloc("[ERROR: allocating strftime buffer]");
711     };
712     last = rrd_last(argc+1, args-1); 
713     if (rrd_test_error()) {
714       char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE)*sizeof(char));
715       sprintf(err, "[ERROR: %s]",rrd_get_error());
716       rrd_clear_error();
717       return err;
718     }
719     tm_last = *localtime(&last);
720     strftime(buf,254,args[1],&tm_last);
721     return buf;
722   }
723   if ( argc < 2 ) {
724     return stralloc("[ERROR: too few arguments for RRD::TIME::LAST]");
725   }
726   return stralloc("[ERROR: not enough arguments for RRD::TIME::LAST]");
729 char* printtimenow(long argc, const char **args) {
730   time_t now = time(NULL);
731   struct tm tm_now;
732   char *buf;
733   if ( argc == 1 ) {
734     buf = malloc(255);
735     if (buf == NULL){   
736         return stralloc("[ERROR: allocating strftime buffer]");
737     };
738     tm_now = *localtime(&now);
739     strftime(buf,254,args[0],&tm_now);
740     return buf;
741   }
742   if ( argc < 1 ) {
743     return stralloc("[ERROR: too few arguments for RRD::TIME::NOW]");
744   }
745   return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]");
748 /* Scan buffer until an unescaped '>' arives.
749  * Update argument array with arguments found.
750  * Return end cursor where parsing stopped, or NULL in case of failure.
751  *
752  * FIXME:
753  * To allow nested constructs, we call rrd_expand_vars() for arguments
754  * that contain RRD::x directives. These introduce a small memory leak
755  * since we have to stralloc the arguments the way parse() works.
756  */
757 char*
758 scanargs(char *line, int *argument_count, char ***arguments)
760         char    *getP;          /* read cursor */
761         char    *putP;          /* write cursor */
762         char    Quote;          /* type of quote if in quoted string, 0 otherwise */
763         int     tagcount;       /* open tag count */
764         int     in_arg;         /* if we currently are parsing an argument or not */
765         int     argsz;          /* argument array size */
766         int             curarg_contains_rrd_directives;
768         /* local array of arguments while parsing */
769         int argc = 0;
770         char** argv;
772 #ifdef DEBUG_PARSER
773         printf("<-- scanargs(%s) -->\n", line);
774 #endif
776         *arguments = NULL;
777         *argument_count = 0;
779         /* create initial argument array of char pointers */
780         argsz = 32;
781         argv = (char **)malloc(argsz * sizeof(char *));
782         if (!argv) {
783                 return NULL;
784         }
786         /* skip leading blanks */
787         while (isspace((int)*line)) {
788                 line++;
789         }
791         getP = line;
792         putP = line;
794         Quote    = 0;
795         in_arg   = 0;
796         tagcount = 0;
798         curarg_contains_rrd_directives = 0;
800         /* start parsing 'line' for arguments */
801         while (*getP)
802         {
803                 unsigned char c = *getP++;
805                 if (c == '>' && !Quote && !tagcount) {
806                         /* this is our closing tag, quit scanning */
807                         break;
808                 }
810                 /* remove all special chars */
811                 if (c < ' ') {
812                         c = ' ';
813                 }
815                 switch (c)
816                 {
817                 case ' ': 
818                         if (Quote || tagcount) {
819                                 /* copy quoted/tagged string */
820                                 *putP++ = c;
821                         }
822                         else if (in_arg)
823                         {
824                                 /* end argument string */
825                                 *putP++ = 0;
826                                 in_arg = 0;
827                                 if (curarg_contains_rrd_directives) {
828                                         argv[argc-1] = rrd_expand_vars(stralloc(argv[argc-1]));
829                                         curarg_contains_rrd_directives = 0;
830                                 }
831                         }
832                         break;
834                 case '"': /* Fall through */
835                 case '\'':
836                         if (Quote != 0) {
837                                 if (Quote == c) {
838                                         Quote = 0;
839                                 } else {
840                                         /* copy quoted string */
841                                         *putP++ = c;
842                                 }
843                         } else {
844                                 if (!in_arg) {
845                                         /* reference argument string in argument array */
846                                         argv[argc++] = putP;
847                                         in_arg=1;
848                                 }
849                                 Quote = c;
850                         }
851                         break;
853                 default:
854                         if (!Quote) {
855                                 if (!in_arg) {
856                                         /* start new argument */
857                                         argv[argc++] = putP;
858                                         in_arg = 1;
859                                 }
860                                 if (c == '>') {
861                                         if (tagcount) {
862                                                 tagcount--;
863                                         }
864                                 }
865                                 if (c == '<') {
866                                         tagcount++;
867                                         if (0 == strncmp(getP, "RRD::", strlen("RRD::"))) {
868                                                 curarg_contains_rrd_directives = 1;
869                                         }
870                                 }
871                         }
872                         *putP++ = c;
873                         break;
874                 }
876                 /* check if our argument array is still large enough */
877                 if (argc == argsz) {
878                         /* resize argument array */
879                         argsz *= 2;
880                         argv = rrd_realloc(argv, argsz * sizeof(char *));
881                         if (*argv == NULL) {
882                                 return NULL;
883                         }
884                 }
885         }
887         /* terminate last argument found */
888         *putP = '\0';
889         if (curarg_contains_rrd_directives) {
890                 argv[argc-1] = rrd_expand_vars(stralloc(argv[argc-1]));
891         }
893 #ifdef DEBUG_PARSER
894         if (argc > 0) {
895                 int n;
896                 printf("<-- arguments found [%d]\n", argc);
897                 for (n=0; n<argc; n++) {
898                         printf("arg %02d: '%s'\n", n, argv[n]);
899                 }
900                 printf("-->\n");
901         } else {
902                 printf("<!-- No arguments found -->\n");
903         }
904 #endif
906         /* update caller's notion of the argument array and it's size */
907         *arguments = argv;
908         *argument_count = argc;
910         if (Quote) {
911                 return NULL;
912         }
914         /* Return new scanning cursor:
915            pointer to char after closing bracket */
916         return getP;
920 /*
921  * Parse(): scan current portion of buffer for given tag.
922  * If found, parse tag arguments and call 'func' for it.
923  * The result of func is inserted at the current position
924  * in the buffer.
925  */
926 int
927 parse(
928         char **buf,     /* buffer */
929         long i,                 /* offset in buffer */
930         char *tag,      /* tag to handle  */
931         char *(*func)(long , const char **) /* function to call for 'tag' */
932         )
934         /* the name of the vairable ... */
935         char *val;
936         long valln;  
937         char **args;
938         char *end;
939         long end_offset;
940         int  argc;
941         size_t taglen = strlen(tag);
943         /* Current position in buffer should start with 'tag' */
944         if (strncmp((*buf)+i, tag, taglen) != 0) {
945                 return 0;
946         }
947         /* .. and match exactly (a whitespace following 'tag') */
948         if (! isspace(*((*buf) + i + taglen)) ) {
949                 return 0;
950         }
952 #ifdef DEBUG_PARSER
953         printf("parse(): handling tag '%s'\n", tag);
954 #endif
956         /* Scan for arguments following the tag;
957            scanargs() puts \0 into *buf ... so after scanargs it is probably
958            not a good time to use strlen on buf */
959         end = scanargs((*buf) + i + taglen, &argc, &args);
960         if (end)
961         {
962                 /* got arguments, call function for 'tag' with arguments */
963                 val = func(argc, args);
964                 free(args);
965         }
966         else
967         {
968                 /* unable to parse arguments, undo 0-termination by scanargs */
969                 for (; argc > 0; argc--) {
970                         *((args[argc-1])-1) = ' ';
971                 }
973                 /* next call, try parsing at current offset +1 */
974                 end = (*buf) + i + 1;
976                 val = stralloc("[ERROR: Parsing Problem with the following text\n"
977                                                 " Check original file. This may have been altered "
978                                                 "by parsing.]\n\n");
979         }
981         /* remember offset where we have to continue parsing */
982         end_offset = end - (*buf);
984         valln = 0;
985         if (val) {
986                 valln = strlen(val);
987         }
989         /* Optionally resize buffer to hold the replacement value:
990            Calculating the new length of the buffer is simple. add current
991            buffer pos (i) to length of string after replaced tag to length
992            of replacement string and add 1 for the final zero ... */
993         if (end - (*buf) < (i + valln)) {
994                 /* make sure we do not shrink the mallocd block */
995                 size_t newbufsize = i + strlen(end) + valln + 1;
996                 *buf = rrd_realloc(*buf, newbufsize);
998                 if (*buf == NULL) {
999                         perror("Realoc buf:");
1000                         exit(1);
1001                 };
1002         }
1004         /* Update new end pointer:
1005            make sure the 'end' pointer gets moved along with the 
1006            buf pointer when realloc moves memory ... */
1007         end = (*buf) + end_offset; 
1009         /* splice the variable:
1010            step 1. Shift pending data to make room for 'val' */
1011         memmove((*buf) + i + valln, end, strlen(end) + 1);
1013         /* step 2. Insert val */
1014         if (val) {
1015                 memmove((*buf)+i, val, valln);
1016                 free(val);
1017         }
1018         return (valln > 0 ? valln-1: valln);
1021 char *
1022 http_time(time_t *now) {
1023         struct tm *tmptime;
1024         static char buf[60];
1026         tmptime=gmtime(now);
1027         strftime(buf,sizeof(buf),"%a, %d %b %Y %H:%M:%S GMT",tmptime);
1028         return(buf);