added a nop control for string formating, so that strings ending in /x are possible...
[rrdtool.git] / src / rrd_graph.c
index d15ce3d..8417291 100644 (file)
@@ -59,6 +59,8 @@ text_prop_t text_prop[] = {
     {5.5, RRD_DEFAULT_FONT,NULL} /* watermark */
 };
 
+char week_fmt[128] = "Week %V";
+
 xlab_t    xlab[] = {
     {0, 0, TMT_SECOND, 30, TMT_MINUTE, 5, TMT_MINUTE, 5, 0, "%H:%M"}
     ,
@@ -87,10 +89,9 @@ xlab_t    xlab[] = {
     ,
     {2400, 0, TMT_HOUR, 12, TMT_DAY, 1, TMT_DAY, 2, 24 * 3600, "%a"}
     ,
-    {3600, 0, TMT_DAY, 1, TMT_WEEK, 1, TMT_WEEK, 1, 7 * 24 * 3600, "Week %V"}
+    {3600, 0, TMT_DAY, 1, TMT_WEEK, 1, TMT_WEEK, 1, 7 * 24 * 3600, week_fmt}
     ,
-    {3 * 3600, 0, TMT_WEEK, 1, TMT_MONTH, 1, TMT_WEEK, 2, 7 * 24 * 3600,
-     "Week %V"}
+    {3 * 3600, 0, TMT_WEEK, 1, TMT_MONTH, 1, TMT_WEEK, 2, 7 * 24 * 3600, week_fmt}
     ,
     {6 * 3600, 0, TMT_MONTH, 1, TMT_MONTH, 1, TMT_MONTH, 1, 30 * 24 * 3600,
      "%b"}
@@ -411,8 +412,11 @@ void auto_scale(
     }
 }
 
+/* power prefixes */
 
 static char si_symbol[] = {
+    'y',                /* 10e-24 Yocto */
+    'z',                /* 10e-21 Zepto */
     'a',                /* 10e-18 Atto */
     'f',                /* 10e-15 Femto */
     'p',                /* 10e-12 Pico */
@@ -426,8 +430,10 @@ static char si_symbol[] = {
     'T',                /* 10e12  Tera */
     'P',                /* 10e15  Peta */
     'E',                /* 10e18  Exa */
+    'Z',                /* 10e21  Zeta */
+    'Y'                 /* 10e24  Yotta */
 };
-static const int si_symbcenter = 6;
+static const int si_symbcenter = 8;
 
 /* find SI magnitude symbol for the numbers on the y-axis*/
 void si_unit(
@@ -894,11 +900,15 @@ int data_fetch(
                                 &im->gdes[i].ds_cnt,
                                 &im->gdes[i].ds_namv,
                                 &im->gdes[i].data)) == -1) {
-                    return -1;
-                }
+                    return -1;                      
+                }               
             }
             im->gdes[i].data_first = 1;
 
+            /* must reduce to at least im->step
+               otherwhise we end up with more data than we can handle in the 
+               chart and visibility of data will be random */            
+            im->gdes[i].step = max(im->gdes[i].step,im->step);
             if (ft_step < im->gdes[i].step) {
                 reduce_data(im->gdes[i].cf_reduce,
                             ft_step,
@@ -1470,6 +1480,13 @@ time_t find_next_time(
 
     localtime_r(&current, &tm);
 
+    int limit = 2;
+    switch (baseint) {
+    case TMT_SECOND: limit = 7200; break;
+    case TMT_MINUTE: limit = 120; break;
+    case TMT_HOUR: limit = 2; break;
+    default: limit = 2; break;
+    }
     do {
         switch (baseint) {
         case TMT_SECOND:
@@ -1500,7 +1517,7 @@ time_t find_next_time(
             tm.       tm_year += basestep;
         }
         madetime = mktime(&tm);
-    } while (madetime == -1);   /* this is necessary to skip impssible times
+    } while (madetime == -1 && limit-- >= 0);   /* this is necessary to skip impossible times
                                    like the daylight saving time skips */
     return madetime;
 
@@ -1582,7 +1599,7 @@ int print_calc(
                 }
             }           /* prepare printval */
 
-            if ((percent_s = strstr(im->gdes[i].format, "%S")) != NULL) {
+            if (!im->gdes[i].strftm && (percent_s = strstr(im->gdes[i].format, "%S")) != NULL) {
                 /* Magfact is set to -1 upon entry to print_calc.  If it
                  * is still less than 0, then we need to run auto_scale.
                  * Otherwise, put the value into the correct units.  If
@@ -1596,7 +1613,7 @@ int print_calc(
                     printval /= magfact;
                 }
                 *(++percent_s) = 's';
-            } else if (strstr(im->gdes[i].format, "%s") != NULL) {
+            } else if (!im->gdes[i].strftm && strstr(im->gdes[i].format, "%s") != NULL) {
                 auto_scale(im, &printval, &si_symb, &magfact);
             }
 
@@ -1773,6 +1790,7 @@ int leg_place(
                 prt_fctn != 'j' &&
                 prt_fctn != 'c' &&
                 prt_fctn != 'u' &&
+                prt_fctn != '.' &&
                 prt_fctn != 's' && prt_fctn != '\0' && prt_fctn != 'g') {
                 free(legspace);
                 rrd_set_error
@@ -1784,6 +1802,10 @@ int leg_place(
             if (prt_fctn == 'n') {
                 prt_fctn = 'l';
             }
+            /* \. is a null operation to allow strings ending in \x */
+            if (prt_fctn == '.') {
+                prt_fctn = '\0';
+            }
 
             /* remove exess space from the end of the legend for \g */
             while (prt_fctn == 'g' &&
@@ -1859,7 +1881,7 @@ int leg_place(
                     glue = 0;
                 }
                 if (prt_fctn == 'c')
-                    leg_x = (double)(legendwidth - fill) / 2.0;
+                    leg_x = border + (double)(legendwidth - fill) / 2.0;
                 if (prt_fctn == 'r')
                     leg_x = legendwidth - fill + border;
                 for (ii = mark; ii <= i; ii++) {
@@ -2506,19 +2528,20 @@ void vertical_grid(
                              mgridtm,
                              im->xlab_user.
                              mgridst);
-             ti < im->end;
+             ti < im->end && ti != -1;
              ti =
              find_next_time(ti, im->xlab_user.gridtm, im->xlab_user.gridst)
             ) {
             /* are we inside the graph ? */
             if (ti < im->start || ti > im->end)
                 continue;
-            while (timajor < ti) {
+            while (timajor < ti && timajor != -1) {
                 timajor = find_next_time(timajor,
                                          im->
                                          xlab_user.
                                          mgridtm, im->xlab_user.mgridst);
             }
+            if (timajor == -1) break; /* fail in case of problems with time increments */
             if (ti == timajor)
                 continue;   /* skip as falls on major grid line */
             X0 = xtr(im, ti);
@@ -2542,7 +2565,7 @@ void vertical_grid(
                               im->
                               xlab_user.
                               mgridst);
-         ti < im->end;
+         ti < im->end && ti != -1;
          ti = find_next_time(ti, im->xlab_user.mgridtm, im->xlab_user.mgridst)
         ) {
         /* are we inside the graph ? */
@@ -2568,9 +2591,9 @@ void vertical_grid(
                          labtm,
                          im->xlab_user.
                          labst);
-         ti <=
+         (ti <=
          im->end -
-         im->xlab_user.precis / 2;
+         im->xlab_user.precis / 2) && ti != -1;
          ti = find_next_time(ti, im->xlab_user.labtm, im->xlab_user.labst)
         ) {
         tilab = ti + im->xlab_user.precis / 2;  /* correct time for the label */
@@ -2929,6 +2952,7 @@ int graph_size_location(
         im->ximg = im->xsize;
         im->yimg = im->ysize;
         im->yorigin = im->ysize;
+        xtr(im, 0);
         ytr(im, DNAN);
         return 0;
     }
@@ -2960,7 +2984,7 @@ int graph_size_location(
     }
     else{
         // we have no title; get a little clearing from the top
-        Ytitle = 1.5 * Yspacing;
+        Ytitle = Yspacing;
     }
 
     if (elements) {
@@ -3054,7 +3078,7 @@ int graph_size_location(
 
             /* reserve space for padding below the graph */
         if (im->extra_flags & NOLEGEND) {
-            Ymain -= Yspacing;
+            Ymain -= 0.5*Yspacing;
         }
 
         if (im->watermark[0] != '\0') {
@@ -3114,7 +3138,7 @@ int graph_size_location(
         }
         /* reserve space for padding below the graph */
         if (im->extra_flags & NOLEGEND) {
-            im->yimg += Yspacing;
+            im->yimg += 0.5*Yspacing;
         }
 
         if (im->watermark[0] != '\0') {
@@ -3143,7 +3167,7 @@ int graph_size_location(
      */
     switch(im->legendposition){
         case NORTH:
-            im->xOriginTitle   = Xvertical + Xylabel + (im->xsize / 2);
+            im->xOriginTitle   = (im->ximg / 2);
             im->yOriginTitle   = 0;
 
             im->xOriginLegend  = 0;
@@ -3164,7 +3188,7 @@ int graph_size_location(
             break;
 
         case WEST:
-            im->xOriginTitle   = im->legendwidth + Xvertical + Xylabel + im->xsize / 2;
+            im->xOriginTitle   = im->legendwidth + im->xsize / 2;
             im->yOriginTitle   = 0;
 
             im->xOriginLegend  = 0;
@@ -3185,7 +3209,7 @@ int graph_size_location(
             break;
 
         case SOUTH:
-            im->xOriginTitle   = Xvertical + Xylabel + im->xsize / 2;
+            im->xOriginTitle   = im->ximg / 2;
             im->yOriginTitle   = 0;
 
             im->xOriginLegend  = 0;
@@ -3206,7 +3230,7 @@ int graph_size_location(
             break;
 
         case EAST:
-            im->xOriginTitle   = Xvertical + Xylabel + im->xsize / 2;
+            im->xOriginTitle   = im->xsize / 2;
             im->yOriginTitle   = 0;
 
             im->xOriginLegend  = Xvertical + Xylabel + Xmain + Xvertical2;
@@ -3448,17 +3472,27 @@ int graph_paint(
             break;
         case GF_LINE:
         case GF_AREA:
-               case GF_GRAD:
-            /* fix data points at oo and -oo */
+        case GF_GRAD: {
+            rrd_value_t diffval = im->maxval - im->minval;
+            rrd_value_t maxlimit = im->maxval + 9 * diffval;
+            rrd_value_t minlimit = im->minval - 9 * diffval;        
             for (ii = 0; ii < im->xsize; ii++) {
+                /* fix data points at oo and -oo */
                 if (isinf(im->gdes[i].p_data[ii])) {
                     if (im->gdes[i].p_data[ii] > 0) {
                         im->gdes[i].p_data[ii] = im->maxval;
                     } else {
                         im->gdes[i].p_data[ii] = im->minval;
                     }
-
                 }
+                /* some versions of cairo go unstable when trying
+                   to draw way out of the canvas ... lets not even try */
+               if (im->gdes[i].p_data[ii] > maxlimit) {
+                   im->gdes[i].p_data[ii] = maxlimit;
+               }
+               if (im->gdes[i].p_data[ii] < minlimit) {
+                   im->gdes[i].p_data[ii] = minlimit;
+               }
             }           /* for */
 
             /* *******************************************************
@@ -3726,6 +3760,7 @@ int graph_paint(
             }
             lastgdes = &(im->gdes[i]);
             break;
+        } /* GF_AREA, GF_LINE, GF_GRAD */
         case GF_STACK:
             rrd_set_error
                 ("STACK should already be turned into LINE or AREA here");
@@ -3991,15 +4026,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");
@@ -4007,6 +4045,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);
@@ -4129,6 +4168,7 @@ void rrd_graph_init(
     im->maxval = DNAN;
     im->minval = 0;
     im->minval = DNAN;
+    im->magfact = 1;
     im->prt_c = 0;
     im->rigid = 0;
     im->rendered_image_size = 0;
@@ -4278,6 +4318,7 @@ void rrd_graph_options(
         { "border",             required_argument, 0, 1007},
         { "grid-dash",          required_argument, 0, 1008},
         { "dynamic-labels",     no_argument,       0, 1009},
+        { "week-fmt",           required_argument, 0, 1010},
         {  0, 0, 0, 0}
 };
 /* *INDENT-ON* */
@@ -4463,6 +4504,10 @@ void rrd_graph_options(
         case 1009: /* enable dynamic labels */
             im->dynamic_labels = 1;
             break;         
+        case 1010:
+            strncpy(week_fmt,optarg,sizeof week_fmt);
+            week_fmt[(sizeof week_fmt)-1]='\0';
+            break;
         case 1002: /* right y axis */
 
             if(sscanf(optarg,