fix use of setlocale all over the place ...
[rrdtool.git] / src / rrd_graph.c
index f5a63c3..898b6ff 100644 (file)
@@ -337,6 +337,12 @@ int im_free(
         free(im->gdes[i].rpnp);
     }
     free(im->gdes);
+
+    for (i = 0; i < DIM(text_prop);i++){
+        pango_font_description_free(im->text_prop[i].font_desc);
+        im->text_prop[i].font_desc = NULL;
+    }
+
     if (im->font_options)
         cairo_font_options_destroy(im->font_options);
 
@@ -344,6 +350,8 @@ int im_free(
         status = cairo_status(im->cr);
         cairo_destroy(im->cr);
     }
+
+
     if (im->rendered_image) {
         free(im->rendered_image);
     }
@@ -846,24 +854,24 @@ int data_fetch(
         }
         if (!skip) {
             unsigned long ft_step = im->gdes[i].step;   /* ft_step will record what we got from fetch */
-            const char *daemon;
+            const char *rrd_daemon;
             int status;
 
             if (im->gdes[i].daemon[0] != 0)
-                daemon = im->gdes[i].daemon;
+                rrd_daemon = im->gdes[i].daemon;
             else
-                daemon = im->daemon_addr;
+                rrd_daemon = im->daemon_addr;
 
             /* "daemon" may be NULL. ENV_RRDCACHED_ADDRESS is evaluated in that
              * case. If "daemon" holds the same value as in the previous
              * iteration, no actual new connection is established - the
              * existing connection is re-used. */
-            rrdc_connect (daemon);
+            rrdc_connect (rrd_daemon);
 
             /* If connecting was successfull, use the daemon to query the data.
              * If there is no connection, for example because no daemon address
              * was specified, (try to) use the local file directly. */
-            if (rrdc_is_connected (daemon))
+            if (rrdc_is_connected (rrd_daemon))
             {
                 status = rrdc_fetch (im->gdes[i].rrd,
                         cf_to_string (im->gdes[i].cf),
@@ -1597,8 +1605,12 @@ int print_calc(
 
                 if (im->gdes[i].strftm) {
                     prline.u_str = (char*)malloc((FMT_LEG_LEN + 2) * sizeof(char));
-                    strftime(prline.u_str,
-                             FMT_LEG_LEN, im->gdes[i].format, &tmvdef);
+                    if (im->gdes[vidx].vf.never == 1) {
+                       time_clean(prline.u_str, im->gdes[i].format);
+                    } else {
+                        strftime(prline.u_str,
+                                 FMT_LEG_LEN, im->gdes[i].format, &tmvdef);
+                    }
                 } else if (bad_format(im->gdes[i].format)) {
                     rrd_set_error
                         ("bad format for PRINT in '%s'", im->gdes[i].format);
@@ -1615,8 +1627,12 @@ int print_calc(
                 /* GF_GPRINT */
 
                 if (im->gdes[i].strftm) {
-                    strftime(im->gdes[i].legend,
-                             FMT_LEG_LEN, im->gdes[i].format, &tmvdef);
+                    if (im->gdes[vidx].vf.never == 1) {
+                       time_clean(im->gdes[i].legend, im->gdes[i].format);
+                    } else {
+                        strftime(im->gdes[i].legend,
+                                 FMT_LEG_LEN, im->gdes[i].format, &tmvdef);
+                    }
                 } else {
                     if (bad_format(im->gdes[i].format)) {
                         rrd_set_error
@@ -2285,7 +2301,7 @@ int horizontal_log_grid(
                         }
                 }
                 else {
-                      sprintf(graph_label_right,im->second_axis_format,sval);
+                      sprintf(graph_label_right,im->second_axis_format,sval,"");
                 }
 
                 gfx_text ( im,
@@ -3975,15 +3991,18 @@ rrd_info_t *rrd_graph_v(
     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");
+    old_locale = setlocale(LC_NUMERIC, NULL);
+    setlocale(LC_NUMERIC, "C");
     rrd_graph_options(argc, argv, &im);
     if (rrd_test_error()) {
+        setlocale(LC_NUMERIC, old_locale); /* reenable locale */
         rrd_info_free(im.grinfo);
         im_free(&im);
         return NULL;
     }
 
     if (optind >= argc) {
+        setlocale(LC_NUMERIC, old_locale); /* reenable locale */
         rrd_info_free(im.grinfo);
         im_free(&im);
         rrd_set_error("missing filename");
@@ -3991,6 +4010,7 @@ rrd_info_t *rrd_graph_v(
     }
 
     if (strlen(argv[optind]) >= MAXPATH) {
+        setlocale(LC_NUMERIC, old_locale); /* reenable locale */
         rrd_set_error("filename (including path) too long");
         rrd_info_free(im.grinfo);
         im_free(&im);
@@ -4060,6 +4080,8 @@ rrd_set_font_desc (
     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';
+        /* if we already got one, drop it first */
+        pango_font_description_free(im->text_prop[prop].font_desc);
         im->text_prop[prop].font_desc = pango_font_description_from_string( font );
     };
     if (size > 0){
@@ -4153,6 +4175,7 @@ void rrd_graph_init(
 
     for (i = 0; i < DIM(text_prop); i++) {
         im->text_prop[i].size = -1;
+        im->text_prop[i].font_desc = NULL;
         rrd_set_font_desc(im,i, deffont ? deffont : text_prop[i].font,text_prop[i].size);
     }
 
@@ -4167,6 +4190,7 @@ void rrd_graph_init(
     pango_cairo_update_context(im->cr,context);
 
     im->layout = pango_layout_new(context);
+    g_object_unref (context);
 
 //  im->layout = pango_cairo_create_layout(im->cr);
 
@@ -4895,6 +4919,7 @@ int vdef_parse(
             gdes->vf.param = param;
             gdes->vf.val = DNAN;    /* undefined */
             gdes->vf.when = 0;  /* undefined */
+            gdes->vf.never = 1;
         } else {
             rrd_set_error
                 ("Parameter '%f' out of range in VDEF '%s'\n",
@@ -4916,6 +4941,7 @@ int vdef_parse(
             gdes->vf.param = DNAN;
             gdes->vf.val = DNAN;
             gdes->vf.when = 0;
+            gdes->vf.never = 1;
         } else {
             rrd_set_error
                 ("Function '%s' needs no parameter in VDEF '%s'\n",
@@ -4961,6 +4987,7 @@ int vdef_calc(
         field = round((dst->vf.param * (double)(steps - 1)) / 100.0);
         dst->vf.val = array[field];
         dst->vf.when = 0;   /* no time component */
+        dst->vf.never = 1;
         free(array);
 #if 0
         for (step = 0; step < steps; step++)
@@ -4994,6 +5021,7 @@ int vdef_calc(
         field = round( dst->vf.param * (double)(nancount - 1) / 100.0);
         dst->vf.val = array[field];
         dst->vf.when = 0;   /* no time component */
+        dst->vf.never = 1;
         free(array);
     }
         break;
@@ -5004,15 +5032,18 @@ int vdef_calc(
         if (step == steps) {
             dst->vf.val = DNAN;
             dst->vf.when = 0;
+            dst->vf.never = 1;
         } else {
             dst->vf.val = data[step * src->ds_cnt];
             dst->vf.when = src->start + (step + 1) * src->step;
+            dst->vf.never = 0;
         }
         while (step != steps) {
             if (finite(data[step * src->ds_cnt])) {
                 if (data[step * src->ds_cnt] > dst->vf.val) {
                     dst->vf.val = data[step * src->ds_cnt];
                     dst->vf.when = src->start + (step + 1) * src->step;
+                    dst->vf.never = 0;
                 }
             }
             step++;
@@ -5035,9 +5066,11 @@ int vdef_calc(
             if (dst->vf.op == VDEF_TOTAL) {
                 dst->vf.val = sum * src->step;
                 dst->vf.when = 0;   /* no time component */
+                dst->vf.never = 1;
             } else if (dst->vf.op == VDEF_AVERAGE) {
                 dst->vf.val = sum / cnt;
                 dst->vf.when = 0;   /* no time component */
+                dst->vf.never = 1;
             } else {
                 average = sum / cnt;
                 sum = 0.0;
@@ -5048,10 +5081,12 @@ int vdef_calc(
                 }
                 dst->vf.val = pow(sum / cnt, 0.5);
                 dst->vf.when = 0;   /* no time component */
+                dst->vf.never = 1;
             };
         } else {
             dst->vf.val = DNAN;
             dst->vf.when = 0;
+            dst->vf.never = 1;
         }
     }
         break;
@@ -5062,15 +5097,18 @@ int vdef_calc(
         if (step == steps) {
             dst->vf.val = DNAN;
             dst->vf.when = 0;
+            dst->vf.never = 1;
         } else {
             dst->vf.val = data[step * src->ds_cnt];
             dst->vf.when = src->start + (step + 1) * src->step;
+            dst->vf.never = 0;
         }
         while (step != steps) {
             if (finite(data[step * src->ds_cnt])) {
                 if (data[step * src->ds_cnt] < dst->vf.val) {
                     dst->vf.val = data[step * src->ds_cnt];
                     dst->vf.when = src->start + (step + 1) * src->step;
+                    dst->vf.never = 0;
                 }
             }
             step++;
@@ -5087,9 +5125,11 @@ int vdef_calc(
         if (step == steps) {    /* all entries were NaN */
             dst->vf.val = DNAN;
             dst->vf.when = 0;
+            dst->vf.never = 1;
         } else {
             dst->vf.val = data[step * src->ds_cnt];
             dst->vf.when = src->start + step * src->step;
+            dst->vf.never = 0;
         }
         break;
     case VDEF_LAST:
@@ -5103,9 +5143,11 @@ int vdef_calc(
         if (step < 0) { /* all entries were NaN */
             dst->vf.val = DNAN;
             dst->vf.when = 0;
+            dst->vf.never = 1;
         } else {
             dst->vf.val = data[step * src->ds_cnt];
             dst->vf.when = src->start + (step + 1) * src->step;
+            dst->vf.never = 0;
         }
         break;
     case VDEF_LSLSLOPE:
@@ -5143,16 +5185,20 @@ int vdef_calc(
             if (dst->vf.op == VDEF_LSLSLOPE) {
                 dst->vf.val = slope;
                 dst->vf.when = 0;
+                dst->vf.never = 1;
             } else if (dst->vf.op == VDEF_LSLINT) {
                 dst->vf.val = y_intercept;
                 dst->vf.when = 0;
+                dst->vf.never = 1;
             } else if (dst->vf.op == VDEF_LSLCORREL) {
                 dst->vf.val = correl;
                 dst->vf.when = 0;
+                dst->vf.never = 1;
             };
         } else {
             dst->vf.val = DNAN;
             dst->vf.when = 0;
+            dst->vf.never = 1;
         }
     }
         break;
@@ -5201,3 +5247,156 @@ void grinfo_push(
         im->grinfo = im->grinfo_current;
     }
 }
+
+
+void time_clean(
+    char *result,
+    char *format)
+{
+    int       j, jj;
+    
+/*     Handling based on
+       - ANSI C99 Specifications                         http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf
+       - Single UNIX Specification version 2             http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html 
+       - POSIX:2001/Single UNIX Specification version 3  http://www.opengroup.org/onlinepubs/009695399/functions/strftime.html
+       - POSIX:2008 Specifications                       http://www.opengroup.org/onlinepubs/9699919799/functions/strftime.html
+       Specifications tells 
+       "If a conversion specifier is not one of the above, the behavior is undefined."
+
+      C99 tells
+       "A conversion specifier consists of a % character, possibly followed by an E or O modifier character (described below), followed by a character that determines the behavior of the conversion specifier.
+
+      POSIX:2001 tells
+      "A conversion specification consists of a '%' character, possibly followed by an E or O modifier, and a terminating conversion specifier character that determines the conversion specification's behavior."
+
+      POSIX:2008 introduce more complexe behavior that are not handled here.
+
+      According to this, this code will replace:
+      - % followed by @ by a %@
+      - % followed by   by a %SPACE
+      - % followed by . by a %.
+      - % followed by % by a %
+      - % followed by t by a TAB
+      - % followed by E then anything by '-'
+      - % followed by O then anything by '-'
+      - % followed by anything else by at least one '-'. More characters may be added to better fit expected output length
+*/
+
+    jj = 0;
+    for(j = 0; (j < FMT_LEG_LEN - 1) && (jj < FMT_LEG_LEN); j++) { /* we don't need to parse the last char */
+        if (format[j] == '%') {
+            if ((format[j+1] == 'E') || (format[j+1] == 'O')) {
+                result[jj++] = '-';
+                j+=2; /* We skip next 2 following char */
+            } else if ((format[j+1] == 'C') || (format[j+1] == 'd') ||
+                       (format[j+1] == 'g') || (format[j+1] == 'H') ||
+                       (format[j+1] == 'I') || (format[j+1] == 'm') ||
+                       (format[j+1] == 'M') || (format[j+1] == 'S') ||
+                       (format[j+1] == 'U') || (format[j+1] == 'V') ||
+                       (format[j+1] == 'W') || (format[j+1] == 'y')) {
+                result[jj++] = '-';
+                if (jj < FMT_LEG_LEN) {
+                    result[jj++] = '-';
+                }
+                j++; /* We skip the following char */
+            } else if (format[j+1] == 'j') {
+                result[jj++] = '-';
+                if (jj < FMT_LEG_LEN - 1) {
+                    result[jj++] = '-';
+                    result[jj++] = '-';
+               }
+                j++; /* We skip the following char */
+            } else if ((format[j+1] == 'G') || (format[j+1] == 'Y')) {
+                /* Assuming Year on 4 digit */
+                result[jj++] = '-';
+                if (jj < FMT_LEG_LEN - 2) {
+                    result[jj++] = '-';
+                    result[jj++] = '-';
+                    result[jj++] = '-';
+                }
+                j++; /* We skip the following char */
+            } else if (format[j+1] == 'R') {
+                result[jj++] = '-';
+                if (jj < FMT_LEG_LEN - 3) {
+                    result[jj++] = '-';
+                    result[jj++] = ':';
+                    result[jj++] = '-';
+                    result[jj++] = '-';
+                }
+                j++; /* We skip the following char */
+            } else if (format[j+1] == 'T') {
+                result[jj++] = '-';
+                if (jj < FMT_LEG_LEN - 6) {
+                    result[jj++] = '-';
+                    result[jj++] = ':';
+                    result[jj++] = '-';
+                    result[jj++] = '-';
+                    result[jj++] = ':';
+                    result[jj++] = '-';
+                    result[jj++] = '-';
+                }
+                j++; /* We skip the following char */
+            } else if (format[j+1] == 'F') {
+                result[jj++] = '-';
+                if (jj < FMT_LEG_LEN - 8) {
+                    result[jj++] = '-';
+                    result[jj++] = '-';
+                    result[jj++] = '-';
+                    result[jj++] = '-';
+                    result[jj++] = '-';
+                    result[jj++] = '-';
+                    result[jj++] = '-';
+                    result[jj++] = '-';
+                    result[jj++] = '-';
+                }
+                j++; /* We skip the following char */
+            } else if (format[j+1] == 'D') {
+                result[jj++] = '-';
+                if (jj < FMT_LEG_LEN - 6) {
+                    result[jj++] = '-';
+                    result[jj++] = '/';
+                    result[jj++] = '-';
+                    result[jj++] = '-';
+                    result[jj++] = '/';
+                    result[jj++] = '-';
+                    result[jj++] = '-';
+                }
+                j++; /* We skip the following char */
+            } else if (format[j+1] == 'n') {
+                result[jj++] = '\r';
+                result[jj++] = '\n';
+                j++; /* We skip the following char */
+            } else if (format[j+1] == 't') {
+                result[jj++] = '\t';
+                j++; /* We skip the following char */
+            } else if (format[j+1] == '%') {
+                result[jj++] = '%';
+                j++; /* We skip the following char */
+            } else if (format[j+1] == ' ') {
+                if (jj < FMT_LEG_LEN - 1) {
+                    result[jj++] = '%';
+                    result[jj++] = ' ';
+                }
+                j++; /* We skip the following char */
+            } else if (format[j+1] == '.') {
+                if (jj < FMT_LEG_LEN - 1) {
+                    result[jj++] = '%';
+                    result[jj++] = '.';
+                }
+                j++; /* We skip the following char */
+            } else if (format[j+1] == '@') {
+                if (jj < FMT_LEG_LEN - 1) {
+                    result[jj++] = '%';
+                    result[jj++] = '@';
+                }
+                j++; /* We skip the following char */
+            } else {
+                result[jj++] = '-';
+                j++; /* We skip the following char */
+            }
+        } else {
+                result[jj++] = format[j];
+        }
+    }
+    result[jj] = '\0'; /* We must force the end of the string */
+}