From: oetiker Date: Sun, 23 Nov 2003 12:55:37 +0000 (+0000) Subject: New recursive parser for rrdcgi by Arend-Jan Wijtzes X-Git-Url: https://git.octo.it/?p=rrdtool.git;a=commitdiff_plain;h=8ba25ed02f431d0ea29747eeabfaf3c032b3a18a New recursive parser for rrdcgi by Arend-Jan Wijtzes git-svn-id: svn://svn.oetiker.ch/rrdtool/trunk/program@226 a5681a0c-68f1-0310-ab6d-d61299d08faa --- diff --git a/doc/rrdcgi.pod b/doc/rrdcgi.pod index cfa79a5..30c6692 100644 --- a/doc/rrdcgi.pod +++ b/doc/rrdcgi.pod @@ -30,7 +30,7 @@ Assume that rrdcgi is being run as a filter and not as a cgi. =back -=head2 Pass 1 +=head2 Keywords =over 8 @@ -60,11 +60,6 @@ Get the value of an environment variable. might give you the name of the remote user given you are using some sort of access control on the directory -=back - -=head2 Pass 2 - -=over 8 =item RRD::GOODFOR I @@ -86,6 +81,14 @@ could use to make sure everything is presented in Universal Time. Note that the values permitted to TZ depend on your OS. +=item RRD::SETVAR I I + +Analog to SETENV but for local variables + +=item RRD::GETVAR I + +Analog to GETENV but for local variables + =item RRD::TIME::LAST I I This gets replaced by the last modification time of the selected RRD. The @@ -105,12 +108,6 @@ must be supplied as either could be relative to the other. This is intended to allow pretty titles on graphs with times that are easier for non rrdtool folks to figure out than "-2weeks". -=back - -=head2 Pass 3 - -=over 8 - =item RRD::GRAPH I This tag creates the RRD graph defined in its argument and then gets diff --git a/src/rrd_cgi.c b/src/rrd_cgi.c index 7f248ab..4a70792 100644 --- a/src/rrd_cgi.c +++ b/src/rrd_cgi.c @@ -1,5 +1,5 @@ /***************************************************************************** - * RRDtool 1.1.x Copyright Tobias Oetiker, 1997 - 2002 + * RRDtool 1.1.x Copyright Tobias Oetiker, 1997 - 2003 ***************************************************************************** * rrd_cgi.c RRD Web Page Generator *****************************************************************************/ @@ -10,6 +10,8 @@ #define MEMBLK 1024 +/*#define DEBUG_PARSER +#define DEBUG_VARS*/ /* global variable for libcgi */ s_cgi **cgiArg; @@ -32,7 +34,7 @@ char* cgigetq(long , char **); /* return a quoted and sanitized cgi variable */ char* cgigetqp(long , char **); -/* call rrd_graph and insert apropriate image tag */ +/* call rrd_graph and insert appropriate image tag */ char* drawgraph(long, char **); /* return PRINT functions from last rrd_graph call */ @@ -44,10 +46,10 @@ char* printtimelast(long, char **); /* pretty-print current time */ char* printtimenow(long,char **); -/* set an evironment variable */ +/* set an environment variable */ char* rrdsetenv(long, char **); -/* get an evironment variable */ +/* get an environment variable */ char* rrdgetenv(long, char **); /* include the named file at this point */ @@ -56,14 +58,171 @@ char* includefile(long, char **); /* for how long is the output of the cgi valid ? */ char* rrdgoodfor(long, char **); +char* rrdstrip(char *buf); +char* scanargs(char *line, int *argc, char ***args); + /* format at-time specified times using strftime */ char* printstrftime(long, char**); -/** http protocol needs special format, and GMT time **/ +/** HTTP protocol needs special format, and GMT time **/ char *http_time(time_t *); -/* return a pointer to newly alocated copy of this string */ -char *stralloc(char *); +/* return a pointer to newly allocated copy of this string */ +char *stralloc(const char *); + + +/* rrd interface to the variable functions {put,get}var() */ +char* rrdgetvar(long argc, char **args); +char* rrdsetvar(long argc, char **args); +char* rrdsetvarconst(long argc, char **args); + + +/* variable store: put/get key-value pairs */ +static int initvar(); +static void donevar(); +static const char* getvar(const char* varname); +static const char* putvar(const char* name, const char* value, int is_const); + +/* key value pair that makes up an entry in the variable store */ +typedef struct +{ + int is_const; /* const variable or not */ + const char* name; /* variable name */ + const char* value; /* variable value */ +} vardata; + +/* the variable heap: + start with a heapsize of 10 variables */ +#define INIT_VARSTORE_SIZE 10 +static vardata* varheap = NULL; +static size_t varheap_size = 0; + +/* allocate and initialize variable heap */ +static int +initvar() +{ + varheap = (vardata*)malloc(sizeof(vardata) * INIT_VARSTORE_SIZE); + if (varheap == NULL) { + fprintf(stderr, "ERROR: unable to initialize variable store\n"); + return -1; + } + memset(varheap, 0, sizeof(vardata) * INIT_VARSTORE_SIZE); + varheap_size = INIT_VARSTORE_SIZE; + return 0; +} + +/* cleanup: free allocated memory */ +static void +donevar() +{ + int i; + if (varheap) { + for (i=0; i %s -->\n", name, varheap[i].value); +#endif + return varheap[i].value; + } + } +#ifdef DEBUG_VARS + printf("\n", name); +#endif + return NULL; +} + +/* Put a variable into the variable store. If a variable by that + name exists, it's value is overwritten with the new value unless it was + marked as 'const' (initialized by RRD::SETCONSTVAR). + Returns a copy the newly allocated value on success, NULL on error. */ +static const char* +putvar(const char* name, const char* value, int is_const) +{ + int i; + for (i=0; i < varheap_size && varheap[i].name; i++) { + if (0 == strcmp(name, varheap[i].name)) { + /* overwrite existing entry */ + if (varheap[i].is_const) { +#ifdef DEBUG_VARS + printf("\n", name, value); +# endif + return varheap[i].value; + } +#ifdef DEBUG_VARS + printf("\n", + name, value, varheap[i].value); +#endif + /* make it possible to promote a variable to readonly */ + varheap[i].is_const = is_const; + free((char*)varheap[i].value); + varheap[i].value = stralloc(value); + return varheap[i].value; + } + } + + /* no existing variable found by that name, add it */ + if (i == varheap_size) { + /* ran out of heap: resize heap to double size */ + size_t new_size = varheap_size * 2; + varheap = (vardata*)(realloc(varheap, sizeof(vardata) * new_size)); + if (!varheap) { + fprintf(stderr, "ERROR: Unable to realloc variable heap\n"); + return NULL; + } + /* initialize newly allocated memory */; + memset(&varheap[varheap_size], 0, sizeof(vardata) * varheap_size); + varheap_size = new_size; + } + varheap[i].is_const = is_const; + varheap[i].name = stralloc(name); + varheap[i].value = stralloc(value); + +#ifdef DEBUG_VARS + printf("\n", name, value); +#endif + return varheap[i].value; +} + +/* expand those RRD:* directives that can be used recursivly */ +static char* +rrd_expand_vars(char* buffer) +{ + int i; + +#ifdef DEBUG_PARSER + printf("expanding variables in '%s'\n", buffer); +#endif + + for (i=0; buffer[i]; i++) { + if (buffer[i] != '<') + continue; + parse(&buffer, i, "= argc ) { + fprintf(stderr, "ERROR: expected a filename\n"); + exit(1); + } else { + length = readfile(argv[optind], &buffer, 1); + } - /* pass 2 */ - for (i=0;buffer[i] != '\0'; i++){ - i += parse(&buffer,i," 0){ - time_t now; - now = time(NULL); - printf ("Last-Modified: %s\n",http_time(&now)); - now += labs(goodfor); - printf ("Expires: %s\n",http_time(&now)); - if (goodfor < 0) { - printf("Refresh: %ld\n", labs(goodfor)); - } - } - printf ("\n"); - } - printf ("%s", buffer); - calfree(); - if (buffer){ - free(buffer); - } - exit(0); + if (!filter) { + printf ("Content-Type: text/html\n" + "Content-Length: %d\n", + strlen(buffer)); + + if (labs(goodfor) > 0) { + time_t now; + now = time(NULL); + printf("Last-Modified: %s\n", http_time(&now)); + now += labs(goodfor); + printf("Expires: %s\n", http_time(&now)); + if (goodfor < 0) { + printf("Refresh: %ld\n", labs(goodfor)); + } + } + printf("\n"); + } + + /* output result */ + printf("%s", buffer); + + /* cleanup */ + calfree(); + if (buffer){ + free(buffer); + } + donevar(); + exit(0); } -/* remove occurences of .. this is a general measure to make +/* remove occurrences of .. this is a general measure to make paths which came in via cgi do not go UP ... */ -char* rrdsetenv(long argc, char **args){ - if (argc >= 2) { - char *xyz=malloc((strlen(args[0])+strlen(args[1])+3)*sizeof(char)); - if (xyz == NULL){ - return stralloc("[ERROR: allocating setenv buffer]"); - }; - sprintf(xyz,"%s=%s",args[0],args[1]); - if( putenv(xyz) == -1) { - return stralloc("[ERROR: faild to do putenv]"); - }; - } else { - return stralloc("[ERROR: setenv faild because not enough arguments were defined]"); - } - return stralloc(""); +char* rrdsetenv(long argc, char **args) { + if (argc >= 2) { + char *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2)); + if (xyz == NULL) { + return stralloc("[ERROR: allocating setenv buffer]"); + }; + sprintf(xyz, "%s=%s", args[0], args[1]); + if(putenv(xyz) == -1) { + free(xyz); + return stralloc("[ERROR: failed to do putenv]"); + }; + } + return stralloc("[ERROR: setenv failed because not enough " + "arguments were defined]"); } -char* rrdgetenv(long argc, char **args){ - if (argc != 1) { - return stralloc("[ERROR: getenv faild because it did not get 1 argument only]"); - } - else if (getenv(args[0]) == NULL) { - return stralloc(""); - } - else { - return stralloc(getenv(args[0])); - } +/* rrd interface to the variable function putvar() */ +char* +rrdsetvar(long argc, char **args) +{ + if (argc >= 2) + { + const char* result = putvar(args[0], args[1], 0 /* not const */); + if (result) { + /* setvar does not return the value set */ + return stralloc(""); + } + return stralloc("[ERROR: putvar failed]"); + } + return stralloc("[ERROR: putvar failed because not enough arguments " + "were defined]"); +} + +/* rrd interface to the variable function putvar() */ +char* +rrdsetvarconst(long argc, char **args) +{ + if (argc >= 2) + { + const char* result = putvar(args[0], args[1], 1 /* const */); + if (result) { + /* setvar does not return the value set */ + return stralloc(""); + } + return stralloc("[ERROR: putvar failed]"); + } + return stralloc("[ERROR: putvar failed because not enough arguments " + "were defined]"); +} + +char* rrdgetenv(long argc, char **args) { + char buf[128]; + const char* envvar; + if (argc != 1) { + return stralloc("[ERROR: getenv failed because it did not " + "get 1 argument only]"); + }; + envvar = getenv(args[0]); + if (envvar) { + return stralloc(envvar); + } else { + snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]); + return stralloc(buf); + } +} + +char* rrdgetvar(long argc, char **args) { + char buf[128]; + const char* value; + if (argc != 1) { + return stralloc("[ERROR: getvar failed because it did not " + "get 1 argument only]"); + }; + value = getvar(args[0]); + if (value) { + return stralloc(value); + } else { + snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]); + return stralloc(buf); + } } char* rrdgoodfor(long argc, char **args){ @@ -247,7 +473,7 @@ char* rrdgoodfor(long argc, char **args){ * */ #define MAX_STRFTIME_SIZE 256 char* printstrftime(long argc, char **args){ - struct rrd_time_value start_tv, end_tv; + struct time_value start_tv, end_tv; char *parsetime_error = NULL; char formatted[MAX_STRFTIME_SIZE]; struct tm *the_tm; @@ -301,9 +527,10 @@ char* printstrftime(long argc, char **args){ char* includefile(long argc, char **args){ char *buffer; if (argc >= 1) { - readfile(args[0], &buffer, 0); + char* filename = args[0]; + readfile(filename, &buffer, 0); if (rrd_test_error()) { - char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE)*sizeof(char)); + char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE)); sprintf(err, "[ERROR: %s]",rrd_get_error()); rrd_clear_error(); return err; @@ -317,17 +544,24 @@ char* includefile(long argc, char **args){ } } -static -char* rrdstrip(char *buf){ - char *start; - if (buf == NULL) return NULL; - buf = stralloc(buf); /* make a copy of the buffer */ - if (buf == NULL) return NULL; - while ((start = strstr(buf,"<"))){ - *start = '_'; +/* make a copy of buf and replace open/close brackets with '_' */ +char* rrdstrip(char *buf) { + char* p; + if (buf == NULL) { + return NULL; } - while ((start = strstr(buf,">"))){ - *start = '_'; + /* make a copy of the buffer */ + buf = stralloc(buf); + if (buf == NULL) { + return NULL; + } + + p = buf; + while (*p) { + if (*p == '<' || *p == '>') { + *p = '_'; + } + p++; } return buf; } @@ -342,7 +576,7 @@ char* cgigetq(long argc, char **args){ for(c=buf;*c != '\0';c++) if (*c == '"') qc++; - if((buf2=malloc((strlen(buf) + qc*4 +4) * sizeof(char)))==NULL){ + if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) { perror("Malloc Buffer"); exit(1); }; @@ -367,53 +601,59 @@ char* cgigetq(long argc, char **args){ return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]"); } -/* remove occurences of .. this is a general measure to make +/* remove occurrences of .. this is a general measure to make paths which came in via cgi do not go UP ... */ char* cgigetqp(long argc, char **args){ - if (argc>= 1){ + if (argc>= 1) { char *buf = rrdstrip(cgiGetValue(cgiArg,args[0])); char *buf2; char *c,*d; int qc=0; - if (buf==NULL) return NULL; - for(c=buf;*c != '\0';c++) - if (*c == '"') qc++; - if((buf2=malloc((strlen(buf) + qc*4 +4) * sizeof(char)))==NULL){ - perror("Malloc Buffer"); - exit(1); + if (buf==NULL) + return NULL; + + for(c=buf;*c != '\0';c++) { + if (*c == '"') { + qc++; + } + } + + if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) { + perror("Malloc Buffer"); + exit(1); }; + c=buf; d=buf2; + *(d++) = '"'; - while(*c != '\0'){ - if (*c == '"') { - *(d++) = '"'; - *(d++) = '\''; - *(d++) = '"'; - *(d++) = '\''; - } - if(*c == '/') { - *(d++) = '_';c++; - } else { - if (*c=='.' && *(c+1) == '.'){ - c += 2; - *(d++) = '_'; *(d++) ='_'; - } else { - - *(d++) = *(c++); - } - } + while (*c != '\0') { + if (*c == '"') { + *(d++) = '"'; + *(d++) = '\''; + *(d++) = '"'; + *(d++) = '\''; + } + if(*c == '/') { + *(d++) = '_'; + c++; + } else { + if (*c=='.' && *(c+1) == '.') { + c += 2; + *(d++) = '_'; *(d++) ='_'; + } else { + *(d++) = *(c++); + } + } } *(d++) = '"'; *(d) = '\0'; free(buf); return buf2; } - return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]"); - } @@ -437,7 +677,7 @@ char* drawgraph(long argc, char **args){ optind=0; /* reset gnu getopt */ opterr=0; /* reset gnu getopt */ calfree(); - if( rrd_graph(argc+1, args-1, &calcpr, &xsize, &ysize, NULL) != -1 ) { + if( rrd_graph(argc+1, args-1, &calcpr, &xsize, &ysize) != -1 ) { return stralloc(calcpr[0]); } else { if (rrd_test_error()) { @@ -477,7 +717,7 @@ char* printtimelast(long argc, char **args) { rrd_clear_error(); return err; } - localtime_r(&last, &tm_last); + tm_last = *localtime(&last); strftime(buf,254,args[1],&tm_last); return buf; } @@ -496,7 +736,7 @@ char* printtimenow(long argc, char **args) { if (buf == NULL){ return stralloc("[ERROR: allocating strftime buffer]"); }; - localtime_r(&now, &tm_now); + tm_now = *localtime(&now); strftime(buf,254,args[0],&tm_now); return buf; } @@ -506,146 +746,285 @@ char* printtimenow(long argc, char **args) { return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]"); } -/* scan aLine until an unescaped '>' arives */ -static -char* scanargs(char *aLine, long *argc, char ***args) +/* Scan buffer until an unescaped '>' arives. + * Update argument array with arguments found. + * Return end cursor where parsing stopped, or NULL in case of failure. + * + * FIXME: + * To allow nested constructs, we call rrd_expand_vars() for arguments + * that contain RRD::x directives. These introduce a small memory leak + * since we have to stralloc the arguments the way parse() works. + */ +char* +scanargs(char *line, int *argument_count, char ***arguments) { - char *getP, *putP; - char Quote = 0; - int argal = MEMBLK; - int braket = 0; - int inArg = 0; - if (((*args) = (char **) malloc(MEMBLK*sizeof(char *))) == NULL) { - return NULL; - } - /* sikp leading blanks */ - while (*aLine && *aLine <= ' ') aLine++; - - *argc = 0; - getP = aLine; - putP = aLine; - while (*getP && !( !Quote && (braket == 0) && ((*getP) == '>'))){ - if ((unsigned)*getP < ' ') *getP = ' '; /*remove all special chars*/ - switch (*getP) { - case ' ': - if (Quote){ - *(putP++)=*getP; - } else - if(inArg) { - *(putP++) = 0; - inArg = 0; - } - break; - case '"': - case '\'': - if (Quote != 0) { - if (Quote == *getP) - Quote = 0; - else { - *(putP++)=*getP; + char *getP; /* read cursor */ + char *putP; /* write cursor */ + char Quote; /* type of quote if in quoted string, 0 otherwise */ + int tagcount; /* open tag count */ + int in_arg; /* if we currently are parsing an argument or not */ + int argsz; /* argument array size */ + int curarg_contains_rrd_directives; + + /* local array of arguments while parsing */ + int argc = 0; + char** argv; + +#ifdef DEBUG_PARSER + printf("<-- scanargs(%s) -->\n", line); +#endif + + *arguments = NULL; + *argument_count = 0; + + /* create initial argument array of char pointers */ + argsz = 32; + argv = (char **)malloc(argsz * sizeof(char *)); + if (!argv) { + return NULL; } - } else { - if(!inArg){ - (*args)[++(*argc)] = putP; - inArg=1; - } - Quote = *getP; - } - break; - default: - if (Quote == 0 && (*getP) == '<') { - braket++; - } - if (Quote == 0 && (*getP) == '>') { - braket--; - } - if(!inArg){ - (*args)[++(*argc)] = putP; - inArg=1; - } - *(putP++)=*getP; - break; - } - if ((*argc) >= argal-10 ) { - argal += MEMBLK; - if (((*args)=rrd_realloc((*args),(argal)*sizeof(char *))) == NULL) { - return NULL; - } - } - getP++; - } - - *putP = '\0'; - (*argc)++; - if (Quote) - return NULL; - else - return getP+1; /* pointer to next char after parameter */ + /* skip leading blanks */ + while (isspace((int)*line)) { + line++; + } + + getP = line; + putP = line; + + Quote = 0; + in_arg = 0; + tagcount = 0; + + curarg_contains_rrd_directives = 0; + + /* start parsing 'line' for arguments */ + while (*getP) + { + unsigned char c = *getP++; + + if (c == '>' && !Quote && !tagcount) { + /* this is our closing tag, quit scanning */ + break; + } + + /* remove all special chars */ + if (c < ' ') { + c = ' '; + } + + switch (c) + { + case ' ': + if (Quote || tagcount) { + /* copy quoted/tagged string */ + *putP++ = c; + } + else if (in_arg) + { + /* end argument string */ + *putP++ = 0; + in_arg = 0; + if (curarg_contains_rrd_directives) { + argv[argc-1] = rrd_expand_vars(stralloc(argv[argc-1])); + curarg_contains_rrd_directives = 0; + } + } + break; + + case '"': /* Fall through */ + case '\'': + if (Quote != 0) { + if (Quote == c) { + Quote = 0; + } else { + /* copy quoted string */ + *putP++ = c; + } + } else { + if (!in_arg) { + /* reference argument string in argument array */ + argv[argc++] = putP; + in_arg=1; + } + Quote = c; + } + break; + + default: + if (!Quote) { + if (!in_arg) { + /* start new argument */ + argv[argc++] = putP; + in_arg = 1; + } + if (c == '>') { + if (tagcount) { + tagcount--; + } + } + if (c == '<') { + tagcount++; + if (0 == strncmp(getP, "RRD::", strlen("RRD::"))) { + curarg_contains_rrd_directives = 1; + } + } + } + *putP++ = c; + break; + } + + /* check if our argument array is still large enough */ + if (argc == argsz) { + /* resize argument array */ + argsz *= 2; + argv = rrd_realloc(argv, argsz * sizeof(char *)); + if (*argv == NULL) { + return NULL; + } + } + } + + /* terminate last argument found */ + *putP = '\0'; + if (curarg_contains_rrd_directives) { + argv[argc-1] = rrd_expand_vars(stralloc(argv[argc-1])); + } + +#ifdef DEBUG_PARSER + if (argc > 0) { + int n; + printf("<-- arguments found [%d]\n", argc); + for (n=0; n\n"); + } else { + printf("\n"); + } +#endif + + /* update caller's notion of the argument array and it's size */ + *arguments = argv; + *argument_count = argc; + + if (Quote) { + return NULL; + } + + /* Return new scanning cursor: + pointer to char after closing bracket */ + return getP; } +/* + * Parse(): scan current portion of buffer for given tag. + * If found, parse tag arguments and call 'func' for it. + * The result of func is inserted at the current position + * in the buffer. + */ +int +parse( + char **buf, /* buffer */ + long i, /* offset in buffer */ + char *tag, /* tag to handle */ + char *(*func)(long argc, char **args) /* function to call for 'tag' */ + ) +{ + /* the name of the vairable ... */ + char *val; + long valln; + char **args; + char *end; + long end_offset; + int argc; + size_t taglen = strlen(tag); + + /* Current position in buffer should start with 'tag' */ + if (strncmp((*buf)+i, tag, taglen) != 0) { + return 0; + } + /* .. and match exactly (a whitespace following 'tag') */ + if (! isspace(*((*buf) + i + taglen)) ) { + return 0; + } -int parse(char **buf, long i, char *tag, - char *(*func)(long argc, char **args)){ - - /* the name of the vairable ... */ - char *val; - long valln; - char **args; - char *end; - long end_offset; - long argc; - /* do we like it ? */ - if (strncmp((*buf)+i, tag, strlen(tag))!=0) return 0; - if (! isspace(*( (*buf) + i + strlen(tag) )) ) return 0; - /* scanargs puts \0 into *buf ... so after scanargs it is probably - not a good time to use strlen on buf */ - end = scanargs((*buf)+i+strlen(tag),&argc,&args); - if (! end) { - for (;argc>2;argc--){ - *((args[argc-1])-1)=' '; - } - val = stralloc("[ERROR: Parsing Problem with the following text\n" - " Check original file. This may have been altered by parsing.]\n\n"); - end = (*buf)+i+1; - } else { - val = func(argc-1,args+1); - free (args); - } - /* for (ii=0;ii 0 ? valln-1: valln; +#ifdef DEBUG_PARSER + printf("parse(): handeling tag '%s'\n", tag); +#endif + + /* Scan for arguments following the tag; + scanargs() puts \0 into *buf ... so after scanargs it is probably + not a good time to use strlen on buf */ + end = scanargs((*buf) + i + taglen, &argc, &args); + if (end) + { + /* got arguments, call function for 'tag' with arguments */ + val = func(argc, args); + free(args); + } + else + { + /* unable to parse arguments, undo 0-termination by scanargs */ + for (; argc > 0; argc--) { + *((args[argc-1])-1) = ' '; + } + + /* next call, try parsing at current offset +1 */ + end = (*buf) + i + 1; + + val = stralloc("[ERROR: Parsing Problem with the following text\n" + " Check original file. This may have been altered " + "by parsing.]\n\n"); + } + + /* remember offset where we have to continue parsing */ + end_offset = end - (*buf); + + valln = 0; + if (val) { + valln = strlen(val); + } + + /* Optionally resize buffer to hold the replacement value: + Calculating the new length of the buffer is simple. add current + buffer pos (i) to length of string after replaced tag to length + of replacement string and add 1 for the final zero ... */ + if (end - (*buf) < (i + valln)) { + /* make sure we do not shrink the mallocd block */ + size_t newbufsize = i + strlen(end) + valln + 1; + *buf = rrd_realloc(*buf, newbufsize); + + if (*buf == NULL) { + perror("Realoc buf:"); + exit(1); + }; + } + + /* Update new end pointer: + make sure the 'end' pointer gets moved along with the + buf pointer when realloc moves memory ... */ + end = (*buf) + end_offset; + + /* splice the variable: + step 1. Shift pending data to make room for 'val' */ + memmove((*buf) + i + valln, end, strlen(end) + 1); + + /* step 2. Insert val */ + if (val) { + memmove((*buf)+i, val, valln); + free(val); + } + return (valln > 0 ? valln-1: valln); } char * http_time(time_t *now) { - struct tm tmptime; + struct tm *tmptime; static char buf[60]; - gmtime_r(now, &tmptime); - strftime(buf,sizeof(buf),"%a, %d %b %Y %H:%M:%S GMT", &tmptime); + tmptime=gmtime(now); + strftime(buf,sizeof(buf),"%a, %d %b %Y %H:%M:%S GMT",tmptime); return(buf); }