/****************************************************************************
- * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ * RRDtool 1.2.99907080300 Copyright by Tobi Oetiker, 1997-2007
****************************************************************************
* rrd__graph.c produce graphs from data in rrdfiles
****************************************************************************/
,
{60, 0, TMT_MINUTE, 30, TMT_HOUR, 2, TMT_HOUR, 2, 0, "%H:%M"}
,
- {60, 24 * 3600, TMT_MINUTE, 30, TMT_HOUR, 2, TMT_HOUR, 4, 0, "%a %H:%M"}
+ {60, 24 * 3600, TMT_MINUTE, 30, TMT_HOUR, 2, TMT_HOUR, 6, 0, "%a %H:%M"}
,
{180, 0, TMT_HOUR, 1, TMT_HOUR, 6, TMT_HOUR, 6, 0, "%H:%M"}
,
conv_if(AREA, GF_AREA);
conv_if(STACK, GF_STACK);
conv_if(TICK, GF_TICK);
+ conv_if(TEXTALIGN, GF_TEXTALIGN);
conv_if(DEF, GF_DEF);
conv_if(CDEF, GF_CDEF);
conv_if(VDEF, GF_VDEF);
image_desc_t *im)
{
unsigned long i, ii;
+ cairo_status_t status = 0;
if (im == NULL)
return 0;
free(im->gdes[i].ds_namv);
}
}
+ /* free allocated memory used for dashed lines */
+ if (im->gdes[i].p_dashes != NULL)
+ free(im->gdes[i].p_dashes);
+
free(im->gdes[i].p_data);
free(im->gdes[i].rpnp);
}
free(im->gdes);
- if (im->surface)
- cairo_surface_destroy(im->surface);
if (im->font_options)
cairo_font_options_destroy(im->font_options);
+
+ if (im->cr) {
+ status = cairo_status(im->cr);
+ cairo_destroy(im->cr);
+ }
+ if (im->surface)
+ cairo_surface_destroy(im->surface);
+ if (status)
+ fprintf(stderr, "OOPS: Cairo has issues it can't even die: %s\n",
+ cairo_status_to_string(status));
+
return 0;
}
else {
switch (cf) {
case CF_HWPREDICT:
+ case CF_MHWPREDICT:
case CF_DEVSEASONAL:
case CF_DEVPREDICT:
case CF_SEASONAL:
} else {
switch (cf) {
case CF_HWPREDICT:
+ case CF_MHWPREDICT:
case CF_DEVSEASONAL:
case CF_DEVPREDICT:
case CF_SEASONAL:
return 0;
}
+static int AlmostEqual2sComplement(
+ float A,
+ float B,
+ int maxUlps)
+{
+
+ int aInt = *(int *) &A;
+ int bInt = *(int *) &B;
+ int intDiff;
+
+ /* Make sure maxUlps is non-negative and small enough that the
+ default NAN won't compare as equal to anything. */
+
+ /* assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); */
+
+ /* Make aInt lexicographically ordered as a twos-complement int */
+
+ if (aInt < 0)
+ aInt = 0x80000000l - aInt;
+
+ /* Make bInt lexicographically ordered as a twos-complement int */
+
+ if (bInt < 0)
+ bInt = 0x80000000l - bInt;
+
+ intDiff = abs(aInt - bInt);
+
+ if (intDiff <= maxUlps)
+ return 1;
+
+ return 0;
+}
+
/* massage data so, that we get one value for each x coordinate in the graph */
int data_proc(
image_desc_t *im)
}
/* adjust min and max values */
+ /* for logscale we add something on top */
if (isnan(im->minval)
- /* don't adjust low-end with log scale *//* why not? */
|| ((!im->rigid) && im->minval > minval)
) {
if (im->logarithmic)
else
im->maxval = maxval;
}
+
/* make sure min is smaller than max */
if (im->minval > im->maxval) {
- im->minval = 0.99 * im->maxval;
+ if (im->minval > 0)
+ im->minval = 0.99 * im->maxval;
+ else
+ im->minval = 1.01 * im->maxval;
}
/* make sure min and max are not equal */
- if (im->minval == im->maxval) {
- im->maxval *= 1.01;
- if (!im->logarithmic) {
- im->minval *= 0.99;
- }
+ if (AlmostEqual2sComplement(im->minval, im->maxval, 4)) {
+ if (im->maxval > 0)
+ im->maxval *= 1.01;
+ else
+ im->maxval *= 0.99;
+
/* make sure min and max are not both zero */
- if (im->maxval == 0.0) {
+ if (AlmostEqual2sComplement(im->maxval, 0, 4)) {
im->maxval = 1.0;
}
}
switch (im->gdes[i].cf) {
case CF_HWPREDICT:
+ case CF_MHWPREDICT:
case CF_DEVPREDICT:
case CF_DEVSEASONAL:
case CF_SEASONAL:
graphelement = 1;
break;
case GF_COMMENT:
+ case GF_TEXTALIGN:
case GF_DEF:
case GF_CDEF:
case GF_VDEF:
int glue = 0;
int i, ii, mark = 0;
char prt_fctn; /*special printfunctions */
+ char default_txtalign = TXA_JUSTIFIED; /*default line orientation */
int *legspace;
if (!(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH)) {
/* hide legends for rules which are not displayed */
+ if (im->gdes[i].gf == GF_TEXTALIGN) {
+ default_txtalign = im->gdes[i].txtalign;
+ }
+
if (!(im->extra_flags & FORCE_RULES_LEGEND)) {
if (im->gdes[i].gf == GF_HRULE &&
(im->gdes[i].yrule < im->minval
return -1;
}
-
- /* remove exess space */
+ /* \n -> \l */
if (prt_fctn == 'n') {
prt_fctn = 'l';
}
+ /* remove exess space from the end of the legend for \g */
while (prt_fctn == 'g' &&
leg_cc > 0 && im->gdes[i].legend[leg_cc - 1] == ' ') {
leg_cc--;
im->gdes[i].legend[leg_cc] = '\0';
}
+
if (leg_cc != 0) {
+
+ /* no interleg space if string ends in \g */
legspace[i] = (prt_fctn == 'g' ? 0 : interleg);
if (fill > 0) {
- /* no interleg space if string ends in \g */
fill += legspace[i];
}
fill += gfx_get_text_width(im, fill + border,
if (prt_fctn == 'g') {
prt_fctn = '\0';
}
- if (prt_fctn == '\0') {
- if (i == im->gdes_c - 1)
- prt_fctn = 'l';
+ if (prt_fctn == '\0') {
+ if (i == im->gdes_c - 1 || fill > im->ximg - 2 * border) {
+ /* just one legend item is left right or center */
+ switch (default_txtalign) {
+ case TXA_RIGHT:
+ prt_fctn = 'r';
+ break;
+ case TXA_CENTER:
+ prt_fctn = 'c';
+ break;
+ case TXA_JUSTIFIED:
+ prt_fctn = 'j';
+ break;
+ default:
+ prt_fctn = 'l';
+ break;
+ }
+ }
/* is it time to place the legends ? */
if (fill > im->ximg - 2 * border) {
if (leg_c > 1) {
i--;
fill = fill_last;
leg_c--;
- prt_fctn = 'j';
- } else {
- prt_fctn = 'l';
}
-
+ }
+ if (leg_c == 1 && prt_fctn == 'j') {
+ prt_fctn = 'l';
}
}
return mnt;
}
-static int AlmostEqual2sComplement(
- float A,
- float B,
- int maxUlps)
-{
-
- int aInt = *(int *) &A;
- int bInt = *(int *) &B;
- int intDiff;
-
- /* Make sure maxUlps is non-negative and small enough that the
- default NAN won't compare as equal to anything. */
-
- /* assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); */
-
- /* Make aInt lexicographically ordered as a twos-complement int */
-
- if (aInt < 0)
- aInt = 0x80000000l - aInt;
-
- /* Make bInt lexicographically ordered as a twos-complement int */
-
- if (bInt < 0)
- bInt = 0x80000000l - bInt;
-
- intDiff = abs(aInt - bInt);
-
- if (intDiff <= maxUlps)
- return 1;
+/* from http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm */
+/* yes we are loosing precision by doing tos with floats instead of doubles
+ but it seems more stable this way. */
- return 0;
-}
/* logaritmic horizontal grid */
int horizontal_log_grid(
/* arrow for X and Y axis direction */
- gfx_new_area(im, im->xorigin + im->xsize + 2, im->yorigin - 2, im->xorigin + im->xsize + 2, im->yorigin + 3, im->xorigin + im->xsize + 7, im->yorigin + 0.5, /* LINEOFFSET */
+
+ gfx_new_area(im, im->xorigin + im->xsize + 2, im->yorigin - 3, im->xorigin + im->xsize + 2, im->yorigin + 3, im->xorigin + im->xsize + 7, im->yorigin, /* horyzontal */
im->graph_col[GRC_ARROW]);
gfx_close_path(im);
- gfx_new_area(im, im->xorigin - 2, im->yorigin - im->ysize - 2, im->xorigin + 3, im->yorigin - im->ysize - 2, im->xorigin + 0.5, im->yorigin - im->ysize - 7, /* LINEOFFSET */
+ gfx_new_area(im, im->xorigin - 3, im->yorigin - im->ysize - 2, im->xorigin + 3, im->yorigin - im->ysize - 2, im->xorigin, im->yorigin - im->ysize - 7, /* vertical */
im->graph_col[GRC_ARROW]);
gfx_close_path(im);
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[] = { 3.0 };
+ cairo_set_dash(im->cr, dashes, 1, 0.0);
+ }
cairo_stroke(im->cr);
cairo_restore(im->cr);
}
return 0;
}
-/* from http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm */
-/* yes we are loosing precision by doing tos with floats instead of doubles
- but it seems more stable this way. */
+static cairo_status_t cairo_write_func_filehandle(
+ void *closure,
+ const unsigned char *data,
+ unsigned int length)
+{
+ if (fwrite(data, length, 1, closure) != 1)
+ return CAIRO_STATUS_WRITE_ERROR;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t cairo_copy_to_buffer(
+ void *closure,
+ const unsigned char *data,
+ unsigned int length)
+{
+ image_desc_t *im = closure;
+
+ im->rendered_image =
+ realloc(im->rendered_image, im->rendered_image_size + length);
+ if (im->rendered_image == NULL) {
+ return CAIRO_STATUS_WRITE_ERROR;
+ }
+
+ memcpy(im->rendered_image + im->rendered_image_size, data, length);
+
+ im->rendered_image_size += length;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
/* draw that picture thing ... */
int graph_paint(
image_desc_t *im,
im->yimg * im->zoom);
break;
case IF_PDF:
- im->surface =
- cairo_pdf_surface_create(im->graphfile, im->ximg * im->zoom,
- im->yimg * im->zoom);
+ im->gridfit = 0;
+ im->surface = strlen(im->graphfile)
+ ? cairo_pdf_surface_create(im->graphfile, im->ximg * im->zoom,
+ im->yimg * im->zoom)
+ : cairo_pdf_surface_create_for_stream(&cairo_copy_to_buffer, im,
+ im->ximg * im->zoom,
+ im->yimg * im->zoom);
break;
case IF_EPS:
- im->surface =
- cairo_ps_surface_create(im->graphfile, im->ximg * im->zoom,
- im->yimg * im->zoom);
+ im->gridfit = 0;
+ im->surface = strlen(im->graphfile)
+ ? cairo_ps_surface_create(im->graphfile, im->ximg * im->zoom,
+ im->yimg * im->zoom)
+ : cairo_ps_surface_create_for_stream(&cairo_copy_to_buffer, im,
+ im->ximg * im->zoom,
+ im->yimg * im->zoom);
break;
case IF_SVG:
- im->surface =
- cairo_svg_surface_create(im->graphfile, im->ximg * im->zoom,
- im->yimg * im->zoom);
+ im->gridfit = 0;
+ im->surface = strlen(im->graphfile)
+ ? cairo_svg_surface_create(im->graphfile, im->ximg * im->zoom,
+ im->yimg * im->zoom)
+ : cairo_svg_surface_create_for_stream(&cairo_copy_to_buffer, im,
+ im->ximg * im->zoom,
+ im->yimg * im->zoom);
cairo_svg_surface_restrict_to_version(im->surface,
CAIRO_SVG_VERSION_1_1);
break;
};
im->cr = cairo_create(im->surface);
- pango_cairo_font_map_set_resolution(PANGO_CAIRO_FONT_MAP(font_map), 100);
cairo_set_antialias(im->cr, im->graph_antialias);
cairo_scale(im->cr, im->zoom, im->zoom);
+ pango_cairo_font_map_set_resolution(PANGO_CAIRO_FONT_MAP(font_map), 100);
gfx_new_area(im,
0, 0,
case GF_PRINT:
case GF_GPRINT:
case GF_COMMENT:
+ case GF_TEXTALIGN:
case GF_HRULE:
case GF_VRULE:
case GF_XPORT:
cairo_new_path(im->cr);
cairo_set_line_width(im->cr, im->gdes[i].linewidth);
+
+ if (im->gdes[i].dash) {
+ cairo_set_dash(im->cr, im->gdes[i].p_dashes,
+ im->gdes[i].ndash, im->gdes[i].offset);
+ }
+
for (ii = 1; ii < im->xsize; ii++) {
if (isnan(im->gdes[i].p_data[ii])
|| (im->slopemode == 1
cairo_restore(im->cr);
} else {
int idxI = -1;
- double *foreY = malloc(sizeof(double) * im->xsize * 2);
- double *foreX = malloc(sizeof(double) * im->xsize * 2);
- double *backY = malloc(sizeof(double) * im->xsize * 2);
- double *backX = malloc(sizeof(double) * im->xsize * 2);
+ double *foreY =
+ (double *) malloc(sizeof(double) * im->xsize * 2);
+ double *foreX =
+ (double *) malloc(sizeof(double) * im->xsize * 2);
+ double *backY =
+ (double *) malloc(sizeof(double) * im->xsize * 2);
+ double *backX =
+ (double *) malloc(sizeof(double) * im->xsize * 2);
int drawem = 0;
for (ii = 0; ii <= im->xsize; ii++) {
case GF_HRULE:
if (im->gdes[i].yrule >= im->minval
&& im->gdes[i].yrule <= im->maxval)
- gfx_line(im,
- im->xorigin, ytr(im, im->gdes[i].yrule),
- im->xorigin + im->xsize, ytr(im,
- im->gdes[i].yrule),
- 1.0, im->gdes[i].col);
+ cairo_save(im->cr);
+ if (im->gdes[i].dash) {
+ cairo_set_dash(im->cr, im->gdes[i].p_dashes,
+ im->gdes[i].ndash, im->gdes[i].offset);
+ }
+ gfx_line(im,
+ im->xorigin, ytr(im, im->gdes[i].yrule),
+ im->xorigin + im->xsize, ytr(im,
+ im->gdes[i].yrule),
+ 1.0, im->gdes[i].col);
+ cairo_stroke(im->cr);
+ cairo_restore(im->cr);
break;
case GF_VRULE:
if (im->gdes[i].xrule >= im->start
&& im->gdes[i].xrule <= im->end)
- gfx_line(im,
- xtr(im, im->gdes[i].xrule), im->yorigin,
- xtr(im, im->gdes[i].xrule),
- im->yorigin - im->ysize, 1.0, im->gdes[i].col);
+ cairo_save(im->cr);
+ if (im->gdes[i].dash) {
+ cairo_set_dash(im->cr, im->gdes[i].p_dashes,
+ im->gdes[i].ndash, im->gdes[i].offset);
+ }
+ gfx_line(im,
+ xtr(im, im->gdes[i].xrule), im->yorigin,
+ xtr(im, im->gdes[i].xrule),
+ im->yorigin - im->ysize, 1.0, im->gdes[i].col);
+ cairo_stroke(im->cr);
+ cairo_restore(im->cr);
break;
default:
break;
switch (im->imgformat) {
case IF_PNG:
- if (cairo_surface_write_to_png(im->surface, im->graphfile) !=
- CAIRO_STATUS_SUCCESS) {
+ {
+ cairo_status_t status;
+
+ if (strlen(im->graphfile) == 0) {
+ status =
+ cairo_surface_write_to_png_stream(im->surface,
+ &cairo_copy_to_buffer, im);
+ } else if (strcmp(im->graphfile, "-") == 0) {
+ status =
+ cairo_surface_write_to_png_stream(im->surface,
+ &cairo_write_func_filehandle,
+ (void *) stdout);
+ } else {
+ status = cairo_surface_write_to_png(im->surface, im->graphfile);
+ }
+
+ if (status != CAIRO_STATUS_SUCCESS) {
rrd_set_error("Could not save png to '%s'", im->graphfile);
return 1;
}
+ }
break;
default:
- cairo_show_page(im->cr);
+ if (strlen(im->graphfile)) {
+ cairo_show_page(im->cr);
+ } else {
+ cairo_surface_finish(im->surface);
+ }
break;
}
return 0;
im->gdes[im->gdes_c - 1].data_first = 0;
im->gdes[im->gdes_c - 1].p_data = NULL;
im->gdes[im->gdes_c - 1].rpnp = NULL;
+ im->gdes[im->gdes_c - 1].p_dashes = NULL;
im->gdes[im->gdes_c - 1].shift = 0.0;
+ im->gdes[im->gdes_c - 1].dash = 0;
+ im->gdes[im->gdes_c - 1].ndash = 0;
+ im->gdes[im->gdes_c - 1].offset = 0;
im->gdes[im->gdes_c - 1].col.red = 0.0;
im->gdes[im->gdes_c - 1].col.green = 0.0;
im->gdes[im->gdes_c - 1].col.blue = 0.0;
im->gdes[im->gdes_c - 1].ds = -1;
im->gdes[im->gdes_c - 1].cf_reduce = CF_AVERAGE;
im->gdes[im->gdes_c - 1].cf = CF_AVERAGE;
- im->gdes[im->gdes_c - 1].p_data = NULL;
im->gdes[im->gdes_c - 1].yrule = DNAN;
im->gdes[im->gdes_c - 1].xrule = 0;
return 0;
/* a dummy surface so that we can measure text sizes for placements */
im.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 10, 10);
im.cr = cairo_create(im.surface);
-
-
- /* not currently using this ... */
im.graphhandle = stream;
rrd_graph_options(argc, argv, &im);
return -1;
}
+ if (optind >= argc) {
+ rrd_set_error("missing filename");
+ return -1;
+ }
+
if (strlen(argv[optind]) >= MAXPATH) {
rrd_set_error("filename (including path) too long");
im_free(&im);
return -1;
}
+
strncpy(im.graphfile, argv[optind], MAXPATH - 1);
im.graphfile[MAXPATH - 1] = '\0';
return 0;
}
+/* a simplified version of the above that just creates the graph in memory
+ and returns a pointer to it. */
+
+unsigned char *rrd_graph_in_memory(
+ int argc,
+ char **argv,
+ char ***prdata,
+ int *xsize,
+ int *ysize,
+ double *ymin,
+ double *ymax,
+ size_t * img_size)
+{
+ image_desc_t im;
+
+ rrd_graph_init(&im);
+
+ /* a dummy surface so that we can measure text sizes for placements */
+ im.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 10, 10);
+ im.cr = cairo_create(im.surface);
+
+ rrd_graph_options(argc, argv, &im);
+ if (rrd_test_error()) {
+ im_free(&im);
+ return NULL;
+ }
+
+ rrd_graph_script(argc, argv, &im, 1);
+ if (rrd_test_error()) {
+ im_free(&im);
+ return NULL;
+ }
+
+ /* Everything is now read and the actual work can start */
+
+ /* by not assigning a name to im.graphfile data will be written to
+ newly allocated memory on im.rendered_image ... */
+
+ (*prdata) = NULL;
+ if (graph_paint(&im, prdata) == -1) {
+ im_free(&im);
+ return NULL;
+ }
+
+ *xsize = im.ximg;
+ *ysize = im.yimg;
+ *ymin = im.minval;
+ *ymax = im.maxval;
+ *img_size = im.rendered_image_size;
+ im_free(&im);
+
+ return im.rendered_image;
+}
+
void rrd_graph_init(
image_desc_t *im)
{
im->yimg = 0;
im->xsize = 400;
im->ysize = 100;
+ im->rendered_image_size = 0;
+ im->rendered_image = NULL;
im->step = 0;
im->ylegend[0] = '\0';
im->title[0] = '\0';
im->symbol = ' ';
im->viewfactor = 1.0;
im->imgformat = IF_PNG;
+ im->graphfile[0] = '\0';
im->cr = NULL;
im->surface = NULL;
im->extra_flags = 0;
long long_tmp;
struct rrd_time_value 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 */
+#define LONGOPT_UNITS_SI 255
+ 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'},
+ {"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'},
+ {"imginfo", required_argument, 0, 'f'},
+ {"imgformat", required_argument, 0, 'a'},
+ {"lazy", no_argument, 0, 'z'},
+ {"zoom", required_argument, 0, 'm'},
+ {"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'},
+ {"no-minor", no_argument, 0, 'I'},
+ {"slope-mode", no_argument, 0, 'E'},
+ {"alt-autoscale", no_argument, 0, 'A'},
+ {"alt-autoscale-min", no_argument, 0, 'J'},
+ {"alt-autoscale-max", no_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},
+ {"step", 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'},
+ {"watermark", required_argument, 0, 'W'},
+ {"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 */
+ {0, 0, 0, 0}
+ };
optind = 0;
opterr = 0; /* initialize getopt */
parsetime("end-24h", &start_tv);
parsetime("now", &end_tv);
- /* defines for long options without a short equivalent. should be bytes,
- and may not collide with (the ASCII value of) short options */
-#define LONGOPT_UNITS_SI 255
-
while (1) {
- static 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'},
- {"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'},
- {"imginfo", required_argument, 0, 'f'},
- {"imgformat", required_argument, 0, 'a'},
- {"lazy", no_argument, 0, 'z'},
- {"zoom", required_argument, 0, 'm'},
- {"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'},
- {"no-minor", no_argument, 0, 'I'},
- {"slope-mode", no_argument, 0, 'E'},
- {"alt-autoscale", no_argument, 0, 'A'},
- {"alt-autoscale-min", no_argument, 0, 'J'},
- {"alt-autoscale-max", no_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},
- {"step", 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'},
- {"watermark", required_argument, 0, 'W'},
- {"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 */
- {0, 0, 0, 0}
- };
int option_index = 0;
int opt;
int col_start, col_end;
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;
}
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);
case 'n':{
char prop[15];
double size = 1;
- char font[1024] = "";
+ int end;
- if (sscanf(optarg, "%10[A-Z]:%lf:%1000s", prop, &size, font) >= 2) {
+ 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) {
im->text_prop[propidx].size = size;
}
- if (strlen(font) > 0) {
- strcpy(im->text_prop[propidx].font, font);
+ if (strlen(prop) > end) {
+ if (prop[end] == ':') {
+ strncpy(im->text_prop[propidx].font,
+ prop + end + 1, 255);
+ im->text_prop[propidx].font[255] = '\0';
+ } else {
+ rrd_set_error
+ ("expected after font size in '%s'",
+ prop);
+ return;
+ }
}
if (propidx == sindex && sindex != 0)
break;
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;
}
}
- if (optind >= argc) {
- rrd_set_error("missing filename");
- return;
- }
-
if (im->logarithmic == 1 && im->minval <= 0) {
rrd_set_error
("for a logarithmic yaxis you must specify a lower-limit > 0");
int vdef_parse(
- gdes,
- str)
- struct graph_desc_t *gdes;
- const char *const str;
+ struct graph_desc_t *gdes,
+ const char *const str)
{
/* A VDEF currently is either "func" or "param,func"
* so the parsing is rather simple. Change if needed.
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 {
gdes->vf.op = VDEF_MAXIMUM;
else if (!strcmp("AVERAGE", func))
gdes->vf.op = VDEF_AVERAGE;
+ else if (!strcmp("STDEV", func))
+ gdes->vf.op = VDEF_STDEV;
else if (!strcmp("MINIMUM", func))
gdes->vf.op = VDEF_MINIMUM;
else if (!strcmp("TOTAL", func))
break;
case VDEF_MAXIMUM:
case VDEF_AVERAGE:
+ case VDEF_STDEV:
case VDEF_MINIMUM:
case VDEF_TOTAL:
case VDEF_FIRST:
int vdef_calc(
- im,
- gdi)
- image_desc_t *im;
- int gdi;
+ image_desc_t *im,
+ int gdi)
{
graph_desc_t *src, *dst;
rrd_value_t *data;
}
break;
case VDEF_TOTAL:
+ case VDEF_STDEV:
case VDEF_AVERAGE:{
int cnt = 0;
double sum = 0.0;
+ double average = 0.0;
for (step = 0; step < steps; step++) {
if (finite(data[step * src->ds_cnt])) {
if (dst->vf.op == VDEF_TOTAL) {
dst->vf.val = sum * src->step;
dst->vf.when = 0; /* no time component */
- } else {
+ } else if (dst->vf.op == VDEF_AVERAGE) {
dst->vf.val = sum / cnt;
dst->vf.when = 0; /* no time component */
+ } else {
+ average = sum / cnt;
+ sum = 0.0;
+ for (step = 0; step < steps; step++) {
+ if (finite(data[step * src->ds_cnt])) {
+ sum += pow((data[step * src->ds_cnt] - average), 2.0);
+ };
+ }
+ dst->vf.val = pow(sum / cnt, 0.5);
+ dst->vf.when = 0; /* no time component */
};
} else {
dst->vf.val = DNAN;
/* NaN < -INF < finite_values < INF */
int vdef_percent_compar(
- a,
- b)
- const void *a, *b;
+ const void *a,
+ const void *b)
{
/* Equality is not returned; this doesn't hurt except
* (maybe) for a little performance.