+/* 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<varheap_size; i++) {
+ if (varheap[i].name) {
+ free((char*)varheap[i].name);
+ }
+ if (varheap[i].value) {
+ free((char*)varheap[i].value);
+ }
+ }
+ free(varheap);
+ }
+}
+
+/* Get a variable from the variable store.
+ Return NULL in case the requested variable was not found. */
+static const char*
+getvar(const char* name)
+{
+ int i;
+ for (i=0; i<varheap_size && varheap[i].name; i++) {
+ if (0 == strcmp(name, varheap[i].name)) {
+#ifdef DEBUG_VARS
+ printf("<!-- getvar(%s) -> %s -->\n", name, varheap[i].value);
+#endif
+ return varheap[i].value;
+ }
+ }
+#ifdef DEBUG_VARS
+ printf("<!-- getvar(%s) -> Not found-->\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("<!-- setver(%s, %s): not assigning: "
+ "const variable -->\n", name, value);
+# endif
+ return varheap[i].value;
+ }
+#ifdef DEBUG_VARS
+ printf("<!-- setvar(%s, %s): overwriting old value (%s) -->\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("<!-- setvar(%s, %s): adding new variable -->\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, "<RRD::CV", cgiget);
+ parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
+ parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
+ parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
+ parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
+ }
+ return buffer;
+}