use _NL_TIME_WEEK_1STDAY to determin the first day of the week.
[rrdtool.git] / src / rrd_graph.c
index 0de1aa8..390a66e 100644 (file)
@@ -1,31 +1,36 @@
 /****************************************************************************
- * RRDtool 1.3.2  Copyright by Tobi Oetiker, 1997-2008
+ * RRDtool 1.4.2  Copyright by Tobi Oetiker, 1997-2009
  ****************************************************************************
  * rrd__graph.c  produce graphs from data in rrdfiles
  ****************************************************************************/
 
 
 #include <sys/stat.h>
-#include <libgen.h>
 
 #ifdef WIN32
 #include "strftime.h"
-#include "plbasename.h"
 #endif
 
 #include "rrd_tool.h"
 
+/* for basename */
+#ifdef HAVE_LIBGEN_H
+#  include <libgen.h>
+#else
+#include "plbasename.h"
+#endif
+
 #if defined(WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
 #include <io.h>
 #include <fcntl.h>
 #endif
 
-#ifdef HAVE_TIME_H
 #include <time.h>
-#endif
 
-#ifdef HAVE_LOCALE_H
 #include <locale.h>
+
+#ifdef HAVE_LANGINFO_H
+#include <langinfo.h>
 #endif
 
 #include "rrd_graph.h"
@@ -51,7 +56,7 @@ text_prop_t text_prop[] = {
     ,                   /* unit */
     {8.0, RRD_DEFAULT_FONT,NULL} /* legend */
     ,
-    {5.5, RRD_DEFAULT_FONT,NULL} /* watermark */    
+    {5.5, RRD_DEFAULT_FONT,NULL} /* watermark */
 };
 
 xlab_t    xlab[] = {
@@ -352,7 +357,7 @@ int im_free(
     if (status)
         fprintf(stderr, "OOPS: Cairo has issues it can't even die: %s\n",
                 cairo_status_to_string(status));
-        
+
     return 0;
 }
 
@@ -576,8 +581,8 @@ void apply_gridfit(
             ytr(im, DNAN);  /* reset precalc */
             log10_range = log10(im->maxval) - log10(im->minval);
         }
-        /* make sure first y=10^x gridline is located on 
-           integer pixel position by moving scale slightly 
+        /* make sure first y=10^x gridline is located on
+           integer pixel position by moving scale slightly
            downwards (sub-pixel movement) */
         ypix = ytr(im, ya) + im->ysize; /* add im->ysize so it always is positive */
         ypixfrac = ypix - floor(ypix);
@@ -797,7 +802,7 @@ void reduce_data(
 }
 
 
-/* get the data required for the graphs from the 
+/* get the data required for the graphs from the
    relevant rrds ... */
 
 int data_fetch(
@@ -914,7 +919,7 @@ int data_fetch(
 /* evaluate the expressions in the CDEF functions */
 
 /*************************************************************
- * CDEF stuff 
+ * CDEF stuff
  *************************************************************/
 
 long find_var_wrapper(
@@ -1202,7 +1207,7 @@ int data_proc(
 {
     long      i, ii;
     double    pixstep = (double) (im->end - im->start)
-        / (double) im->xsize;   /* how much time 
+        / (double) im->xsize;   /* how much time
                                    passes in one pixel */
     double    paintval;
     double    minval = DNAN, maxval = DNAN;
@@ -1247,7 +1252,7 @@ int data_proc(
                     } else
                         if (((long int) gr_time >=
                              (long int) im->gdes[vidx].start)
-                            && ((long int) gr_time <=
+                            && ((long int) gr_time <
                                 (long int) im->gdes[vidx].end)) {
                         value = im->gdes[vidx].data[(unsigned long)
                                                     floor((double)
@@ -1300,7 +1305,7 @@ int data_proc(
             minval = 0.0;   /* catching this right away below */
             maxval = 5.1;
         }
-        /* in logarithm mode, where minval is smaller or equal 
+        /* in logarithm mode, where minval is smaller or equal
            to 0 make the beast just way smaller than maxval */
         if (minval <= 0) {
             minval = maxval / 10e8;
@@ -1355,6 +1360,21 @@ int data_proc(
 }
 
 
+static int find_first_weekday(void){
+    static int first_weekday = -1;
+    if (first_weekday == -1){
+#if defined(HAVE_NL_LANGINFO)
+        /* according to http://sourceware.org/ml/libc-locales/2009-q1/msg00011.html */
+        long week_1stday_l = (long) nl_langinfo (_NL_TIME_WEEK_1STDAY);
+        if (week_1stday_l == 19971130) first_weekday = 0; /* Sun */
+        else if (week_1stday_l == 19971201) first_weekday = 1; /* Mon */
+        else first_weekday = 1; /* we go for a monday default */
+#else
+        first_weekday = 1;
+#endif
+    }
+    return first_weekday;
+}
 
 /* identify the point where the first gridline, label ... gets placed */
 
@@ -1396,10 +1416,10 @@ time_t find_first_time(
         tm.       tm_sec = 0;
         tm.       tm_min = 0;
         tm.       tm_hour = 0;
-        tm.       tm_mday -= tm.tm_wday - 1;    /* -1 because we want the monday */
+        tm.       tm_mday -= tm.tm_wday - find_first_weekday();
 
-        if (tm.tm_wday == 0)
-            tm.       tm_mday -= 7; /* we want the *previous* monday */
+        if (tm.tm_wday == 0 && find_first_weekday() > 0)
+            tm.       tm_mday -= 7; /* we want the *previous* week */
 
         break;
     case TMT_MONTH:
@@ -1649,27 +1669,38 @@ int print_calc(
 }
 
 
+
 /* place legends with color spots */
 int leg_place(
     image_desc_t *im,
-    int *gY)
+    int calc_width)
 {
     /* graph labels */
     int       interleg = im->text_prop[TEXT_PROP_LEGEND].size * 2.0;
     int       border = im->text_prop[TEXT_PROP_LEGEND].size * 2.0;
     int       fill = 0, fill_last;
+    double    legendwidth; // = im->ximg - 2 * border;
     int       leg_c = 0;
     double    leg_x = border;
-    int       leg_y = im->yimg;
-    int       leg_y_prev = im->yimg;
+    int       leg_y = 0; //im->yimg;
+    int       leg_y_prev = 0; // im->yimg;
     int       leg_cc;
     double    glue = 0;
     int       i, ii, mark = 0;
     char      default_txtalign = TXA_JUSTIFIED; /*default line orientation */
     int      *legspace;
     char     *tab;
+    char      saved_legend[FMT_LEG_LEN + 5];
 
-    if (!(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH)) {
+    if(calc_width){
+        legendwidth = 0;
+    }
+    else{
+        legendwidth = im->legendwidth - 2 * border;
+    }
+
+
+    if (!(im->extra_flags & NOLEGEND) && !(im->extra_flags & ONLY_GRAPH)) {
         if ((legspace = (int*)malloc(im->gdes_c * sizeof(int))) == NULL) {
             rrd_set_error("malloc for legspace");
             return -1;
@@ -1677,6 +1708,10 @@ int leg_place(
 
         for (i = 0; i < im->gdes_c; i++) {
             char      prt_fctn; /*special printfunctions */
+            if(calc_width){
+                strcpy(saved_legend, im->gdes[i].legend);
+            }
+
             fill_last = fill;
             /* hide legends for rules which are not displayed */
             if (im->gdes[i].gf == GF_TEXTALIGN) {
@@ -1699,6 +1734,7 @@ int leg_place(
                 memmove(tab, tab + 1, strlen(tab));
                 tab[0] = (char) 9;
             }
+
             leg_cc = strlen(im->gdes[i].legend);
             /* is there a controle code at the end of the legend string ? */
             if (leg_cc >= 2 && im->gdes[i].legend[leg_cc - 2] == '\\') {
@@ -1713,6 +1749,7 @@ int leg_place(
                 prt_fctn != 'r' &&
                 prt_fctn != 'j' &&
                 prt_fctn != 'c' &&
+                prt_fctn != 'u' &&
                 prt_fctn != 's' && prt_fctn != '\0' && prt_fctn != 'g') {
                 free(legspace);
                 rrd_set_error
@@ -1757,7 +1794,10 @@ int leg_place(
             }
 
             if (prt_fctn == '\0') {
-                if (i == im->gdes_c - 1 || fill > im->ximg - 2 * border) {
+                if(calc_width && (fill > legendwidth)){
+                    legendwidth = fill;
+                }
+                if (i == im->gdes_c - 1 || fill > legendwidth) {
                     /* just one legend item is left right or center */
                     switch (default_txtalign) {
                     case TXA_RIGHT:
@@ -1775,7 +1815,7 @@ int leg_place(
                     }
                 }
                 /* is it time to place the legends ? */
-                if (fill > im->ximg - 2 * border) {
+                if (fill > legendwidth) {
                     if (leg_c > 1) {
                         /* go back one */
                         i--;
@@ -1788,23 +1828,22 @@ int leg_place(
                 }
             }
 
-
             if (prt_fctn != '\0') {
                 leg_x = border;
                 if (leg_c >= 2 && prt_fctn == 'j') {
-                    glue = (double)(im->ximg - fill - 2 * border) / (double)(leg_c - 1);
+                    glue = (double)(legendwidth - fill) / (double)(leg_c - 1);
                 } else {
                     glue = 0;
                 }
                 if (prt_fctn == 'c')
-                    leg_x = (double)(im->ximg - fill) / 2.0;
+                    leg_x = (double)(legendwidth - fill) / 2.0;
                 if (prt_fctn == 'r')
-                    leg_x = im->ximg - fill - border;
+                    leg_x = legendwidth - fill + border;
                 for (ii = mark; ii <= i; ii++) {
                     if (im->gdes[ii].legend[0] == '\0')
                         continue;   /* skip empty legends */
                     im->gdes[ii].leg_x = leg_x;
-                    im->gdes[ii].leg_y = leg_y;
+                    im->gdes[ii].leg_y = leg_y + border;
                     leg_x +=
                         (double)gfx_get_text_width(im, leg_x,
                                            im->
@@ -1820,26 +1859,27 @@ int leg_place(
                     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 (prt_fctn == 'u')
+                    leg_y -= im->text_prop[TEXT_PROP_LEGEND].size *1.8;
+
+                if(calc_width && (fill > legendwidth)){
+                    legendwidth = fill;
+                }
                 fill = 0;
                 leg_c = 0;
                 mark = ii;
             }
-        }
 
-        if (im->extra_flags & FULL_SIZE_MODE) {
-            /* 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;
+            if(calc_width){
+                strcpy(im->gdes[i].legend, saved_legend);
             }
-            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 +
-                border * 0.6;
+        }
+
+        if(calc_width){
+            im->legendwidth = legendwidth + 2 * border;
+        }
+        else{
+            im->legendheight = leg_y + border * 0.6;
         }
         free(legspace);
     }
@@ -1965,7 +2005,7 @@ int draw_horizontal_grid(
     double    MaxY;
     double second_axis_magfact = 0;
     char *second_axis_symb = "";
-    
+
     scaledstep =
         im->ygrid_scale.gridstep /
         (double) im->magfact * (double) im->viewfactor;
@@ -2025,8 +2065,8 @@ int draw_horizontal_grid(
                                 auto_scale(im,&dummy,&second_axis_symb,&second_axis_magfact);
                             }
                             sval /= second_axis_magfact;
-                            if(MaxY < 10) { 
+
+                            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);
@@ -2034,7 +2074,7 @@ int draw_horizontal_grid(
                         }
                         else {
                            sprintf(graph_label_right,im->second_axis_format,sval);
-                        }        
+                        }
                         gfx_text ( im,
                                X1+7, Y0,
                                im->graph_col[GRC_FONT],
@@ -2042,7 +2082,7 @@ int draw_horizontal_grid(
                                im->tabwidth,0.0, GFX_H_LEFT, GFX_V_CENTER,
                                graph_label_right );
                 }
+
                 gfx_text(im,
                          X0 -
                          im->
@@ -2220,7 +2260,7 @@ 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){
@@ -2233,14 +2273,14 @@ int horizontal_log_grid(
                                 auto_scale(im,&sval,&symb,&mfac);
                                 sprintf(graph_label_right,"%4.0f %s", sval,symb);
                         }
-                        else {        
+                        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],
@@ -2248,7 +2288,7 @@ int horizontal_log_grid(
                                im->tabwidth,0.0, GFX_H_LEFT, GFX_V_CENTER,
                                graph_label_right );
         }
-      
+
         gfx_text(im,
                  X0 -
                  im->
@@ -2566,7 +2606,7 @@ void axis_paint(
        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, 
+       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 */
@@ -2584,20 +2624,23 @@ void grid_paint(
     double    X0, Y0;   /* points for filled graph and more */
     struct gfx_color_t water_color;
 
-    /* draw 3d border */
-    gfx_new_area(im, 0, im->yimg,
-                 2, im->yimg - 2, 2, 2, im->graph_col[GRC_SHADEA]);
-    gfx_add_point(im, im->ximg - 2, 2);
-    gfx_add_point(im, im->ximg, 0);
-    gfx_add_point(im, 0, 0);
-    gfx_close_path(im);
-    gfx_new_area(im, 2, im->yimg - 2,
-                 im->ximg - 2,
-                 im->yimg - 2, im->ximg - 2, 2, im->graph_col[GRC_SHADEB]);
-    gfx_add_point(im, im->ximg, 0);
-    gfx_add_point(im, im->ximg, im->yimg);
-    gfx_add_point(im, 0, im->yimg);
-    gfx_close_path(im);
+    if (im->draw_3d_border > 0) {
+           /* draw 3d border */
+           i = im->draw_3d_border;
+           gfx_new_area(im, 0, im->yimg,
+                        i, im->yimg - i, i, i, im->graph_col[GRC_SHADEA]);
+           gfx_add_point(im, im->ximg - i, i);
+           gfx_add_point(im, im->ximg, 0);
+           gfx_add_point(im, 0, 0);
+           gfx_close_path(im);
+           gfx_new_area(im, i, im->yimg - i,
+                        im->ximg - i,
+                        im->yimg - i, im->ximg - i, i, im->graph_col[GRC_SHADEB]);
+           gfx_add_point(im, im->ximg, 0);
+           gfx_add_point(im, im->ximg, im->yimg);
+           gfx_add_point(im, 0, im->yimg);
+           gfx_close_path(im);
+    }
     if (im->draw_x_grid == 1)
         vertical_grid(im);
     if (im->draw_y_grid == 1) {
@@ -2626,35 +2669,31 @@ void grid_paint(
     /* yaxis unit description */
     if (im->ylegend[0] != '\0'){
         gfx_text(im,
-                 10,
-                 (im->yorigin -
-                  im->ysize / 2),
+                 im->xOriginLegendY+10,
+                 im->yOriginLegendY,
                  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->xOriginLegendY2+10,
+                  im->yOriginLegendY2,
                   im->graph_col[GRC_FONT],
                   im->text_prop[TEXT_PROP_UNIT].font_desc,
-                  im->tabwidth, 
+                  im->tabwidth,
                   RRDGRAPH_YLEGEND_ANGLE,
                   GFX_H_CENTER, GFX_V_CENTER,
                   im->second_axis_legend);
-    }        
+    }
+
     /* graph title */
     gfx_text(im,
-             im->ximg / 2, 6,
+             im->xOriginTitle, im->yOriginTitle+6,
              im->graph_col[GRC_FONT],
              im->
              text_prop[TEXT_PROP_TITLE].
@@ -2664,15 +2703,18 @@ void grid_paint(
     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,
+        double xpos = im->legendposition == EAST ? im->xOriginLegendY : im->ximg - 4;
+        gfx_text(im, xpos, 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') {
+        water_color = im->graph_col[GRC_FONT];
+        water_color.alpha = 0.3;
         gfx_text(im,
                  im->ximg / 2, im->yimg - 6,
                  water_color,
@@ -2683,13 +2725,13 @@ void grid_paint(
     }
 
     /* graph labels */
-    if (!(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH)) {
+    if (!(im->extra_flags & NOLEGEND) && !(im->extra_flags & ONLY_GRAPH)) {
         for (i = 0; i < im->gdes_c; i++) {
             if (im->gdes[i].legend[0] == '\0')
                 continue;
             /* im->gdes[i].leg_y is the bottom of the legend */
-            X0 = im->gdes[i].leg_x;
-            Y0 = im->gdes[i].leg_y;
+            X0 = im->xOriginLegend + im->gdes[i].leg_x;
+            Y0 = im->legenddirection == TOP_DOWN ? im->yOriginLegend + im->gdes[i].leg_y : im->yOriginLegend + im->legendheight - im->gdes[i].leg_y;
             gfx_text(im, X0, Y0,
                      im->graph_col[GRC_FONT],
                      im->
@@ -2713,41 +2755,63 @@ void grid_paint(
                 boxV = boxH;
                 /* shift the box up a bit */
                 Y0 -= boxV * 0.4;
-                /* make sure transparent colors show up the same way as in the graph */
-                gfx_new_area(im,
-                             X0, Y0 - boxV,
-                             X0, Y0, X0 + boxH, Y0, im->graph_col[GRC_BACK]);
-                gfx_add_point(im, X0 + boxH, Y0 - boxV);
-                gfx_close_path(im);
-                gfx_new_area(im, X0, Y0 - boxV, X0,
-                             Y0, X0 + boxH, Y0, im->gdes[i].col);
-                gfx_add_point(im, X0 + boxH, Y0 - boxV);
-                gfx_close_path(im);
-                cairo_save(im->cr);
-                cairo_new_path(im->cr);
-                cairo_set_line_width(im->cr, 1.0);
-                X1 = X0 + boxH;
-                Y1 = Y0 - boxV;
-                gfx_line_fit(im, &X0, &Y0);
-                gfx_line_fit(im, &X1, &Y1);
-                cairo_move_to(im->cr, X0, Y0);
-                cairo_line_to(im->cr, X1, Y0);
-                cairo_line_to(im->cr, X1, Y1);
-                cairo_line_to(im->cr, X0, Y1);
-                cairo_close_path(im->cr);
-                cairo_set_source_rgba(im->cr,
-                                      im->
-                                      graph_col
-                                      [GRC_FRAME].
-                                      red,
-                                      im->
-                                      graph_col
-                                      [GRC_FRAME].
-                                      green,
-                                      im->
-                                      graph_col
-                                      [GRC_FRAME].
-                                      blue, im->graph_col[GRC_FRAME].alpha);
+
+        if (im->dynamic_labels && im->gdes[i].gf == GF_HRULE) { /* [-] */ 
+                       cairo_save(im->cr);
+                       cairo_new_path(im->cr);
+                       cairo_set_line_width(im->cr, 1.0);
+                       gfx_line(im,
+                               X0, Y0 - boxV / 2,
+                               X0 + boxH, Y0 - boxV / 2,
+                               1.0, im->gdes[i].col);
+                       gfx_close_path(im);
+               } else if (im->dynamic_labels && im->gdes[i].gf == GF_VRULE) { /* [|] */
+                       cairo_save(im->cr);
+                       cairo_new_path(im->cr);
+                       cairo_set_line_width(im->cr, 1.0);
+                       gfx_line(im,
+                               X0 + boxH / 2, Y0,
+                               X0 + boxH / 2, Y0 - boxV,
+                               1.0, im->gdes[i].col);
+                       gfx_close_path(im);
+               } else if (im->dynamic_labels && im->gdes[i].gf == GF_LINE) { /* [/] */
+                       cairo_save(im->cr);
+                       cairo_new_path(im->cr);
+                       cairo_set_line_width(im->cr, im->gdes[i].linewidth);
+                       gfx_line(im,
+                               X0, Y0,
+                               X0 + boxH, Y0 - boxV,
+                               im->gdes[i].linewidth, im->gdes[i].col);
+                       gfx_close_path(im);
+               } else {
+               /* make sure transparent colors show up the same way as in the graph */
+                       gfx_new_area(im,
+                                    X0, Y0 - boxV,
+                                    X0, Y0, X0 + boxH, Y0, im->graph_col[GRC_BACK]);
+                       gfx_add_point(im, X0 + boxH, Y0 - boxV);
+                       gfx_close_path(im);
+                       gfx_new_area(im, X0, Y0 - boxV, X0,
+                                    Y0, X0 + boxH, Y0, im->gdes[i].col);
+                       gfx_add_point(im, X0 + boxH, Y0 - boxV);
+                       gfx_close_path(im);
+                       cairo_save(im->cr);
+                       cairo_new_path(im->cr);
+                       cairo_set_line_width(im->cr, 1.0);
+                       X1 = X0 + boxH;
+                       Y1 = Y0 - boxV;
+                       gfx_line_fit(im, &X0, &Y0);
+                       gfx_line_fit(im, &X1, &Y1);
+                       cairo_move_to(im->cr, X0, Y0);
+                       cairo_line_to(im->cr, X1, Y0);
+                       cairo_line_to(im->cr, X1, Y1);
+                       cairo_line_to(im->cr, X0, Y1);
+                       cairo_close_path(im->cr);
+                       cairo_set_source_rgba(im->cr,
+                                             im->graph_col[GRC_FRAME].red,
+                                             im->graph_col[GRC_FRAME].green,
+                                             im->graph_col[GRC_FRAME].blue,
+                                             im->graph_col[GRC_FRAME].alpha);
+               }
                 if (im->gdes[i].dash) {
                     /* make box borders in legend dashed if the graph is dashed */
                     double    dashes[] = {
@@ -2806,28 +2870,18 @@ int graph_size_location(
     /* The actual size of the image to draw is determined from
      ** several sources.  The size given on the command line is
      ** the graph area but we need more as we have to draw labels
-     ** and other things outside the graph area
+     ** and other things outside the graph area. If the option
+     ** --full-size-mode is selected the size defines the total
+     ** image size and the size available for the graph is
+     ** calculated.
      */
 
-    int       Xvertical = 0, Ytitle =
-        0, Xylabel = 0, Xmain = 0, Ymain =
-        0, Yxlabel = 0, Xspacing = 15, Yspacing = 15, Ywatermark = 4;
-
-    if (im->extra_flags & ONLY_GRAPH) {
-        im->xorigin = 0;
-        im->ximg = im->xsize;
-        im->yimg = im->ysize;
-        im->yorigin = im->ysize;
-        ytr(im, DNAN);
-        return 0;
-    }
-
     /** +---+-----------------------------------+
      ** | y |...............graph title.........|
      ** |   +---+-------------------------------+
      ** | a | y |                               |
      ** | x |   |                               |
-     ** | i | a |                               |    
+     ** | i | a |                               |
      ** | s | x |       main graph area         |
      ** |   | i |                               |
      ** | t | s |                               |
@@ -2842,10 +2896,37 @@ int graph_size_location(
      ** +---------------------------------------+
      */
 
+    int       Xvertical = 0, Xvertical2 = 0, Ytitle =
+        0, Xylabel = 0, Xmain = 0, Ymain =
+        0, Yxlabel = 0, Xspacing = 15, Yspacing = 15, Ywatermark = 4;
+
+    // no legends and no the shall be plotted it's easy
+    if (im->extra_flags & ONLY_GRAPH) {
+        im->xorigin = 0;
+        im->ximg = im->xsize;
+        im->yimg = im->ysize;
+        im->yorigin = im->ysize;
+        ytr(im, DNAN);
+        return 0;
+    }
+
+    if(im->watermark[0] != '\0') {
+        Ywatermark = im->text_prop[TEXT_PROP_WATERMARK].size * 2;
+    }
+
+    // calculate the width of the left vertical legend
     if (im->ylegend[0] != '\0') {
         Xvertical = im->text_prop[TEXT_PROP_UNIT].size * 2;
     }
 
+    // calculate the width of the right vertical legend
+    if (im->second_axis_legend[0] != '\0') {
+        Xvertical2 = im->text_prop[TEXT_PROP_UNIT].size * 2;
+    }
+    else{
+        Xvertical2 = Xspacing;
+    }
+
     if (im->title[0] != '\0') {
         /* The title is placed "inbetween" two text lines so it
          ** automatically has some vertical spacing.  The horizontal
@@ -2854,92 +2935,112 @@ int graph_size_location(
         /* if necessary, reduce the font size of the title until it fits the image width */
         Ytitle = im->text_prop[TEXT_PROP_TITLE].size * 2.6 + 10;
     }
+    else{
+        // we have no title; get a little clearing from the top
+        Ytitle = 1.5 * Yspacing;
+    }
 
     if (elements) {
         if (im->draw_x_grid) {
+            // calculate the height of the horizontal labelling
             Yxlabel = im->text_prop[TEXT_PROP_AXIS].size * 2.5;
         }
         if (im->draw_y_grid || im->forceleftspace) {
+            // calculate the width of the vertical labelling
             Xylabel =
                 gfx_get_text_width(im, 0,
-                                   im->
-                                   text_prop
-                                   [TEXT_PROP_AXIS].
-                                   font_desc,
+                                   im->text_prop[TEXT_PROP_AXIS].font_desc,
                                    im->tabwidth, "0") * im->unitslength;
         }
     }
 
+    // add some space to the labelling
+    Xylabel += Xspacing;
+
+    /* If the legend is printed besides the graph the width has to be
+     ** calculated first. Placing the legend north or south of the
+     ** graph requires the width calculation first, so the legend is
+     ** skipped for the moment.
+     */
+    im->legendheight = 0;
+    im->legendwidth = 0;
+    if (!(im->extra_flags & NOLEGEND)) {
+        if(im->legendposition == WEST || im->legendposition == EAST){
+            if (leg_place(im, 1) == -1){
+                return -1;
+            }
+        }
+    }
+
     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 axis labels, and the title.
          */
-        im->xorigin = 0;
         im->ximg = im->xsize;
         im->yimg = im->ysize;
-        im->yorigin = im->ysize;
         Xmain = im->ximg;
         Ymain = im->yimg;
+
         /* 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 - 3 * Xspacing;
-
-        im->xorigin = Xspacing + Xylabel;
 
-        if (Xvertical) {    /* unit description */
-            Xmain -= Xvertical;
-            im->xorigin += Xvertical;
+        Xmain -= Xylabel;// + Xspacing;
+        if((im->legendposition == WEST || im->legendposition == EAST) && !(im->extra_flags & NOLEGEND) ){
+            Xmain -= im->legendwidth;// + Xspacing;
         }
-
-        /* adjust space for second axis */
         if (im->second_axis_scale != 0){
-            Xmain -= Xylabel + Xspacing;
-        }
-        if (im->extra_flags & NO_RRDTOOL_TAG){
-            Xmain += Xspacing;
+            Xmain -= Xylabel;
         }
-        if (im->second_axis_legend[0] != '\0' ) {
-            Xmain -= im->text_prop[TEXT_PROP_UNIT].size * 1.5;
+        if (!(im->extra_flags & NO_RRDTOOL_TAG)){
+            Xmain -= Xspacing;
         }
 
+        Xmain -= Xvertical + Xvertical2;
+
+        /* limit the remaining space to 0 */
+        if(Xmain < 1){
+            Xmain = 1;
+        }
         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) {
-            im->yorigin = im->yimg -
-                im->text_prop[TEXT_PROP_AXIS].size * 2.5 - Yspacing;
-            Ymain = im->yorigin;
+        /* Putting the legend north or south, the height can now be calculated */
+        if (!(im->extra_flags & NOLEGEND)) {
+            if(im->legendposition == NORTH || im->legendposition == SOUTH){
+                im->legendwidth = im->ximg;
+                if (leg_place(im, 0) == -1){
+                    return -1;
+                }
+            }
         }
-        else {            
-            /* Determine where to place the legends onto the image.
-             ** Set Ymain and adjust im->yorigin to match the space requirements.
-             */
-            if (leg_place(im, &Ymain) == -1)
-                return -1;
+
+        if( (im->legendposition == NORTH || im->legendposition == SOUTH)  && !(im->extra_flags & NOLEGEND) ){
+            Ymain -=  Yxlabel + im->legendheight;
+        }
+        else{
+            Ymain -= Yxlabel;
         }
 
+        /* reserve space for the title *or* some padding above the graph */
+        Ymain -= Ytitle;
 
-        /* remove title space *or* some padding above the graph from the main graph area */
-        if (Ytitle) {
-            Ymain -= Ytitle;
-        } else {
-            Ymain -= 1.5 * Yspacing;
+            /* reserve space for padding below the graph */
+        if (im->extra_flags & NOLEGEND) {
+            Ymain -= Yspacing;
         }
 
-        /* watermark doesn't seem to effect the vertical size of the main graph area, oh well! */
         if (im->watermark[0] != '\0') {
             Ymain -= Ywatermark;
         }
-
+        /* limit the remaining height to 0 */
+        if(Ymain < 1){
+            Ymain = 1;
+        }
         im->ysize = Ymain;
     } else {            /* dimension options -width and -height refer to the dimensions of the main graph area */
 
@@ -2949,94 +3050,171 @@ int graph_size_location(
          ** and other things outside the graph area.
          */
 
-        if (im->ylegend[0] != '\0') {
-            Xvertical = im->text_prop[TEXT_PROP_UNIT].size * 2;
-        }
-
-
-        if (im->title[0] != '\0') {
-            /* The title is placed "inbetween" two text lines so it
-             ** automatically has some vertical spacing.  The horizontal
-             ** spacing is added here, on each side.
-             */
-            /* don't care for the with of the title
-               Xtitle = gfx_get_text_width(im->canvas, 0,
-               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;
-        }
-
         if (elements) {
-            Xmain = im->xsize;
+            Xmain = im->xsize; // + Xspacing;
             Ymain = im->ysize;
         }
-        /* 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. */
-
-        /* The legend width cannot yet be determined, as a result we
-         ** have problems adjusting the image to it.  For now, we just
-         ** forget about it at all; the legend will have to fit in the
-         ** size already allocated.
-         */
-        im->ximg = Xylabel + Xmain + 2 * Xspacing;
 
-        if (im->second_axis_scale != 0){
-            im->ximg += Xylabel + Xspacing;
+        im->ximg = Xmain + Xylabel;
+        if (!(im->extra_flags & NO_RRDTOOL_TAG)){
+            im->ximg += Xspacing;
         }
-        if (im->extra_flags & NO_RRDTOOL_TAG){
-            im->ximg -= Xspacing;
+
+        if( (im->legendposition == WEST || im->legendposition == EAST) && !(im->extra_flags & NOLEGEND) ){
+            im->ximg += im->legendwidth;// + Xspacing;
         }
-        
-        if (Xmain)
-            im->ximg += 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 */
-            im->ximg += Xvertical;
-            im->xorigin += Xvertical;
+        if (im->second_axis_scale != 0){
+            im->ximg += Xylabel;
         }
-        if (im->second_axis_legend[0] != '\0' ) {
-            im->ximg += Xvertical;
+
+        im->ximg += Xvertical + Xvertical2;
+
+        if (!(im->extra_flags & NOLEGEND)) {
+            if(im->legendposition == NORTH || im->legendposition == SOUTH){
+                im->legendwidth = im->ximg;
+                if (leg_place(im, 0) == -1){
+                    return -1;
+                }
+            }
         }
-      
-        xtr(im, 0);
-        /* The vertical size is interesting... we need to compare
-         ** the sum of {Ytitle, Ymain, Yxlabel, Ylegend, Ywatermark} with 
-         ** Yvertical however we need to know {Ytitle+Ymain+Yxlabel}
-         ** in order to start even thinking about Ylegend or Ywatermark.
-         **
-         ** Do it in three portions: First calculate the inner part,
-         ** then do the legend, then adjust the total height of the img,
-         ** adding space for a watermark if one exists;
-         */
-        /* reserve space for main and/or pie */
+
         im->yimg = Ymain + Yxlabel;
-        im->yorigin = im->yimg - Yxlabel;
+        if( (im->legendposition == NORTH || im->legendposition == SOUTH)  && !(im->extra_flags & NOLEGEND) ){
+             im->yimg += im->legendheight;
+        }
+
         /* reserve space for the title *or* some padding above the graph */
         if (Ytitle) {
             im->yimg += Ytitle;
-            im->yorigin += Ytitle;
         } else {
             im->yimg += 1.5 * Yspacing;
-            im->yorigin += 1.5 * Yspacing;
         }
         /* reserve space for padding below the graph */
-        im->yimg += Yspacing;
-        /* Determine where to place the legends onto the image.
-         ** Adjust im->yimg to match the space requirements.
-         */
-        if (leg_place(im, 0) == -1)
-            return -1;
+        if (im->extra_flags & NOLEGEND) {
+            im->yimg += Yspacing;
+        }
+
         if (im->watermark[0] != '\0') {
             im->yimg += Ywatermark;
         }
     }
 
+
+    /* In case of putting the legend in west or east position the first
+     ** legend calculation might lead to wrong positions if some items
+     ** are not aligned on the left hand side (e.g. centered) as the
+     ** legendwidth wight have been increased after the item was placed.
+     ** In this case the positions have to be recalculated.
+     */
+    if (!(im->extra_flags & NOLEGEND)) {
+        if(im->legendposition == WEST || im->legendposition == EAST){
+            if (leg_place(im, 0) == -1){
+                return -1;
+            }
+        }
+    }
+
+    /* After calculating all dimensions
+     ** it is now possible to calculate
+     ** all offsets.
+     */
+    switch(im->legendposition){
+        case NORTH:
+            im->xOriginTitle   = Xvertical + Xylabel + (im->xsize / 2);
+            im->yOriginTitle   = 0;
+
+            im->xOriginLegend  = 0;
+            im->yOriginLegend  = Ytitle;
+
+            im->xOriginLegendY = 0;
+            im->yOriginLegendY = Ytitle + im->legendheight + (Ymain / 2) + Yxlabel;
+
+            im->xorigin        = Xvertical + Xylabel;
+            im->yorigin        = Ytitle + im->legendheight + Ymain;
+
+            im->xOriginLegendY2 = Xvertical + Xylabel + Xmain;
+            if (im->second_axis_scale != 0){
+                im->xOriginLegendY2 += Xylabel;
+            }
+            im->yOriginLegendY2 = Ytitle + im->legendheight + (Ymain / 2) + Yxlabel;
+
+            break;
+
+        case WEST:
+            im->xOriginTitle   = im->legendwidth + Xvertical + Xylabel + im->xsize / 2;
+            im->yOriginTitle   = 0;
+
+            im->xOriginLegend  = 0;
+            im->yOriginLegend  = Ytitle;
+
+            im->xOriginLegendY = im->legendwidth;
+            im->yOriginLegendY = Ytitle + (Ymain / 2);
+
+            im->xorigin        = im->legendwidth + Xvertical + Xylabel;
+            im->yorigin        = Ytitle + Ymain;
+
+            im->xOriginLegendY2 = im->legendwidth + Xvertical + Xylabel + Xmain;
+            if (im->second_axis_scale != 0){
+                im->xOriginLegendY2 += Xylabel;
+            }
+            im->yOriginLegendY2 = Ytitle + (Ymain / 2);
+
+            break;
+
+        case SOUTH:
+            im->xOriginTitle   = Xvertical + Xylabel + im->xsize / 2;
+            im->yOriginTitle   = 0;
+
+            im->xOriginLegend  = 0;
+            im->yOriginLegend  = Ytitle + Ymain + Yxlabel;
+
+            im->xOriginLegendY = 0;
+            im->yOriginLegendY = Ytitle + (Ymain / 2);
+
+            im->xorigin        = Xvertical + Xylabel;
+            im->yorigin        = Ytitle + Ymain;
+
+            im->xOriginLegendY2 = Xvertical + Xylabel + Xmain;
+            if (im->second_axis_scale != 0){
+                im->xOriginLegendY2 += Xylabel;
+            }
+            im->yOriginLegendY2 = Ytitle + (Ymain / 2);
+
+            break;
+
+        case EAST:
+            im->xOriginTitle   = Xvertical + Xylabel + im->xsize / 2;
+            im->yOriginTitle   = 0;
+
+            im->xOriginLegend  = Xvertical + Xylabel + Xmain + Xvertical2;
+            if (im->second_axis_scale != 0){
+                im->xOriginLegend += Xylabel;
+            }
+            im->yOriginLegend  = Ytitle;
+
+            im->xOriginLegendY = 0;
+            im->yOriginLegendY = Ytitle + (Ymain / 2);
+
+            im->xorigin        = Xvertical + Xylabel;
+            im->yorigin        = Ytitle + Ymain;
+
+            im->xOriginLegendY2 = Xvertical + Xylabel + Xmain;
+            if (im->second_axis_scale != 0){
+                im->xOriginLegendY2 += Xylabel;
+            }
+            im->yOriginLegendY2 = Ytitle + (Ymain / 2);
+
+            if (!(im->extra_flags & NO_RRDTOOL_TAG)){
+                im->xOriginTitle    += Xspacing;
+                im->xOriginLegend   += Xspacing;
+                im->xOriginLegendY  += Xspacing;
+                im->xorigin         += Xspacing;
+                im->xOriginLegendY2 += Xspacing;
+            }
+            break;
+    }
+
+    xtr(im, 0);
     ytr(im, DNAN);
     return 0;
 }
@@ -3070,14 +3248,6 @@ int graph_paint(
 
 //    PangoFontMap *font_map = pango_cairo_font_map_get_default();
 
-    /* if we want and can be lazy ... quit now */
-    if (lazy) {
-        info.u_cnt = im->ximg;
-        grinfo_push(im, sprintf_alloc("image_width"), RD_I_CNT, info);
-        info.u_cnt = im->yimg;
-        grinfo_push(im, sprintf_alloc("image_height"), RD_I_CNT, info);
-        return 0;
-    }
     /* pull the data from the rrd files ... */
     if (data_fetch(im) == -1)
         return -1;
@@ -3086,14 +3256,15 @@ int graph_paint(
         return -1;
     /* calculate and PRINT and GPRINT definitions. We have to do it at
      * this point because it will affect the length of the legends
-     * if there are no graph elements (i==0) we stop here ... 
-     * if we are lazy, try to quit ... 
+     * if there are no graph elements (i==0) we stop here ...
+     * if we are lazy, try to quit ...
      */
     i = print_calc(im);
     if (i < 0)
         return -1;
 
-    if ((i == 0) || lazy)
+    /* if we want and can be lazy ... quit now */
+    if (i == 0)
         return 0;
 
 /**************************************************************
@@ -3115,6 +3286,14 @@ int graph_paint(
     grinfo_push(im, sprintf_alloc("image_width"), RD_I_CNT, info);
     info.u_cnt = im->yimg;
     grinfo_push(im, sprintf_alloc("image_height"), RD_I_CNT, info);
+    info.u_cnt = im->start;
+    grinfo_push(im, sprintf_alloc("graph_start"), RD_I_CNT, info);
+    info.u_cnt = im->end;
+    grinfo_push(im, sprintf_alloc("graph_end"), RD_I_CNT, info);
+
+    /* if we want and can be lazy ... quit now */
+    if (lazy)
+        return 0;
 
     /* get actual drawing data and find min and max values */
     if (data_proc(im) == -1)
@@ -3133,6 +3312,7 @@ int graph_paint(
     info.u_val = im->maxval;
     grinfo_push(im, sprintf_alloc("value_max"), RD_I_VAL, info);
 
+
     if (!calc_horizontal_grid(im))
         return -1;
     /* reset precalc */
@@ -3258,13 +3438,13 @@ int graph_paint(
             }           /* for */
 
             /* *******************************************************
-               a           ___. (a,t) 
+               a           ___. (a,t)
                |   |    ___
                ____|   |   |   |
                |       |___|
-               -------|--t-1--t--------------------------------      
+               -------|--t-1--t--------------------------------
 
-               if we know the value at time t was a then 
+               if we know the value at time t was a then
                we draw a square from t-1 to t with the value a.
 
                ********************************************************* */
@@ -3571,7 +3751,7 @@ int graph_paint(
 
 
 /*****************************************************
- * graph stuff 
+ * graph stuff
  *****************************************************/
 
 int gdes_alloc(
@@ -3710,8 +3890,11 @@ int rrd_graph(
             (*prdata)[prlines] = NULL;
             strcpy((*prdata)[prlines - 1], walker->value.u_str);
         } else if (strcmp(walker->key, "image") == 0) {
-            fwrite(walker->value.u_blo.ptr, walker->value.u_blo.size, 1,
-                   (stream ? stream : stdout));
+            if ( fwrite(walker->value.u_blo.ptr, walker->value.u_blo.size, 1,
+                   (stream ? stream : stdout)) == 0 && ferror(stream ? stream : stdout)){
+                rrd_set_error("writing image");
+                return 0;
+            }
         }
         /* skip anything else */
         walker = walker->next;
@@ -3733,9 +3916,10 @@ rrd_info_t *rrd_graph_v(
 {
     image_desc_t im;
     rrd_info_t *grinfo;
+    char *old_locale;
     rrd_graph_init(&im);
     /* a dummy surface so that we can measure text sizes for placements */
-    
+    old_locale = setlocale(LC_NUMERIC, "C");
     rrd_graph_options(argc, argv, &im);
     if (rrd_test_error()) {
         rrd_info_free(im.grinfo);
@@ -3765,6 +3949,8 @@ rrd_info_t *rrd_graph_v(
     }
 
     rrd_graph_script(argc, argv, &im, 1);
+    setlocale(LC_NUMERIC, old_locale); /* reenable locale for rendering the graph */
+
     if (rrd_test_error()) {
         rrd_info_free(im.grinfo);
         im_free(&im);
@@ -3812,15 +3998,15 @@ rrd_info_t *rrd_graph_v(
     return grinfo;
 }
 
-static void 
+static void
 rrd_set_font_desc (
     image_desc_t *im,int prop,char *font, double size ){
     if (font){
-        strncpy(im->text_prop[prop].font, font, sizeof(text_prop[prop].font) - 1);        
-        im->text_prop[prop].font[sizeof(text_prop[prop].font) - 1] = '\0';   
-        im->text_prop[prop].font_desc = pango_font_description_from_string( font );        
+        strncpy(im->text_prop[prop].font, font, sizeof(text_prop[prop].font) - 1);
+        im->text_prop[prop].font[sizeof(text_prop[prop].font) - 1] = '\0';
+        im->text_prop[prop].font_desc = pango_font_description_from_string( font );
     };
-    if (size > 0){  
+    if (size > 0){
         im->text_prop[prop].size = size;
     };
     if (im->text_prop[prop].font_desc && im->text_prop[prop].size ){
@@ -3840,16 +4026,13 @@ void rrd_graph_init(
 #ifdef HAVE_TZSET
     tzset();
 #endif
-#ifdef HAVE_SETLOCALE
-    setlocale(LC_TIME, "");
-#ifdef HAVE_MBSTOWCS
-    setlocale(LC_CTYPE, "");
-#endif
-#endif
+
     im->base = 1000;
     im->daemon_addr = NULL;
     im->draw_x_grid = 1;
     im->draw_y_grid = 1;
+    im->draw_3d_border = 2;
+    im->dynamic_labels = 0;
     im->extra_flags = 0;
     im->font_options = cairo_font_options_create();
     im->forceleftspace = 0;
@@ -3864,6 +4047,10 @@ void rrd_graph_init(
     im->imgformat = IF_PNG;
     im->imginfo = NULL;
     im->lazy = 0;
+    im->legenddirection = TOP_DOWN;
+    im->legendheight = 0;
+    im->legendposition = SOUTH;
+    im->legendwidth = 0;
     im->logarithmic = 0;
     im->maxval = DNAN;
     im->minval = 0;
@@ -3885,6 +4072,10 @@ void rrd_graph_init(
     im->ximg = 0;
     im->xlab_user.minsec = -1;
     im->xorigin = 0;
+    im->xOriginLegend = 0;
+    im->xOriginLegendY = 0;
+    im->xOriginLegendY2 = 0;
+    im->xOriginTitle = 0;
     im->xsize = 400;
     im->ygridstep = DNAN;
     im->yimg = 0;
@@ -3892,12 +4083,16 @@ void rrd_graph_init(
     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->second_axis_format[0] = '\0';
     im->yorigin = 0;
+    im->yOriginLegend = 0;
+    im->yOriginLegendY = 0;
+    im->yOriginLegendY2 = 0;
+    im->yOriginTitle = 0;
     im->ysize = 100;
     im->zoom = 1;
 
-    im->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 10, 10);     
+    im->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 10, 10);
     im->cr = cairo_create(im->surface);
 
     for (i = 0; i < DIM(text_prop); i++) {
@@ -3948,7 +4143,6 @@ void rrd_graph_options(
     long      long_tmp;
     rrd_time_value_t start_tv, end_tv;
     long unsigned int color;
-    char     *old_locale = "";
 
     /* defines for long options without a short equivalent. should be bytes,
        and may not collide with (the ASCII value of) short options */
@@ -3956,53 +4150,58 @@ void rrd_graph_options(
 
 /* *INDENT-OFF* */
     struct option long_options[] = {
-        { "start",              required_argument, 0, 's'}, 
-        { "end",                required_argument, 0, 'e'},
-        { "x-grid",             required_argument, 0, 'x'},
-        { "y-grid",             required_argument, 0, 'y'},
-        { "vertical-label",     required_argument, 0, 'v'},
-        { "width",              required_argument, 0, 'w'},
-        { "height",             required_argument, 0, 'h'},
-        { "full-size-mode",     no_argument,       0, 'D'},
-        { "interlaced",         no_argument,       0, 'i'},
-        { "upper-limit",        required_argument, 0, 'u'},
-        { "lower-limit",        required_argument, 0, 'l'},
-        { "rigid",              no_argument,       0, 'r'},
+        { "alt-autoscale",      no_argument,       0, 'A'},
+        { "imgformat",          required_argument, 0, 'a'},
+        { "font-smoothing-threshold", required_argument, 0, 'B'},
         { "base",               required_argument, 0, 'b'},
-        { "logarithmic",        no_argument,       0, 'o'},
         { "color",              required_argument, 0, 'c'},
-        { "font",               required_argument, 0, 'n'},
-        { "title",              required_argument, 0, 't'},
+        { "full-size-mode",     no_argument,       0, 'D'},
+        { "daemon",             required_argument, 0, 'd'},
+        { "slope-mode",         no_argument,       0, 'E'},
+        { "end",                required_argument, 0, 'e'},
+        { "force-rules-legend", no_argument,       0, 'F'},
         { "imginfo",            required_argument, 0, 'f'},
-        { "imgformat",          required_argument, 0, 'a'},
-        { "lazy",               no_argument,       0, 'z'},
-        { "zoom",               required_argument, 0, 'm'},
+        { "graph-render-mode",  required_argument, 0, 'G'},
         { "no-legend",          no_argument,       0, 'g'},
-        { "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'},
+        { "height",             required_argument, 0, 'h'},
+        { "no-minor",           no_argument,       0, 'I'},
+        { "interlaced",         no_argument,       0, 'i'},
         { "alt-autoscale-min",  no_argument,       0, 'J'},
+        { "only-graph",         no_argument,       0, 'j'},
+        { "units-length",       required_argument, 0, 'L'},
+        { "lower-limit",        required_argument, 0, 'l'},
         { "alt-autoscale-max",  no_argument,       0, 'M'},
+        { "zoom",               required_argument, 0, 'm'},
         { "no-gridfit",         no_argument,       0, 'N'},
-        { "units-exponent",     required_argument, 0, 'X'},
-        { "units-length",       required_argument, 0, 'L'},
-        { "units",              required_argument, 0, LONGOPT_UNITS_SI},
+        { "font",               required_argument, 0, 'n'},
+        { "logarithmic",        no_argument,       0, 'o'},
+        { "pango-markup",       no_argument,       0, 'P'},
+        { "font-render-mode",   required_argument, 0, 'R'},
+        { "rigid",              no_argument,       0, 'r'},
         { "step",               required_argument, 0, 'S'},
+        { "start",              required_argument, 0, 's'},
         { "tabwidth",           required_argument, 0, 'T'},
-        { "font-render-mode",   required_argument, 0, 'R'},
-        { "graph-render-mode",  required_argument, 0, 'G'},
-        { "font-smoothing-threshold", required_argument, 0, 'B'},
+        { "title",              required_argument, 0, 't'},
+        { "upper-limit",        required_argument, 0, 'u'},
+        { "vertical-label",     required_argument, 0, 'v'},
         { "watermark",          required_argument, 0, 'W'},
+        { "width",              required_argument, 0, 'w'},
+        { "units-exponent",     required_argument, 0, 'X'},
+        { "x-grid",             required_argument, 0, 'x'},
+        { "alt-y-grid",         no_argument,       0, 'Y'},
+        { "y-grid",             required_argument, 0, 'y'},
+        { "lazy",               no_argument,       0, 'z'},
+        { "units",              required_argument, 0, LONGOPT_UNITS_SI},
         { "alt-y-mrtg",         no_argument,       0, 1000},    /* this has no effect it is just here to save old apps from crashing when they use it */
-        { "pango-markup",       no_argument,       0, 'P'},
-        { "daemon",             required_argument, 0, 'd'},
+        { "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},
+        { "legend-position",    required_argument, 0, 1005},
+        { "legend-direction",   required_argument, 0, 1006},
+        { "border",             required_argument, 0, 1007},
+        { "grid-dash",          required_argument, 0, 1008},
+        { "dynamic-labels",     no_argument,       0, 1009},
         {  0, 0, 0, 0}
 };
 /* *INDENT-ON* */
@@ -4017,7 +4216,7 @@ void rrd_graph_options(
         int       col_start, col_end;
 
         opt = getopt_long(argc, argv,
-                          "s:e:x:y:v:w:h:D:iu:l:rb:oc:n:m:t:f:a:I:zgjFYAMEX:L:S:T:NR:B:W:kPd:",
+                          "Aa:B:b:c:Dd:Ee:Ff:G:gh:IiJjL:l:Nn:Bb:oPR:rS:s:T:t:u:v:W:w:X:x:Yy:z",
                           long_options, &option_index);
         if (opt == EOF)
             break;
@@ -4043,16 +4242,39 @@ void rrd_graph_options(
         case 'g':
             im->extra_flags |= NOLEGEND;
             break;
+        case 1005:
+            if (strcmp(optarg, "north") == 0) {
+                im->legendposition = NORTH;
+            } else if (strcmp(optarg, "west") == 0) {
+                im->legendposition = WEST;
+            } else if (strcmp(optarg, "south") == 0) {
+                im->legendposition = SOUTH;
+            } else if (strcmp(optarg, "east") == 0) {
+                im->legendposition = EAST;
+            } else {
+                rrd_set_error("unknown legend-position '%s'", optarg);
+                return;
+            }
+            break;
+        case 1006:
+            if (strcmp(optarg, "topdown") == 0) {
+                im->legenddirection = TOP_DOWN;
+            } else if (strcmp(optarg, "bottomup") == 0) {
+                im->legenddirection = BOTTOM_UP;
+            } else {
+                rrd_set_error("unknown legend-position '%s'", optarg);
+                return;
+            }
+            break;
         case 'F':
             im->extra_flags |= FORCE_RULES_LEGEND;
             break;
         case 1001:
             im->extra_flags |= NO_RRDTOOL_TAG;
-            break;              
+            break;
         case LONGOPT_UNITS_SI:
             if (im->extra_flags & FORCE_UNITS) {
                 rrd_set_error("--units can only be used once!");
-                setlocale(LC_NUMERIC, old_locale);
                 return;
             }
             if (strcmp(optarg, "si") == 0)
@@ -4070,14 +4292,10 @@ void rrd_graph_options(
             im->forceleftspace = 1;
             break;
         case 'T':
-            old_locale = setlocale(LC_NUMERIC, "C");
             im->tabwidth = atof(optarg);
-            setlocale(LC_NUMERIC, old_locale);
             break;
         case 'S':
-            old_locale = setlocale(LC_NUMERIC, "C");
             im->step = atoi(optarg);
-            setlocale(LC_NUMERIC, old_locale);
             break;
         case 'N':
             im->gridfit = 0;
@@ -4141,9 +4359,7 @@ void rrd_graph_options(
                 im->draw_y_grid = 0;
                 break;
             };
-            old_locale = setlocale(LC_NUMERIC, "C");
             if (sscanf(optarg, "%lf:%d", &im->ygridstep, &im->ylabfact) == 2) {
-                setlocale(LC_NUMERIC, old_locale);
                 if (im->ygridstep <= 0) {
                     rrd_set_error("grid step must be > 0");
                     return;
@@ -4152,11 +4368,25 @@ void rrd_graph_options(
                     return;
                 }
             } else {
-                setlocale(LC_NUMERIC, old_locale);
                 rrd_set_error("invalid y-grid format");
                 return;
             }
             break;
+        case 1007:
+            im->draw_3d_border = atoi(optarg);
+            break;
+        case 1008: /* grid-dash */
+            if(sscanf(optarg,
+                      "%lf:%lf",
+                      &im->grid_dash_on,
+                      &im->grid_dash_off) != 2) {
+                rrd_set_error("expected grid-dash format float:float");
+                return;
+            }
+            break;   
+        case 1009: /* enable dynamic labels */
+            im->dynamic_labels = 1;
+            break;         
         case 1002: /* right y axis */
 
             if(sscanf(optarg,
@@ -4189,14 +4419,10 @@ void rrd_graph_options(
             im->ylegend[150] = '\0';
             break;
         case 'u':
-            old_locale = setlocale(LC_NUMERIC, "C");
             im->maxval = atof(optarg);
-            setlocale(LC_NUMERIC, old_locale);
             break;
         case 'l':
-            old_locale = setlocale(LC_NUMERIC, "C");
             im->minval = atof(optarg);
-            setlocale(LC_NUMERIC, old_locale);
             break;
         case 'b':
             im->base = atol(optarg);
@@ -4301,20 +4527,18 @@ void rrd_graph_options(
             double    size = 1;
             int       end;
 
-            old_locale = setlocale(LC_NUMERIC, "C");
             if (sscanf(optarg, "%10[A-Z]:%lf%n", prop, &size, &end) >= 2) {
                 int       sindex, propidx;
 
-                setlocale(LC_NUMERIC, old_locale);
                 if ((sindex = text_prop_conv(prop)) != -1) {
                     for (propidx = sindex;
                          propidx < TEXT_PROP_LAST; propidx++) {
                         if (size > 0) {
-                            rrd_set_font_desc(im,propidx,NULL,size);   
+                            rrd_set_font_desc(im,propidx,NULL,size);
                         }
                         if ((int) strlen(optarg) > end+2) {
                             if (optarg[end] == ':') {
-                                rrd_set_font_desc(im,propidx,optarg + end + 1,0);   
+                                rrd_set_font_desc(im,propidx,optarg + end + 1,0);
                             } else {
                                 rrd_set_error
                                     ("expected : after font size in '%s'",
@@ -4332,16 +4556,13 @@ void rrd_graph_options(
                     return;
                 }
             } else {
-                setlocale(LC_NUMERIC, old_locale);
                 rrd_set_error("invalid text property format");
                 return;
             }
             break;
         }
         case 'm':
-            old_locale = setlocale(LC_NUMERIC, "C");
             im->zoom = atof(optarg);
-            setlocale(LC_NUMERIC, old_locale);
             if (im->zoom <= 0.0) {
                 rrd_set_error("zoom factor must be > 0");
                 return;
@@ -4420,7 +4641,7 @@ void rrd_graph_options(
         int status = rrdc_connect(im->daemon_addr);
         if (status != 0) return;
     }
-    
+
     pango_cairo_context_set_font_options(pango_layout_get_context(im->layout), im->font_options);
     pango_layout_context_changed(im->layout);
 
@@ -4564,12 +4785,9 @@ int vdef_parse(
     double    param;
     char      func[30];
     int       n;
-    char     *old_locale;
 
     n = 0;
-    old_locale = setlocale(LC_NUMERIC, "C");
     sscanf(str, "%le,%29[A-Z]%n", &param, func, &n);
-    setlocale(LC_NUMERIC, old_locale);
     if (n == (int) strlen(str)) {   /* matched */
         ;
     } else {
@@ -4689,7 +4907,7 @@ int vdef_calc(
             array[step] = data[step * src->ds_cnt];
         }
         qsort(array, step, sizeof(double), vdef_percent_compar);
-        field = (steps - 1) * dst->vf.param / 100;
+        field = round((dst->vf.param * (double)(steps - 1)) / 100.0);
         dst->vf.val = array[field];
         dst->vf.when = 0;   /* no time component */
         free(array);
@@ -4722,7 +4940,7 @@ int vdef_calc(
             }
         }
         qsort(array, nancount, sizeof(double), vdef_percent_compar);
-        field = (nancount - 1) * dst->vf.param / 100;
+        field = round( dst->vf.param * (double)(nancount - 1) / 100.0);
         dst->vf.val = array[field];
         dst->vf.when = 0;   /* no time component */
         free(array);