support for dashed lines in graphs
[rrdtool.git] / src / rrd_graph_helper.c
index 0c1c7d8..d4b3732 100644 (file)
@@ -1,5 +1,5 @@
 /****************************************************************************
- * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ * RRDtool 1.2.99907080300  Copyright by Tobi Oetiker, 1997-2007
  ****************************************************************************
  * rrd_graph_helper.c  commandline parser functions 
  *                     this code initially written by Alex van den Bogaerdt
@@ -52,66 +52,82 @@ int       rrd_parse_find_gf(
     const char *const,
     unsigned int *const,
     graph_desc_t *const);
+
 int       rrd_parse_legend(
     const char *const,
     unsigned int *const,
     graph_desc_t *const);
+
 int       rrd_parse_color(
     const char *const,
     graph_desc_t *const);
+
+int       rrd_parse_textalign(
+    const char *const,
+    unsigned int *const,
+    graph_desc_t *const);
+
+
 int       rrd_parse_CF(
     const char *const,
     unsigned int *const,
     graph_desc_t *const,
     enum cf_en *const);
+
 int       rrd_parse_print(
     const char *const,
     unsigned int *const,
     graph_desc_t *const,
     image_desc_t *const);
+
 int       rrd_parse_shift(
     const char *const,
     unsigned int *const,
     graph_desc_t *const,
     image_desc_t *const);
+
 int       rrd_parse_xport(
     const char *const,
     unsigned int *const,
     graph_desc_t *const,
     image_desc_t *const);
+
 int       rrd_parse_PVHLAST(
     const char *const,
     unsigned int *const,
     graph_desc_t *const,
     image_desc_t *const);
+
 int       rrd_parse_make_vname(
     const char *const,
     unsigned int *const,
     graph_desc_t *const,
     image_desc_t *const);
+
 int       rrd_parse_find_vname(
     const char *const,
     unsigned int *const,
     graph_desc_t *const,
     image_desc_t *const);
+
 int       rrd_parse_def(
     const char *const,
     unsigned int *const,
     graph_desc_t *const,
     image_desc_t *const);
+
 int       rrd_parse_vdef(
     const char *const,
     unsigned int *const,
     graph_desc_t *const,
     image_desc_t *const);
+
 int       rrd_parse_cdef(
     const char *const,
     unsigned int *const,
     graph_desc_t *const,
     image_desc_t *const);
 
-
-
 int rrd_parse_find_gf(
     const char *const line,
     unsigned int *const eaten,
@@ -158,7 +174,7 @@ int rrd_parse_find_gf(
     case GF_LINE:
         if (c1 == ':') {
             gdp->linewidth = 1;
-            dprintf("- using default width of 1\n");
+            dprintf("- using default width of 1\n");
         } else {
             i = 0;
             sscanf(&line[*eaten], "%lf:%n", &gdp->linewidth, &i);
@@ -167,7 +183,7 @@ int rrd_parse_find_gf(
                               &line[*eaten], line);
                 return 1;
             } else {
-                dprintf("- scanned width %f\n", gdp->linewidth);
+                dprintf("- scanned width %f\n", gdp->linewidth);
                 if (isnan(gdp->linewidth)) {
                     rrd_set_error
                         ("LINE width '%s' is not a number in line '%s'\n",
@@ -266,7 +282,7 @@ int rrd_parse_color(
     default:
         return 1;       /* wrong number of digits */
     }
-    gdp->col = r << 24 | g << 16 | b << 8 | a;
+    gdp->col = gfx_hex_to_col(r << 24 | g << 16 | b << 8 | a);
     return 0;
 }
 
@@ -429,9 +445,12 @@ int rrd_parse_shift(
             return 1;
         }
     } else {
+        long      time_tmp = 0;
+
         rrd_clear_error();
         i = 0;
-        sscanf(&line[*eaten], "%li%n", &gdp->shval, &i);
+        sscanf(&line[*eaten], "%li%n", &time_tmp, &i);
+        gdp->shval = time_tmp;
         if (i != (int) strlen(&line[*eaten])) {
             rrd_set_error("Not a valid offset: %s in line %s", &line[*eaten],
                           line);
@@ -475,6 +494,28 @@ int rrd_parse_xport(
     return 0;
 }
 
+int rrd_parse_textalign(
+    const char *const line,
+    unsigned int *const eaten,
+    graph_desc_t *const gdp)
+{
+    if (strcmp(&line[*eaten], "left") == 0) {
+        gdp->txtalign = TXA_LEFT;
+    } else if (strcmp(&line[*eaten], "right") == 0) {
+        gdp->txtalign = TXA_RIGHT;
+    } else if (strcmp(&line[*eaten], "justified") == 0) {
+        gdp->txtalign = TXA_JUSTIFIED;
+    } else if (strcmp(&line[*eaten], "center") == 0) {
+        gdp->txtalign = TXA_CENTER;
+    } else {
+        rrd_set_error("Unknown alignement type '%s'", &line[*eaten]);
+        return 1;
+    }
+    *eaten += strlen(&line[*eaten]);
+    return 0;
+}
+
+
 /* Parsing of PART, VRULE, HRULE, LINE, AREA, STACK and TICK
 ** is done in one function.
 **
@@ -497,23 +538,22 @@ int rrd_parse_PVHLAST(
     static int spacecnt = 0;
 
     if (spacecnt == 0) {
-        float     one_space = gfx_get_text_width(im->canvas, 0,
+        float     one_space = gfx_get_text_width(im, 0,
                                                  im->
                                                  text_prop[TEXT_PROP_LEGEND].
                                                  font,
                                                  im->
                                                  text_prop[TEXT_PROP_LEGEND].
                                                  size,
-                                                 im->tabwidth, "    ",
-                                                 0) / 4.0;
-        float     target_space = gfx_get_text_width(im->canvas, 0,
+                                                 im->tabwidth, "    ") / 4.0;
+        float     target_space = gfx_get_text_width(im, 0,
                                                     im->
                                                     text_prop
                                                     [TEXT_PROP_LEGEND].font,
                                                     im->
                                                     text_prop
                                                     [TEXT_PROP_LEGEND].size,
-                                                    im->tabwidth, "oo", 0);
+                                                    im->tabwidth, "oo");
 
         spacecnt = target_space / one_space;
         dprintf("- spacecnt: %i onespace: %f targspace: %f\n", spacecnt,
@@ -557,9 +597,6 @@ int rrd_parse_PVHLAST(
     if ((gdp->vidx = find_var(im, tmpstr)) >= 0) {
         dprintf("- found vname: '%s' vidx %li\n", tmpstr, gdp->vidx);
         switch (gdp->gf) {
-#ifdef WITH_PIECHART
-        case GF_PART:
-#endif
         case GF_VRULE:
         case GF_HRULE:
             if (im->gdes[gdp->vidx].gf != GF_VDEF) {
@@ -571,11 +608,14 @@ int rrd_parse_PVHLAST(
         default:;
         }
     } else {
+        long      time_tmp = 0;
+
         dprintf("- it is not an existing vname\n");
         switch (gdp->gf) {
         case GF_VRULE:
             k = 0;
-            sscanf(tmpstr, "%li%n", &gdp->xrule, &k);
+            sscanf(tmpstr, "%li%n", &time_tmp, &k);
+            gdp->xrule = time_tmp;
             if (((j != 0) && (k == j)) || ((j == 0) && (k == i))) {
                 dprintf("- found time: %li\n", gdp->xrule);
             } else {
@@ -589,9 +629,9 @@ int rrd_parse_PVHLAST(
             k = 0;
             sscanf(tmpstr, "%lf%n", &gdp->yrule, &k);
             if (((j != 0) && (k == j)) || ((j == 0) && (k == i))) {
-                dprintf("- found number: %f\n", gdp->yrule);
+                dprintf("- found number: %lf\n", gdp->yrule);
             } else {
-                dprintf("- is is not a valid number: %li\n", gdp->xrule);
+                dprintf("- is is not a valid number: %lf\n", gdp->yrule);
                 rrd_set_error
                     ("parameter '%s' does not represent a number in line %s\n",
                      tmpstr, line);
@@ -607,7 +647,8 @@ int rrd_parse_PVHLAST(
             rrd_set_error("Could not parse color in '%s'", &tmpstr[j]);
             return 1;
         }
-        dprintf("- parsed color 0x%08x\n", (unsigned int) gdp->col);
+        dprintf("- parsed color %0.0f,%0.0f,%0.0f,%0.0f\n", gdp->col.red,
+                gdp->col.green, gdp->col.blue, gdp->col.alpha);
         colorfound = 1;
     } else {
         dprintf("- no color present in '%s'\n", tmpstr);
@@ -699,31 +740,110 @@ int rrd_parse_PVHLAST(
     (*eaten)++;         /* after colon */
 
     /* PART, HRULE, VRULE and TICK cannot be stacked. */
-    if ((gdp->gf == GF_HRULE)
-        || (gdp->gf == GF_VRULE)
-#ifdef WITH_PIECHART
-        || (gdp->gf == GF_PART)
-#endif
-        || (gdp->gf == GF_TICK)
-        )
-        return 0;
+    if ((gdp->gf != GF_HRULE)
+        && (gdp->gf != GF_VRULE)
+        && (gdp->gf != GF_TICK)) {
 
-    dprintf("- parsing '%s'\n", &line[*eaten]);
-    if (line[*eaten] != '\0') {
-        dprintf("- still more, should be STACK\n");
+        dprintf("- parsing '%s', looking for STACK\n", &line[*eaten]);
         j = scan_for_col(&line[*eaten], 5, tmpstr);
-        if (line[*eaten + j] != '\0' && line[*eaten + j] != ':') {
-            /* not 5 chars */
-            rrd_set_error("Garbage found where STACK expected");
-            return 1;
-        }
         if (!strcmp("STACK", tmpstr)) {
             dprintf("- found STACK\n");
             gdp->stack = 1;
             (*eaten) += j;
-        } else {
-            rrd_set_error("Garbage found where STACK expected");
-            return 1;
+            if (line[*eaten] == ':') {
+                (*eaten) += 1;
+            } else if (line[*eaten] == '\0') {
+                dprintf("- done parsing line\n");
+                return 0;
+            } else {
+                dprintf("- found %s instead of just STACK\n", &line[*eaten]);
+                rrd_set_error("STACK expected but %s found", &line[*eaten]);
+                return 1;
+            }
+        } else
+            dprintf("- not STACKing\n");
+    }
+
+    dprintf("- still more, should be DASHED[:...]\n");
+    dprintf("- parsing '%s'\n", &line[*eaten]);
+    if (line[*eaten] != '\0') {
+        // parse dash arguments here. Possible options:
+        // DASHED
+        // DASHED:dashes=n_on,n_off,n_on,n_off
+        // DASHED:dashes=n_on,n_off,n_on,n_off:dash-offset=offset
+        // start with DASHED
+        j = scan_for_col(&line[*eaten], 6, tmpstr);
+        if (!strcmp("DASHED", tmpstr)) {
+            dprintf("- found %s\n", tmpstr);
+            // initialise all required variables we need for dashed lines
+            // using default dash length of 5 pixels
+            gdp->dash = 1;
+            gdp->p_dashes = (double *) malloc(sizeof(double));
+            gdp->p_dashes[0] = 5;
+            gdp->ndash = 1;
+            gdp->offset = 0;
+            (*eaten) += j;
+            if (line[*eaten] == ':')
+                (*eaten) += 1;
+        }
+        if (line[*eaten] == '\0') {
+            dprintf("- done parsing line\n");
+            return 0;
+        }
+        // DASHED:dashes=n_on,n_off,n_on,n_off
+        // allowing 64 characters for definition of dash style
+        j = scan_for_col(&line[*eaten], 64, tmpstr);
+        if (sscanf(tmpstr, "dashes=%s", tmpstr)) {
+            char      csv[64];
+            char     *pch;
+            float     dsh;
+
+            strcpy(csv, tmpstr);
+            int       count = 0;
+
+            pch = strtok(tmpstr, ",");
+            while (pch != NULL) {
+                pch = strtok(NULL, ",");
+                count++;
+            }
+            dprintf("- %d dash values found: ", count);
+            gdp->ndash = count;
+            if (gdp->p_dashes != NULL)
+                free(gdp->p_dashes);
+            gdp->p_dashes = (double *) malloc(sizeof(double) * count);
+            pch = strtok(csv, ",");
+            count = 0;
+            while (pch != NULL) {
+                if (sscanf(pch, "%f", &dsh)) {
+                    gdp->p_dashes[count] = (double) dsh;
+                    dprintf("%.1f ", gdp->p_dashes[count]);
+                    count++;
+                }
+                pch = strtok(NULL, ",");
+            }
+            dprintf("\n");
+            (*eaten) += j;
+            if (line[*eaten] == ':')
+                (*eaten) += 1;
+        }
+        if (line[*eaten] == '\0') {
+            dprintf("- done parsing line\n");
+            return 0;
+        }
+        // DASHED:dashes=n_on,n_off,n_on,n_off:dash-offset=offset
+        // allowing 16 characters for dash-offset=....
+        // => 4 characters for the offset value
+        j = scan_for_col(&line[*eaten], 16, tmpstr);
+        if (sscanf(tmpstr, "dash-offset=%lf", &gdp->offset)) {
+            dprintf("- found %s\n", tmpstr);
+            gdp->dash = 1;
+            (*eaten) += j;
+            if (line[*eaten] == ':')
+                (*eaten) += 1;
+        }
+        if (line[*eaten] == '\0') {
+            dprintf("- done parsing line\n");
+            return 0;
         }
     }
     if (line[*eaten] == '\0') {
@@ -975,6 +1095,10 @@ void rrd_graph_script(
             if (rrd_parse_shift(argv[i], &eaten, gdp, im))
                 return;
             break;
+        case GF_TEXTALIGN: /* left|right|center|justified */
+            if (rrd_parse_textalign(argv[i], &eaten, gdp))
+                return;
+            break;
         case GF_XPORT:
             if (rrd_parse_xport(argv[i], &eaten, gdp, im))
                 return;
@@ -989,9 +1113,6 @@ void rrd_graph_script(
             if (rrd_parse_legend(argv[i], &eaten, gdp))
                 return;
             break;
-#ifdef WITH_PIECHART
-        case GF_PART:  /* value[#color[:legend]] */
-#endif
         case GF_VRULE: /* value#color[:legend] */
         case GF_HRULE: /* value#color[:legend] */
         case GF_LINE:  /* vname-or-value[#color[:legend]][:STACK] */
@@ -1029,14 +1150,14 @@ void rrd_graph_script(
             break;
         }
         if (gdp->debug) {
-            dprintf("used %i out of %i chars\n", eaten, strlen(argv[i]));
+            dprintf("used %i out of %zi chars\n", eaten, strlen(argv[i]));
             dprintf("parsed line: '%s'\n", argv[i]);
             dprintf("remaining: '%s'\n", &argv[i][eaten]);
             if (eaten >= strlen(argv[i]))
                 dprintf("Command finished successfully\n");
         }
         if (eaten < strlen(argv[i])) {
-            rrd_set_error("Garbage '%s' after command:\n%s",
+            rrd_set_error("I don't understand '%s' in command: '%s'.",
                           &argv[i][eaten], argv[i]);
             return;
         }