Code

control: Updated standards-version to 3.9.5 -- no changes.
[pkg-rrdtool.git] / src / rrd_graph.c
index eeff31c41ca33db51c06eb9737ac97a7ca156c9c..d783a358609d68465555b4e31ff6a10485a9ab21 100644 (file)
@@ -1,5 +1,5 @@
 /****************************************************************************
- * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ * RRDtool 1.4.8  Copyright by Tobi Oetiker, 1997-2013
  ****************************************************************************
  * rrd__graph.c  produce graphs from data in rrdfiles
  ****************************************************************************/
@@ -89,8 +89,7 @@ xlab_t    xlab[] = {
     ,
     {3600, 0, TMT_DAY, 1, TMT_WEEK, 1, TMT_WEEK, 1, 7 * 24 * 3600, "Week %V"}
     ,
-    {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 %V"}
     ,
     {6 * 3600, 0, TMT_MONTH, 1, TMT_MONTH, 1, TMT_MONTH, 1, 30 * 24 * 3600,
      "%b"}
@@ -318,6 +317,14 @@ int im_free(
     if (im->daemon_addr != NULL)
       free(im->daemon_addr);
 
+    if (im->gdef_map){
+        g_hash_table_destroy(im->gdef_map);        
+    }
+
+    if (im->rrd_map){
+        g_hash_table_destroy(im->rrd_map);        
+    }
+
     for (i = 0; i < (unsigned) im->gdes_c; i++) {
         if (im->gdes[i].data_first) {
             /* careful here, because a single pointer can occur several times */
@@ -336,6 +343,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);
 
@@ -343,6 +356,8 @@ int im_free(
         status = cairo_status(im->cr);
         cairo_destroy(im->cr);
     }
+
+
     if (im->rendered_image) {
         free(im->rendered_image);
     }
@@ -402,8 +417,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 */
@@ -417,8 +435,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(
@@ -809,7 +829,6 @@ int data_fetch(
     image_desc_t *im)
 {
     int       i, ii;
-    int       skip;
 
     /* pull the data from the rrd files ... */
     for (i = 0; i < (int) im->gdes_c; i++) {
@@ -817,33 +836,21 @@ int data_fetch(
         if (im->gdes[i].gf != GF_DEF)
             continue;
 
-        skip = 0;
         /* do we have it already ? */
-        for (ii = 0; ii < i; ii++) {
-            if (im->gdes[ii].gf != GF_DEF)
-                continue;
-            if ((strcmp(im->gdes[i].rrd, im->gdes[ii].rrd) == 0)
-                && (im->gdes[i].cf == im->gdes[ii].cf)
-                && (im->gdes[i].cf_reduce == im->gdes[ii].cf_reduce)
-                && (im->gdes[i].start_orig == im->gdes[ii].start_orig)
-                && (im->gdes[i].end_orig == im->gdes[ii].end_orig)
-                && (im->gdes[i].step_orig == im->gdes[ii].step_orig)) {
-                /* OK, the data is already there.
-                 ** Just copy the header portion
-                 */
-                im->gdes[i].start = im->gdes[ii].start;
-                im->gdes[i].end = im->gdes[ii].end;
-                im->gdes[i].step = im->gdes[ii].step;
-                im->gdes[i].ds_cnt = im->gdes[ii].ds_cnt;
-                im->gdes[i].ds_namv = im->gdes[ii].ds_namv;
-                im->gdes[i].data = im->gdes[ii].data;
-                im->gdes[i].data_first = 0;
-                skip = 1;
-            }
-            if (skip)
-                break;
-        }
-        if (!skip) {
+        gpointer value;
+        char *key = gdes_fetch_key(im->gdes[i]);
+        gboolean ok = g_hash_table_lookup_extended(im->rrd_map,key,NULL,&value);
+        free(key);
+        if (ok){
+            ii = GPOINTER_TO_INT(value);
+            im->gdes[i].start = im->gdes[ii].start;
+            im->gdes[i].end = im->gdes[ii].end;
+            im->gdes[i].step = im->gdes[ii].step;
+            im->gdes[i].ds_cnt = im->gdes[ii].ds_cnt;
+            im->gdes[i].ds_namv = im->gdes[ii].ds_namv;
+            im->gdes[i].data = im->gdes[ii].data;
+            im->gdes[i].data_first = 0;
+        } else {
             unsigned long ft_step = im->gdes[i].step;   /* ft_step will record what we got from fetch */
 
             /* Flush the file if
@@ -888,6 +895,10 @@ int data_fetch(
             }
             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,
@@ -934,16 +945,16 @@ long find_var(
     image_desc_t *im,
     char *key)
 {
-    long      ii;
-
-    for (ii = 0; ii < im->gdes_c - 1; ii++) {
-        if ((im->gdes[ii].gf == GF_DEF
-             || im->gdes[ii].gf == GF_VDEF || im->gdes[ii].gf == GF_CDEF)
-            && (strcmp(im->gdes[ii].vname, key) == 0)) {
-            return ii;
-        }
+    long match = -1;
+    gpointer value;
+    gboolean ok = g_hash_table_lookup_extended(im->gdef_map,key,NULL,&value);
+    if (ok){
+        match = GPOINTER_TO_INT(value);
     }
-    return -1;
+
+    /* printf("%s -> %ld\n",key,match); */
+
+    return match;    
 }
 
 /* find the greatest common divisor for all the numbers
@@ -1274,7 +1285,7 @@ int data_proc(
                     /* GF_TICK: the data values are not
                      ** relevant for min and max
                      */
-                    if (finite(paintval) && im->gdes[ii].gf != GF_TICK) {
+                    if (finite(paintval) && im->gdes[ii].gf != GF_TICK && !im->gdes[ii].skipscale) {
                         if ((isnan(minval) || paintval < minval) &&
                             !(im->logarithmic && paintval <= 0.0))
                             minval = paintval;
@@ -1364,10 +1375,18 @@ static int find_first_weekday(void){
     if (first_weekday == -1){
 #ifdef HAVE__NL_TIME_WEEK_1STDAY
         /* according to http://sourceware.org/ml/libc-locales/2009-q1/msg00011.html */
+        /* See correct way here http://pasky.or.cz/dev/glibc/first_weekday.c */
+        first_weekday = nl_langinfo (_NL_TIME_FIRST_WEEKDAY)[0];
+        int week_1stday;
         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 */
+        if (week_1stday_l == 19971130) week_1stday = 0; /* Sun */
+        else if (week_1stday_l == 19971201) week_1stday = 1; /* Mon */
+        else
+        {
+            first_weekday = 1;
+            return first_weekday; /* we go for a monday default */
+        }
+        first_weekday=(week_1stday + first_weekday - 1) % 7;
 #else
         first_weekday = 1;
 #endif
@@ -1386,6 +1405,8 @@ time_t find_first_time(
     struct tm tm;
 
     localtime_r(&start, &tm);
+    /* let mktime figure this dst on its own */
+    tm.tm_isdst = -1;
 
     switch (baseint) {
     case TMT_SECOND:
@@ -1454,7 +1475,16 @@ time_t find_next_time(
     time_t    madetime;
 
     localtime_r(&current, &tm);
+    /* let mktime figure this dst on its own */
+    tm.tm_isdst = -1;
 
+    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:
@@ -1485,7 +1515,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;
 
@@ -1567,7 +1597,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
@@ -1581,7 +1611,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);
             }
 
@@ -1682,7 +1712,6 @@ int leg_place(
     int       leg_c = 0;
     double    leg_x = border;
     int       leg_y = 0; //im->yimg;
-    int       leg_y_prev = 0; // im->yimg;
     int       leg_cc;
     double    glue = 0;
     int       i, ii, mark = 0;
@@ -1749,6 +1778,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
@@ -1760,6 +1790,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' &&
@@ -1835,7 +1869,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++) {
@@ -1853,7 +1887,6 @@ int leg_place(
                         +(double)legspace[ii]
                         + glue;
                 }
-                leg_y_prev = leg_y;
                 if (leg_x > border || prt_fctn == 's')
                     leg_y += im->text_prop[TEXT_PROP_LEGEND].size * 1.8;
                 if (prt_fctn == 's')
@@ -2072,7 +2105,7 @@ int draw_horizontal_grid(
                             }
                         }
                         else {
-                           sprintf(graph_label_right,im->second_axis_format,sval);
+                           sprintf(graph_label_right,im->second_axis_format,sval,"");
                         }
                         gfx_text ( im,
                                X1+7, Y0,
@@ -2277,7 +2310,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,
@@ -2482,19 +2515,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);
@@ -2518,7 +2552,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 ? */
@@ -2544,9 +2578,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 */
@@ -2905,6 +2939,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;
     }
@@ -2936,7 +2971,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) {
@@ -3030,7 +3065,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') {
@@ -3090,7 +3125,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') {
@@ -3119,7 +3154,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;
@@ -3140,7 +3175,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;
@@ -3161,7 +3196,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;
@@ -3182,7 +3217,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;
@@ -3423,16 +3458,26 @@ int graph_paint(
             }
             break;
         case GF_LINE:
-        case GF_AREA:
-            /* fix data points at oo and -oo */
+        case GF_AREA: {
+            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 */
 
@@ -3659,6 +3704,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");
@@ -3769,6 +3815,7 @@ int gdes_alloc(
     im->gdes[im->gdes_c - 1].step = im->step;
     im->gdes[im->gdes_c - 1].step_orig = im->step;
     im->gdes[im->gdes_c - 1].stack = 0;
+    im->gdes[im->gdes_c - 1].skipscale = 0;
     im->gdes[im->gdes_c - 1].linewidth = 0;
     im->gdes[im->gdes_c - 1].debug = 0;
     im->gdes[im->gdes_c - 1].start = im->start;
@@ -3918,7 +3965,8 @@ 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()) {
         rrd_info_free(im.grinfo);
@@ -4003,6 +4051,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){
@@ -4025,7 +4075,8 @@ void rrd_graph_init(
 #ifdef HAVE_TZSET
     tzset();
 #endif
-
+    im->gdef_map = g_hash_table_new_full(g_str_hash, g_str_equal,g_free,NULL);
+    im->rrd_map = g_hash_table_new_full(g_str_hash, g_str_equal,g_free,NULL);
     im->base = 1000;
     im->daemon_addr = NULL;
     im->draw_x_grid = 1;
@@ -4054,6 +4105,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;
@@ -4096,6 +4148,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);
     }
 
@@ -4110,6 +4163,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);