+}
+
+char *drawprint(
+ long argc,
+ const char **args)
+{
+ if (argc == 1 && calcpr) {
+ long i = 0;
+
+ while (calcpr[i] != NULL)
+ i++; /*determine number lines in calcpr */
+ if (atol(args[0]) < i - 1)
+ return stralloc(calcpr[atol(args[0]) + 1]);
+ }
+ return stralloc("[ERROR: RRD::PRINT argument error]");
+}
+
+char *printtimelast(
+ long argc,
+ const char **args)
+{
+ time_t last;
+ struct tm tm_last;
+ char *buf;
+
+ if (argc == 2) {
+ buf = malloc(255);
+ if (buf == NULL) {
+ return stralloc("[ERROR: allocating strftime buffer]");
+ };
+ last = rrd_last(argc + 1, (char **) args - 1);
+ if (rrd_test_error()) {
+ char *err =
+ malloc((strlen(rrd_get_error()) +
+ DS_NAM_SIZE) * sizeof(char));
+ sprintf(err, "[ERROR: %s]", rrd_get_error());
+ rrd_clear_error();
+ return err;
+ }
+ tm_last = *localtime(&last);
+ strftime(buf, 254, args[1], &tm_last);
+ return buf;
+ }
+ if (argc < 2) {
+ return stralloc("[ERROR: too few arguments for RRD::TIME::LAST]");
+ }
+ return stralloc("[ERROR: not enough arguments for RRD::TIME::LAST]");
+}
+
+char *printtimenow(
+ long argc,
+ const char **args)
+{
+ time_t now = time(NULL);
+ struct tm tm_now;
+ char *buf;
+
+ if (argc == 1) {
+ buf = malloc(255);
+ if (buf == NULL) {
+ return stralloc("[ERROR: allocating strftime buffer]");
+ };
+ tm_now = *localtime(&now);
+ strftime(buf, 254, args[0], &tm_now);
+ return buf;
+ }
+ if (argc < 1) {
+ return stralloc("[ERROR: too few arguments for RRD::TIME::NOW]");
+ }
+ return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]");
+}
+
+/* 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; /* 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;
+ }
+
+ /* 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 (=RRD expanded) 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 start of argument string in argument array */
+ argv[argc++] = putP;
+ in_arg = 1;
+ }
+ Quote = c;
+ }
+ break;
+
+ default:
+ 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 < argc; n++) {
+ printf("arg %02d: '%s'\n", n, argv[n]);
+ }
+ printf("-->\n");
+ } else {
+ printf("<!-- No arguments found -->\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,
+ const char **) /* 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;
+ }
+#ifdef DEBUG_PARSER
+ printf("parse(): handling 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, (const char **) 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;
+ static char buf[60];
+
+ tmptime = gmtime(now);
+ strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", tmptime);
+ return (buf);
+}
+
+void rrdcgiHeader(
+ void)
+{
+ if (rrdcgiType)
+ printf("Content-type: %s\n", rrdcgiType);
+ else
+ printf("Content-type: text/html\n");
+ if (rrdcgiHeaderString)
+ printf("%s", rrdcgiHeaderString);
+ printf("\n");
+}
+
+void rrdcgiDebug(
+ int level,
+ int where)
+{
+ if (level > 0)
+ rrdcgiDebugLevel = level;
+ else
+ rrdcgiDebugLevel = 0;
+ if (where)
+ rrdcgiDebugStderr = 0;
+ else
+ rrdcgiDebugStderr = 1;
+}
+
+char *rrdcgiDecodeString(
+ char *text)
+{
+ char *cp, *xp;
+
+ for (cp = text, xp = text; *cp; cp++) {
+ if (*cp == '%') {
+ if (strchr("0123456789ABCDEFabcdef", *(cp + 1))
+ && strchr("0123456789ABCDEFabcdef", *(cp + 2))) {
+ if (islower(*(cp + 1)))
+ *(cp + 1) = toupper(*(cp + 1));
+ if (islower(*(cp + 2)))
+ *(cp + 2) = toupper(*(cp + 2));
+ *(xp) =
+ (*(cp + 1) >=
+ 'A' ? *(cp + 1) - 'A' + 10 : *(cp + 1) - '0') * 16 +
+ (*(cp + 2) >=
+ 'A' ? *(cp + 2) - 'A' + 10 : *(cp + 2) - '0');
+ xp++;
+ cp += 2;
+ }
+ } else {
+ *(xp++) = *cp;
+ }
+ }
+ memset(xp, 0, cp - xp);
+ return text;
+}
+
+/* rrdcgiReadVariables()
+ *
+ * Read from stdin if no string is provided via CGI. Variables that
+ * doesn't have a value associated with it doesn't get stored.
+ */
+s_var **rrdcgiReadVariables(
+ void)
+{
+ int length;
+ char *line = NULL;
+ int numargs;
+ char *cp, *ip, *esp, *sptr;
+ s_var **result;
+ int i, k, len;
+ char tmp[101];
+
+ cp = getenv("REQUEST_METHOD");
+ ip = getenv("CONTENT_LENGTH");
+
+ if (cp && !strcmp(cp, "POST")) {
+ if (ip) {
+ length = atoi(ip);
+ if ((line = (char *) malloc(length + 2)) == NULL)
+ return NULL;
+ if (fgets(line, length + 1, stdin) == NULL)
+ return NULL;
+ } else
+ return NULL;
+ } else if (cp && !strcmp(cp, "GET")) {
+ esp = getenv("QUERY_STRING");
+ if (esp && strlen(esp)) {
+ if ((line = (char *) malloc(strlen(esp) + 2)) == NULL)
+ return NULL;
+ sprintf(line, "%s", esp);
+ } else
+ return NULL;
+ } else {
+ length = 0;
+ printf("(offline mode: enter name=value pairs on standard input)\n");
+ memset(tmp, 0, sizeof(tmp));
+ while ((cp = fgets(tmp, 100, stdin)) != NULL) {
+ if (strlen(tmp)) {
+ if (tmp[strlen(tmp) - 1] == '\n')
+ tmp[strlen(tmp) - 1] = '&';
+ if (length) {
+ length += strlen(tmp);
+ len = (length + 1) * sizeof(char);
+ if ((line = (char *) realloc(line, len)) == NULL)
+ return NULL;
+ strcat(line, tmp);
+ } else {
+ length = strlen(tmp);
+ len = (length + 1) * sizeof(char);
+ if ((line = (char *) malloc(len)) == NULL)
+ return NULL;
+ memset(line, 0, len);
+ strcpy(line, tmp);
+ }
+ }
+ memset(tmp, 0, sizeof(tmp));
+ }
+ if (!line)
+ return NULL;
+ if (line[strlen(line) - 1] == '&')
+ line[strlen(line) - 1] = '\0';
+ }
+
+ /*
+ * From now on all cgi variables are stored in the variable line
+ * and look like foo=bar&foobar=barfoo&foofoo=
+ */
+
+ if (rrdcgiDebugLevel > 0) {
+ if (rrdcgiDebugStderr)
+ fprintf(stderr, "Received cgi input: %s\n", line);
+ else
+ printf
+ ("<b>Received cgi input</b><br>\n<pre>\n--\n%s\n--\n</pre>\n\n",
+ line);
+ }
+
+ for (cp = line; *cp; cp++)
+ if (*cp == '+')
+ *cp = ' ';
+
+ if (strlen(line)) {
+ for (numargs = 1, cp = line; *cp; cp++)
+ if (*cp == '&')
+ numargs++;
+ } else
+ numargs = 0;
+ if (rrdcgiDebugLevel > 0) {
+ if (rrdcgiDebugStderr)
+ fprintf(stderr, "%d cgi variables found.\n", numargs);
+ else
+ printf("%d cgi variables found.<br>\n", numargs);
+ }
+
+ len = (numargs + 1) * sizeof(s_var *);
+ if ((result = (s_var **) malloc(len)) == NULL)
+ return NULL;
+ memset(result, 0, len);
+
+ cp = line;
+ i = 0;
+ while (*cp) {
+ if ((ip = (char *) strchr(cp, '&')) != NULL) {
+ *ip = '\0';
+ } else
+ ip = cp + strlen(cp);
+
+ if ((esp = (char *) strchr(cp, '=')) == NULL) {
+ cp = ++ip;
+ continue;
+ }
+
+ if (!strlen(esp)) {
+ cp = ++ip;
+ continue;
+ }
+
+ if (i < numargs) {
+
+ /* try to find out if there's already such a variable */
+ for (k = 0; k < i && (strncmp(result[k]->name, cp, esp - cp)
+ || !(strlen(result[k]->name) ==
+ (size_t) (esp - cp))); k++);
+
+ if (k == i) { /* No such variable yet */
+ if ((result[i] = (s_var *) malloc(sizeof(s_var))) == NULL)
+ return NULL;
+ if ((result[i]->name =
+ (char *) malloc((esp - cp + 1) * sizeof(char))) == NULL)
+ return NULL;
+ memset(result[i]->name, 0, esp - cp + 1);
+ strncpy(result[i]->name, cp, esp - cp);
+ cp = ++esp;
+ if ((result[i]->value =
+ (char *) malloc((ip - esp + 1) * sizeof(char))) == NULL)
+ return NULL;
+ memset(result[i]->value, 0, ip - esp + 1);
+ strncpy(result[i]->value, cp, ip - esp);
+ result[i]->value = rrdcgiDecodeString(result[i]->value);
+ if (rrdcgiDebugLevel) {
+ if (rrdcgiDebugStderr)
+ fprintf(stderr, "%s: %s\n", result[i]->name,
+ result[i]->value);
+ else
+ printf("<h3>Variable %s</h3>\n<pre>\n%s\n</pre>\n\n",
+ result[i]->name, result[i]->value);
+ }
+ i++;
+ } else { /* There is already such a name, suppose a mutiple field */
+ cp = ++esp;
+ len =
+ (strlen(result[k]->value) + (ip - esp) +
+ 2) * sizeof(char);
+ if ((sptr = (char *) malloc(len)) == NULL)
+ return NULL;
+ memset(sptr, 0, len);
+ sprintf(sptr, "%s\n", result[k]->value);
+ strncat(sptr, cp, ip - esp);
+ free(result[k]->value);
+ result[k]->value = rrdcgiDecodeString(sptr);
+ }
+ }
+ cp = ++ip;
+ }
+ return result;
+}
+
+/* rrdcgiInit()
+ *
+ * Read from stdin if no string is provided via CGI. Variables that
+ * doesn't have a value associated with it doesn't get stored.
+ */
+s_cgi *rrdcgiInit(
+ void)
+{
+ s_cgi *res;
+ s_var **vars;
+
+ vars = rrdcgiReadVariables();
+
+ if (!vars)
+ return NULL;
+
+ if ((res = (s_cgi *) malloc(sizeof(s_cgi))) == NULL)
+ return NULL;
+ res->vars = vars;
+
+ return res;
+}
+
+char *rrdcgiGetValue(
+ s_cgi * parms,
+ const char *name)
+{
+ int i;
+
+ if (!parms || !parms->vars)
+ return NULL;
+ for (i = 0; parms->vars[i]; i++)
+ if (!strcmp(name, parms->vars[i]->name)) {
+ if (rrdcgiDebugLevel > 0) {
+ if (rrdcgiDebugStderr)
+ fprintf(stderr, "%s found as %s\n", name,
+ parms->vars[i]->value);
+ else
+ printf("%s found as %s<br>\n", name,
+ parms->vars[i]->value);
+ }
+ return parms->vars[i]->value;
+ }
+ if (rrdcgiDebugLevel) {
+ if (rrdcgiDebugStderr)
+ fprintf(stderr, "%s not found\n", name);
+ else
+ printf("%s not found<br>\n", name);
+ }