In rrd_graph: the width/height values are written into rrd_infoval_t's u_cnt
[rrdtool.git] / src / rrd_graph.c
index a24889f..0de1aa8 100644 (file)
@@ -10,7 +10,9 @@
 
 #ifdef WIN32
 #include "strftime.h"
+#include "plbasename.h"
 #endif
+
 #include "rrd_tool.h"
 
 #if defined(WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
@@ -236,7 +238,7 @@ enum gf_en gf_conv(
     conv_if(XPORT, GF_XPORT);
     conv_if(SHIFT, GF_SHIFT);
 
-    return (-1);
+    return (enum gf_en)(-1);
 }
 
 enum gfx_if_en if_conv(
@@ -248,7 +250,7 @@ enum gfx_if_en if_conv(
     conv_if(EPS, IF_EPS);
     conv_if(PDF, IF_PDF);
 
-    return (-1);
+    return (enum gfx_if_en)(-1);
 }
 
 enum tmt_en tmt_conv(
@@ -262,7 +264,7 @@ enum tmt_en tmt_conv(
     conv_if(WEEK, TMT_WEEK);
     conv_if(MONTH, TMT_MONTH);
     conv_if(YEAR, TMT_YEAR);
-    return (-1);
+    return (enum tmt_en)(-1);
 }
 
 enum grc_en grc_conv(
@@ -280,7 +282,7 @@ enum grc_en grc_conv(
     conv_if(AXIS, GRC_AXIS);
     conv_if(FRAME, GRC_FRAME);
 
-    return -1;
+    return (enum grc_en)(-1);
 }
 
 enum text_prop_en text_prop_conv(
@@ -293,7 +295,7 @@ enum text_prop_en text_prop_conv(
     conv_if(UNIT, TEXT_PROP_UNIT);
     conv_if(LEGEND, TEXT_PROP_LEGEND);
     conv_if(WATERMARK, TEXT_PROP_WATERMARK);
-    return -1;
+    return (enum text_prop_en)(-1);
 }
 
 
@@ -303,7 +305,7 @@ int im_free(
     image_desc_t *im)
 {
     unsigned long i, ii;
-    cairo_status_t status = 0;
+    cairo_status_t status = (cairo_status_t) 0;
 
     if (im == NULL)
         return 0;
@@ -427,7 +429,7 @@ void si_unit(
 
     if (im->unitsexponent != 9999) {
         /* unitsexponent = 9, 6, 3, 0, -3, -6, -9, etc */
-        viewdigits = floor(im->unitsexponent / 3);
+        viewdigits = floor((double)(im->unitsexponent / 3));
     } else {
         viewdigits = digits;
     }
@@ -1053,7 +1055,7 @@ int data_calc(
                         /* add one entry to the array that keeps track of the step sizes of the
                          * data sources going into the CDEF. */
                         if ((steparray =
-                             rrd_realloc(steparray,
+                             (long*)rrd_realloc(steparray,
                                          (++stepcnt +
                                           1) * sizeof(*steparray))) == NULL) {
                             rrd_set_error("realloc steparray");
@@ -1121,7 +1123,7 @@ int data_calc(
              */
             im->gdes[gdi].step = lcd(steparray);
             free(steparray);
-            if ((im->gdes[gdi].data = malloc(((im->gdes[gdi].end -
+            if ((im->gdes[gdi].data = (rrd_value_t*)malloc(((im->gdes[gdi].end -
                                                im->gdes[gdi].start)
                                               / im->gdes[gdi].step)
                                              * sizeof(double))) == NULL) {
@@ -1211,7 +1213,7 @@ int data_proc(
     for (i = 0; i < im->gdes_c; i++) {
         if ((im->gdes[i].gf == GF_LINE) ||
             (im->gdes[i].gf == GF_AREA) || (im->gdes[i].gf == GF_TICK)) {
-            if ((im->gdes[i].p_data = malloc((im->xsize + 1)
+            if ((im->gdes[i].p_data = (rrd_value_t*)malloc((im->xsize + 1)
                                              * sizeof(rrd_value_t))) == NULL) {
                 rrd_set_error("malloc data_proc");
                 return -1;
@@ -1568,7 +1570,7 @@ int print_calc(
                 rrd_infoval_t prline;
 
                 if (im->gdes[i].strftm) {
-                    prline.u_str = malloc((FMT_LEG_LEN + 2) * sizeof(char));
+                    prline.u_str = (char*)malloc((FMT_LEG_LEN + 2) * sizeof(char));
                     strftime(prline.u_str,
                              FMT_LEG_LEN, im->gdes[i].format, &tmvdef);
                 } else if (bad_format(im->gdes[i].format)) {
@@ -1663,21 +1665,18 @@ int leg_place(
     int       leg_cc;
     double    glue = 0;
     int       i, ii, mark = 0;
-    char      prt_fctn; /*special printfunctions */
     char      default_txtalign = TXA_JUSTIFIED; /*default line orientation */
     int      *legspace;
     char     *tab;
 
     if (!(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH)) {
-        if ((legspace = malloc(im->gdes_c * sizeof(int))) == NULL) {
+        if ((legspace = (int*)malloc(im->gdes_c * sizeof(int))) == NULL) {
             rrd_set_error("malloc for legspace");
             return -1;
         }
 
-        if (im->extra_flags & FULL_SIZE_MODE)
-            leg_y = leg_y_prev =
-                leg_y - (int) (im->text_prop[TEXT_PROP_LEGEND].size * 1.8);
         for (i = 0; i < im->gdes_c; i++) {
+            char      prt_fctn; /*special printfunctions */
             fill_last = fill;
             /* hide legends for rules which are not displayed */
             if (im->gdes[i].gf == GF_TEXTALIGN) {
@@ -1701,7 +1700,7 @@ int leg_place(
                 tab[0] = (char) 9;
             }
             leg_cc = strlen(im->gdes[i].legend);
-            /* is there a controle code ant the end of the legend string ? */
+            /* is there a controle code at the end of the legend string ? */
             if (leg_cc >= 2 && im->gdes[i].legend[leg_cc - 2] == '\\') {
                 prt_fctn = im->gdes[i].legend[leg_cc - 1];
                 leg_cc -= 2;
@@ -1817,18 +1816,10 @@ int leg_place(
                         + glue;
                 }
                 leg_y_prev = leg_y;
-                if (im->extra_flags & FULL_SIZE_MODE) {
-                    /* only add y space if there was text on the line */
-                    if (leg_x > border || prt_fctn == 's')
-                        leg_y -= im->text_prop[TEXT_PROP_LEGEND].size * 1.8;
-                    if (prt_fctn == 's')
-                        leg_y += im->text_prop[TEXT_PROP_LEGEND].size;
-                } else {
-                    if (leg_x > border || prt_fctn == 's')
-                        leg_y += im->text_prop[TEXT_PROP_LEGEND].size * 1.8;
-                    if (prt_fctn == 's')
-                        leg_y -= im->text_prop[TEXT_PROP_LEGEND].size;
-                }
+                if (leg_x > border || prt_fctn == 's')
+                    leg_y += im->text_prop[TEXT_PROP_LEGEND].size * 1.8;
+                if (prt_fctn == 's')
+                    leg_y -= im->text_prop[TEXT_PROP_LEGEND].size;
                 fill = 0;
                 leg_c = 0;
                 mark = ii;
@@ -1836,11 +1827,15 @@ int leg_place(
         }
 
         if (im->extra_flags & FULL_SIZE_MODE) {
-            if (leg_y != leg_y_prev) {
-                *gY = leg_y - im->text_prop[TEXT_PROP_LEGEND].size * 1.8;
-                im->yorigin =
-                    leg_y - im->text_prop[TEXT_PROP_LEGEND].size * 1.8;
+            /* now for some backpaddeling. We have to shift up all the
+               legend items into the graph and tell the caller about the
+               space we used up. */
+            long shift_up = leg_y - im->yimg - im->text_prop[TEXT_PROP_LEGEND].size * 1.8 + border * 0.7;
+            for (i = 0; i < im->gdes_c; i++) {
+                im->gdes[i].leg_y -= shift_up;
             }
+            im->yorigin = im->yorigin - leg_y + im->yimg - im->text_prop[TEXT_PROP_LEGEND].size * 1.8 - border;            
+            *gY = im->yorigin;
         } else {
             im->yimg =
                 leg_y - im->text_prop[TEXT_PROP_LEGEND].size * 1.8 +
@@ -1968,7 +1963,9 @@ int draw_horizontal_grid(
     int       sgrid = (int) (im->minval / im->ygrid_scale.gridstep - 1);
     int       egrid = (int) (im->maxval / im->ygrid_scale.gridstep + 1);
     double    MaxY;
-
+    double second_axis_magfact = 0;
+    char *second_axis_symb = "";
+    
     scaledstep =
         im->ygrid_scale.gridstep /
         (double) im->magfact * (double) im->viewfactor;
@@ -2019,6 +2016,33 @@ int draw_horizontal_grid(
                     }
                 }
                 nlabels++;
+                if (im->second_axis_scale != 0){
+                        char graph_label_right[100];
+                        double sval = im->ygrid_scale.gridstep*(double)i*im->second_axis_scale+im->second_axis_shift;
+                        if (im->second_axis_format[0] == '\0'){
+                            if (!second_axis_magfact){
+                                double dummy = im->ygrid_scale.gridstep*(double)(sgrid+egrid)/2.0*im->second_axis_scale+im->second_axis_shift;
+                                auto_scale(im,&dummy,&second_axis_symb,&second_axis_magfact);
+                            }
+                            sval /= second_axis_magfact;
+                            if(MaxY < 10) { 
+                                sprintf(graph_label_right,"%5.1f %s",sval,second_axis_symb);
+                            } else {
+                                sprintf(graph_label_right,"%5.0f %s",sval,second_axis_symb);
+                            }
+                        }
+                        else {
+                           sprintf(graph_label_right,im->second_axis_format,sval);
+                        }        
+                        gfx_text ( im,
+                               X1+7, Y0,
+                               im->graph_col[GRC_FONT],
+                               im->text_prop[TEXT_PROP_AXIS].font_desc,
+                               im->tabwidth,0.0, GFX_H_LEFT, GFX_V_CENTER,
+                               graph_label_right );
+                }
                 gfx_text(im,
                          X0 -
                          im->
@@ -2070,7 +2094,7 @@ double frexp10(
     double    mnt;
     int       iexp;
 
-    iexp = floor(log(fabs(x)) / log(10));
+    iexp = floor(log((double)fabs(x)) / log((double)10));
     mnt = x / pow(10.0, iexp);
     if (mnt >= 10.0) {
         iexp++;
@@ -2196,8 +2220,35 @@ int horizontal_log_grid(
             else
                 symbol = '?';
             sprintf(graph_label, "%3.0f %c", pvalue, symbol);
-        } else
+        } else {            
             sprintf(graph_label, "%3.0e", value);
+        }
+        if (im->second_axis_scale != 0){
+                char graph_label_right[100];
+                double sval = value*im->second_axis_scale+im->second_axis_shift;
+                if (im->second_axis_format[0] == '\0'){
+                        if (im->extra_flags & FORCE_UNITS_SI) {
+                                double mfac = 1;
+                                char   *symb = "";
+                                auto_scale(im,&sval,&symb,&mfac);
+                                sprintf(graph_label_right,"%4.0f %s", sval,symb);
+                        }
+                        else {        
+                                sprintf(graph_label_right,"%3.0e", sval);
+                        }
+                }
+                else {
+                      sprintf(graph_label_right,im->second_axis_format,sval);
+                }    
+    
+                gfx_text ( im,
+                               X1+7, Y0,
+                               im->graph_col[GRC_FONT],
+                               im->text_prop[TEXT_PROP_AXIS].font_desc,
+                               im->tabwidth,0.0, GFX_H_LEFT, GFX_V_CENTER,
+                               graph_label_right );
+        }
+      
         gfx_text(im,
                  X0 -
                  im->
@@ -2511,6 +2562,18 @@ void axis_paint(
     gfx_new_area(im, im->xorigin - 3, im->yorigin - im->ysize - 2, im->xorigin + 3, im->yorigin - im->ysize - 2, im->xorigin, im->yorigin - im->ysize - 7,  /* vertical */
                  im->graph_col[GRC_ARROW]);
     gfx_close_path(im);
+    if (im->second_axis_scale != 0){
+       gfx_line ( im, im->xorigin+im->xsize,im->yorigin+4,
+                         im->xorigin+im->xsize,im->yorigin-im->ysize-4,
+                         MGRIDWIDTH, im->graph_col[GRC_AXIS]);
+       gfx_new_area ( im, 
+                   im->xorigin+im->xsize-2,  im->yorigin-im->ysize-2,
+                   im->xorigin+im->xsize+3,  im->yorigin-im->ysize-2,
+                   im->xorigin+im->xsize,    im->yorigin-im->ysize-7, /* LINEOFFSET */
+                   im->graph_col[GRC_ARROW]);
+       gfx_close_path(im);
+    }
+
 }
 
 void grid_paint(
@@ -2561,16 +2624,34 @@ void grid_paint(
     }
 
     /* yaxis unit description */
-    gfx_text(im,
-             10,
-             (im->yorigin -
-              im->ysize / 2),
-             im->graph_col[GRC_FONT],
-             im->
-             text_prop[TEXT_PROP_UNIT].
-             font_desc,
-             im->tabwidth,
-             RRDGRAPH_YLEGEND_ANGLE, GFX_H_CENTER, GFX_V_CENTER, im->ylegend);
+    if (im->ylegend[0] != '\0'){
+        gfx_text(im,
+                 10,
+                 (im->yorigin -
+                  im->ysize / 2),
+                 im->graph_col[GRC_FONT],
+                 im->
+                 text_prop[TEXT_PROP_UNIT].
+                 font_desc,
+                 im->tabwidth,
+                 RRDGRAPH_YLEGEND_ANGLE, GFX_H_CENTER, GFX_V_CENTER, im->ylegend);
+    }
+    if (im->second_axis_legend[0] != '\0'){
+            double Xylabel=gfx_get_text_width(im, 0,
+                        im->text_prop[TEXT_PROP_AXIS].font_desc,
+                        im->tabwidth,
+                        "0") * im->unitslength
+                    + im->text_prop[TEXT_PROP_UNIT].size *2;
+            gfx_text( im,
+                  im->xorigin+im->xsize+Xylabel+8, (im->yorigin - im->ysize/2),
+                  im->graph_col[GRC_FONT],
+                  im->text_prop[TEXT_PROP_UNIT].font_desc,
+                  im->tabwidth, 
+                  RRDGRAPH_YLEGEND_ANGLE,
+                  GFX_H_CENTER, GFX_V_CENTER,
+                  im->second_axis_legend);
+    }        
     /* graph title */
     gfx_text(im,
              im->ximg / 2, 6,
@@ -2580,14 +2661,16 @@ void grid_paint(
              font_desc,
              im->tabwidth, 0.0, GFX_H_CENTER, GFX_V_TOP, im->title);
     /* rrdtool 'logo' */
-    water_color = im->graph_col[GRC_FONT];
-    water_color.alpha = 0.3;
-    gfx_text(im, im->ximg - 4, 5,
-             water_color,
-             im->
-             text_prop[TEXT_PROP_WATERMARK].
-             font_desc, im->tabwidth,
-             -90, GFX_H_LEFT, GFX_V_TOP, "RRDTOOL / TOBI OETIKER");
+    if (!(im->extra_flags & NO_RRDTOOL_TAG)){
+        water_color = im->graph_col[GRC_FONT];
+        water_color.alpha = 0.3;
+        gfx_text(im, im->ximg - 4, 5,
+                 water_color,
+                 im->
+                 text_prop[TEXT_PROP_WATERMARK].
+                 font_desc, im->tabwidth,
+                 -90, GFX_H_LEFT, GFX_V_TOP, "RRDTOOL / TOBI OETIKER");
+    }    
     /* graph watermark */
     if (im->watermark[0] != '\0') {
         gfx_text(im,
@@ -2739,24 +2822,24 @@ int graph_size_location(
         return 0;
     }
 
-    /** +---+--------------------------------------------+
-     ** | y |...............graph title..................|
-     ** |   +---+-------------------------------+--------+
-     ** | a | y |                               |        |
-     ** | x |   |                               |        |
-     ** | i | a |                               |  pie   |
-     ** | s | x |       main graph area         | chart  |
-     ** |   | i |                               |  area  |
-     ** | t | s |                               |        |
-     ** | i |   |                               |        |
-     ** | t | l |                               |        |
-     ** | l | b +-------------------------------+--------+
-     ** | e | l |       x axis labels           |        |
-     ** +---+---+-------------------------------+--------+
-     ** |....................legends.....................|
-     ** +------------------------------------------------+
-     ** |                   watermark                    |
-     ** +------------------------------------------------+
+    /** +---+-----------------------------------+
+     ** | y |...............graph title.........|
+     ** |   +---+-------------------------------+
+     ** | a | y |                               |
+     ** | x |   |                               |
+     ** | i | a |                               |    
+     ** | s | x |       main graph area         |
+     ** |   | i |                               |
+     ** | t | s |                               |
+     ** | i |   |                               |
+     ** | t | l |                               |
+     ** | l | b +-------------------------------+
+     ** | e | l |       x axis labels           |
+     ** +---+---+-------------------------------+
+     ** |....................legends............|
+     ** +---------------------------------------+
+     ** |                   watermark           |
+     ** +---------------------------------------+
      */
 
     if (im->ylegend[0] != '\0') {
@@ -2790,7 +2873,7 @@ int graph_size_location(
     if (im->extra_flags & FULL_SIZE_MODE) {
         /* The actual size of the image to draw has been determined by the user.
          ** The graph area is the space remaining after accounting for the legend,
-         ** the watermark, the pie chart, the axis labels, and the title.
+         ** the watermark, the axis labels, and the title.
          */
         im->xorigin = 0;
         im->ximg = im->xsize;
@@ -2798,36 +2881,45 @@ int graph_size_location(
         im->yorigin = im->ysize;
         Xmain = im->ximg;
         Ymain = im->yimg;
-        im->yorigin += Ytitle;
         /* Now calculate the total size.  Insert some spacing where
            desired.  im->xorigin and im->yorigin need to correspond
            with the lower left corner of the main graph area or, if
            this one is not set, the imaginary box surrounding the
            pie chart area. */
         /* Initial size calculation for the main graph area */
-        Xmain = im->ximg - (Xylabel + 2 * Xspacing);
-        if (Xmain)
-            Xmain -= Xspacing;  /* put space between main graph area and right edge */
+        Xmain = im->ximg - Xylabel - 3 * Xspacing;
+
         im->xorigin = Xspacing + Xylabel;
-        /* the length of the title should not influence with width of the graph
-           if (Xtitle > im->ximg) im->ximg = Xtitle; */
+
         if (Xvertical) {    /* unit description */
             Xmain -= Xvertical;
             im->xorigin += Xvertical;
         }
+
+        /* adjust space for second axis */
+        if (im->second_axis_scale != 0){
+            Xmain -= Xylabel + Xspacing;
+        }
+        if (im->extra_flags & NO_RRDTOOL_TAG){
+            Xmain += Xspacing;
+        }
+        if (im->second_axis_legend[0] != '\0' ) {
+            Xmain -= im->text_prop[TEXT_PROP_UNIT].size * 1.5;
+        }
+
         im->xsize = Xmain;
+
         xtr(im, 0);
         /* The vertical size of the image is known in advance.  The main graph area
          ** (Ymain) and im->yorigin must be set according to the space requirements
          ** of the legend and the axis labels.
          */
         if (im->extra_flags & NOLEGEND) {
-            /* set dimensions correctly if using full size mode with no legend */
-            im->yorigin =
-                im->yimg -
+            im->yorigin = im->yimg -
                 im->text_prop[TEXT_PROP_AXIS].size * 2.5 - Yspacing;
             Ymain = im->yorigin;
-        } else {
+        }
+        else {            
             /* Determine where to place the legends onto the image.
              ** Set Ymain and adjust im->yorigin to match the space requirements.
              */
@@ -2869,8 +2961,7 @@ int graph_size_location(
              */
             /* don't care for the with of the title
                Xtitle = gfx_get_text_width(im->canvas, 0,
-               im->text_prop[TEXT_PROP_TITLE].font,
-               im->text_prop[TEXT_PROP_TITLE].size,
+               im->text_prop[TEXT_PROP_TITLE].font_desc,
                im->tabwidth,
                im->title, 0) + 2*Xspacing; */
             Ytitle = im->text_prop[TEXT_PROP_TITLE].size * 2.6 + 10;
@@ -2892,6 +2983,14 @@ int graph_size_location(
          ** size already allocated.
          */
         im->ximg = Xylabel + Xmain + 2 * Xspacing;
+
+        if (im->second_axis_scale != 0){
+            im->ximg += Xylabel + Xspacing;
+        }
+        if (im->extra_flags & NO_RRDTOOL_TAG){
+            im->ximg -= Xspacing;
+        }
+        
         if (Xmain)
             im->ximg += Xspacing;
         im->xorigin = Xspacing + Xylabel;
@@ -2901,6 +3000,10 @@ int graph_size_location(
             im->ximg += Xvertical;
             im->xorigin += Xvertical;
         }
+        if (im->second_axis_legend[0] != '\0' ) {
+            im->ximg += Xvertical;
+        }
+      
         xtr(im, 0);
         /* The vertical size is interesting... we need to compare
          ** the sum of {Ytitle, Ymain, Yxlabel, Ylegend, Ywatermark} with 
@@ -2944,10 +3047,10 @@ static cairo_status_t cairo_output(
     *data,
     unsigned int length)
 {
-    image_desc_t *im = closure;
+    image_desc_t *im = (image_desc_t*)closure;
 
     im->rendered_image =
-        realloc(im->rendered_image, im->rendered_image_size + length);
+        (unsigned char*)realloc(im->rendered_image, im->rendered_image_size + length);
     if (im->rendered_image == NULL)
         return CAIRO_STATUS_WRITE_ERROR;
     memcpy(im->rendered_image + im->rendered_image_size, data, length);
@@ -3122,7 +3225,7 @@ int graph_paint(
                     if (im->gdes[i].yrule > 0) {
                         gfx_line(im,
                                  im->xorigin + ii,
-                                 im->yorigin,
+                                 im->yorigin + 1.0,
                                  im->xorigin + ii,
                                  im->yorigin -
                                  im->gdes[i].yrule *
@@ -3130,11 +3233,11 @@ int graph_paint(
                     } else if (im->gdes[i].yrule < 0) {
                         gfx_line(im,
                                  im->xorigin + ii,
-                                 im->yorigin - im->ysize,
+                                 im->yorigin - im->ysize - 1.0,
                                  im->xorigin + ii,
-                                 im->yorigin - (1 -
+                                 im->yorigin - im->ysize -
                                                 im->gdes[i].
-                                                yrule) *
+                                                yrule *
                                  im->ysize, 1.0, im->gdes[i].col);
                     }
                 }
@@ -3566,13 +3669,13 @@ int rrd_graph(
         if (strcmp(walker->key, "image_info") == 0) {
             prlines++;
             if (((*prdata) =
-                 rrd_realloc((*prdata),
+                 (char**)rrd_realloc((*prdata),
                              (prlines + 1) * sizeof(char *))) == NULL) {
                 rrd_set_error("realloc prdata");
                 return 0;
             }
             /* imginfo goes to position 0 in the prdata array */
-            (*prdata)[prlines - 1] = malloc((strlen(walker->value.u_str)
+            (*prdata)[prlines - 1] = (char*)malloc((strlen(walker->value.u_str)
                                              + 2) * sizeof(char));
             strcpy((*prdata)[prlines - 1], walker->value.u_str);
             (*prdata)[prlines] = NULL;
@@ -3587,9 +3690,9 @@ int rrd_graph(
     *ymax = 0;
     while (walker) {
         if (strcmp(walker->key, "image_width") == 0) {
-            *xsize = walker->value.u_int;
+            *xsize = walker->value.u_cnt;
         } else if (strcmp(walker->key, "image_height") == 0) {
-            *ysize = walker->value.u_int;
+            *ysize = walker->value.u_cnt;
         } else if (strcmp(walker->key, "value_min") == 0) {
             *ymin = walker->value.u_val;
         } else if (strcmp(walker->key, "value_max") == 0) {
@@ -3597,12 +3700,12 @@ int rrd_graph(
         } else if (strncmp(walker->key, "print", 5) == 0) { /* keys are prdate[0..] */
             prlines++;
             if (((*prdata) =
-                 rrd_realloc((*prdata),
+                 (char**)rrd_realloc((*prdata),
                              (prlines + 1) * sizeof(char *))) == NULL) {
                 rrd_set_error("realloc prdata");
                 return 0;
             }
-            (*prdata)[prlines - 1] = malloc((strlen(walker->value.u_str)
+            (*prdata)[prlines - 1] = (char*)malloc((strlen(walker->value.u_str)
                                              + 2) * sizeof(char));
             (*prdata)[prlines] = NULL;
             strcpy((*prdata)[prlines - 1], walker->value.u_str);
@@ -3786,6 +3889,10 @@ void rrd_graph_init(
     im->ygridstep = DNAN;
     im->yimg = 0;
     im->ylegend[0] = '\0';
+    im->second_axis_scale = 0; /* 0 disables it */
+    im->second_axis_shift = 0; /* no shift by default */
+    im->second_axis_legend[0] = '\0';
+    im->second_axis_format[0] = '\0'; 
     im->yorigin = 0;
     im->ysize = 100;
     im->zoom = 1;
@@ -3874,6 +3981,10 @@ void rrd_graph_options(
         { "force-rules-legend", no_argument,       0, 'F'},
         { "only-graph",         no_argument,       0, 'j'},
         { "alt-y-grid",         no_argument,       0, 'Y'},
+        {"disable-rrdtool-tag", no_argument,       0,  1001},
+        {"right-axis",          required_argument, 0,  1002},
+        {"right-axis-label",    required_argument, 0,  1003},
+        {"right-axis-format",   required_argument, 0,  1004},     
         { "no-minor",           no_argument,       0, 'I'}, 
         { "slope-mode",         no_argument,       0, 'E'},
         { "alt-autoscale",      no_argument,       0, 'A'},
@@ -3935,6 +4046,9 @@ void rrd_graph_options(
         case 'F':
             im->extra_flags |= FORCE_RULES_LEGEND;
             break;
+        case 1001:
+            im->extra_flags |= NO_RRDTOOL_TAG;
+            break;              
         case LONGOPT_UNITS_SI:
             if (im->extra_flags & FORCE_UNITS) {
                 rrd_set_error("--units can only be used once!");
@@ -4043,6 +4157,33 @@ void rrd_graph_options(
                 return;
             }
             break;
+        case 1002: /* right y axis */
+
+            if(sscanf(optarg,
+                      "%lf:%lf",
+                      &im->second_axis_scale,
+                      &im->second_axis_shift) == 2) {
+                if(im->second_axis_scale==0){
+                    rrd_set_error("the second_axis_scale  must not be 0");
+                    return;
+                }
+            } else {
+                rrd_set_error("invalid right-axis format expected scale:shift");
+                return;
+            }
+            break;
+        case 1003:
+            strncpy(im->second_axis_legend,optarg,150);
+            im->second_axis_legend[150]='\0';
+            break;
+        case 1004:
+            if (bad_format(optarg)){
+                rrd_set_error("use either %le or %lf formats");
+                return;
+            }
+            strncpy(im->second_axis_format,optarg,150);
+            im->second_axis_format[150]='\0';
+            break;
         case 'v':
             strncpy(im->ylegend, optarg, 150);
             im->ylegend[150] = '\0';
@@ -4445,6 +4586,8 @@ int vdef_parse(
     }
     if (!strcmp("PERCENT", func))
         gdes->vf.op = VDEF_PERCENT;
+    else if (!strcmp("PERCENTNAN", func))
+        gdes->vf.op = VDEF_PERCENTNAN;
     else if (!strcmp("MAXIMUM", func))
         gdes->vf.op = VDEF_MAXIMUM;
     else if (!strcmp("AVERAGE", func))
@@ -4472,6 +4615,7 @@ int vdef_parse(
     };
     switch (gdes->vf.op) {
     case VDEF_PERCENT:
+    case VDEF_PERCENTNAN:
         if (isnan(param)) { /* no parameter given */
             rrd_set_error
                 ("Function '%s' needs parameter in VDEF '%s'\n",
@@ -4537,7 +4681,7 @@ int vdef_calc(
     case VDEF_PERCENT:{
         rrd_value_t *array;
         int       field;
-        if ((array = malloc(steps * sizeof(double))) == NULL) {
+        if ((array = (rrd_value_t*)malloc(steps * sizeof(double))) == NULL) {
             rrd_set_error("malloc VDEV_PERCENT");
             return -1;
         }
@@ -4556,6 +4700,34 @@ int vdef_calc(
 #endif
     }
         break;
+    case VDEF_PERCENTNAN:{
+        rrd_value_t *array;
+        int       field;
+       /* count number of "valid" values */
+       int nancount=0;
+       for (step = 0; step < steps; step++) {
+         if (!isnan(data[step * src->ds_cnt])) { nancount++; }
+       }
+       /* and allocate it */
+        if ((array = (rrd_value_t*)malloc(nancount * sizeof(double))) == NULL) {
+            rrd_set_error("malloc VDEV_PERCENT");
+            return -1;
+        }
+       /* and fill it in */
+       field=0;
+        for (step = 0; step < steps; step++) {
+           if (!isnan(data[step * src->ds_cnt])) {
+                array[field] = data[step * src->ds_cnt];
+               field++;
+            }
+        }
+        qsort(array, nancount, sizeof(double), vdef_percent_compar);
+        field = (nancount - 1) * dst->vf.param / 100;
+        dst->vf.val = array[field];
+        dst->vf.when = 0;   /* no time component */
+        free(array);
+    }
+        break;
     case VDEF_MAXIMUM:
         step = 0;
         while (step != steps && isnan(data[step * src->ds_cnt]))