diff --git a/src/rrd_graph.c b/src/rrd_graph.c
index 2df1835d1b4764e25b4d2bbd0f7d25a0c6bfa184..5f70a3847b7ffd367699980f8dfd8eedc6672f7b 100644 (file)
--- a/src/rrd_graph.c
+++ b/src/rrd_graph.c
/****************************************************************************
- * RRDtool 1.3.2 Copyright by Tobi Oetiker, 1997-2008
+ * RRDtool 1.4.7 Copyright by Tobi Oetiker, 1997-2012
****************************************************************************
* rrd__graph.c produce graphs from data in rrdfiles
****************************************************************************/
#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"
,
{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"}
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);
status = cairo_status(im->cr);
cairo_destroy(im->cr);
}
+
+
if (im->rendered_image) {
free(im->rendered_image);
}
}
}
+/* 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 */
'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(
}
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,
} 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)
return 0;
}
-
+static int find_first_weekday(void){
+ static int first_weekday = -1;
+ if (first_weekday == -1){
+#ifdef HAVE__NL_TIME_WEEK_1STDAY
+ /* 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 */
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:
localtime_r(¤t, &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:
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;
}
} /* 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
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);
}
prt_fctn != 'r' &&
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
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' &&
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;
+ leg_x = legendwidth - fill + border;
for (ii = mark; ii <= i; ii++) {
if (im->gdes[ii].legend[0] == '\0')
continue; /* skip empty legends */
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;
}
}
else {
- sprintf(graph_label_right,im->second_axis_format,sval);
+ sprintf(graph_label_right,im->second_axis_format,sval,"");
}
gfx_text ( im,
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);
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 ? */
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 */
boxV = boxH;
/* shift the box up a bit */
Y0 -= boxV * 0.4;
- if (im->gdes[i].gf == GF_HRULE) { /* [-] */
+
+ 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);
X0 + boxH, Y0 - boxV / 2,
1.0, im->gdes[i].col);
gfx_close_path(im);
- } else if (im->gdes[i].gf == GF_VRULE) { /* [|] */
+ } 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);
X0 + boxH / 2, Y0 - boxV,
1.0, im->gdes[i].col);
gfx_close_path(im);
- } else if (im->gdes[i].gf == GF_LINE) { /* [/] */
+ } 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);
im->ximg = im->xsize;
im->yimg = im->ysize;
im->yorigin = im->ysize;
+ xtr(im, 0);
ytr(im, DNAN);
return 0;
}
}
else{
// we have no title; get a little clearing from the top
- Ytitle = 1.5 * Yspacing;
+ Ytitle = Yspacing;
}
if (elements) {
/* reserve space for padding below the graph */
if (im->extra_flags & NOLEGEND) {
- Ymain -= Yspacing;
+ Ymain -= 0.5*Yspacing;
}
if (im->watermark[0] != '\0') {
}
/* 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') {
*/
switch(im->legendposition){
case NORTH:
- im->xOriginTitle = Xvertical + Xylabel + (im->xsize / 2);
+ im->xOriginTitle = (im->ximg / 2);
im->yOriginTitle = 0;
im->xOriginLegend = 0;
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;
break;
case SOUTH:
- im->xOriginTitle = Xvertical + Xylabel + im->xsize / 2;
+ im->xOriginTitle = im->ximg / 2;
im->yOriginTitle = 0;
im->xOriginLegend = 0;
break;
case EAST:
- im->xOriginTitle = Xvertical + Xylabel + im->xsize / 2;
+ im->xOriginTitle = im->xsize / 2;
im->yOriginTitle = 0;
im->xOriginLegend = Xvertical + Xylabel + Xmain + Xvertical2;
}
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 */
}
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");
{
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, NULL);
+ setlocale(LC_NUMERIC, "C");
rrd_graph_options(argc, argv, &im);
if (rrd_test_error()) {
rrd_info_free(im.grinfo);
}
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);
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){
#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;
im->maxval = DNAN;
im->minval = 0;
im->minval = DNAN;
+ im->magfact = 1;
im->prt_c = 0;
im->rigid = 0;
im->rendered_image_size = 0;
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);
}
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);
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 */
{ "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* */
int col_start, col_end;
opt = getopt_long(argc, argv,
- "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",
+ "Aa:B:b:c:Dd:Ee:Ff:G:gh:IiJjL:l:Mm:Nn:oPR:rS:s:T:t:u:v:W:w:X:x:Yy:z",
long_options, &option_index);
if (opt == EOF)
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)
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;
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;
return;
}
} else {
- setlocale(LC_NUMERIC, old_locale);
rrd_set_error("invalid y-grid format");
return;
}
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,
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);
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++) {
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;
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", ¶m, func, &n);
- setlocale(LC_NUMERIC, old_locale);
if (n == (int) strlen(str)) { /* matched */
;
} else {