X-Git-Url: https://git.octo.it/?p=rrdtool.git;a=blobdiff_plain;f=src%2Frrd_graph.c;h=0e847869a70984e401520abb293916b46530f5c5;hp=e83812132329c2201eabec6cb82f21dc1a576504;hb=299d1636596a1911659e0a53bfad5c47e72835e0;hpb=f8c81c20d9fe8183cb481944cd9b29ace3ee37aa diff --git a/src/rrd_graph.c b/src/rrd_graph.c index e838121..0e84786 100644 --- a/src/rrd_graph.c +++ b/src/rrd_graph.c @@ -1,5 +1,5 @@ /**************************************************************************** - * 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 ****************************************************************************/ @@ -61,7 +61,7 @@ xlab_t xlab[] = { , {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"} , @@ -235,6 +235,7 @@ enum gf_en gf_conv( 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); @@ -307,7 +308,7 @@ int im_free( image_desc_t *im) { unsigned long i, ii; - cairo_status_t status; + cairo_status_t status = 0; if (im == NULL) return 0; @@ -321,6 +322,10 @@ int im_free( 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); } @@ -328,14 +333,14 @@ int im_free( if (im->font_options) cairo_font_options_destroy(im->font_options); - status = cairo_status(im->cr); - - if (im->cr) + 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 issuesm it can't even die: %s\n", + fprintf(stderr, "OOPS: Cairo has issues it can't even die: %s\n", cairo_status_to_string(status)); return 0; @@ -716,6 +721,7 @@ void reduce_data( else { switch (cf) { case CF_HWPREDICT: + case CF_MHWPREDICT: case CF_DEVSEASONAL: case CF_DEVPREDICT: case CF_SEASONAL: @@ -741,6 +747,7 @@ void reduce_data( } else { switch (cf) { case CF_HWPREDICT: + case CF_MHWPREDICT: case CF_DEVSEASONAL: case CF_DEVPREDICT: case CF_SEASONAL: @@ -1112,6 +1119,39 @@ int data_calc( 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) @@ -1224,8 +1264,8 @@ int data_proc( } /* 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) @@ -1241,19 +1281,24 @@ int data_proc( 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; } } @@ -1437,6 +1482,7 @@ int print_calc( switch (im->gdes[i].cf) { case CF_HWPREDICT: + case CF_MHWPREDICT: case CF_DEVPREDICT: case CF_DEVSEASONAL: case CF_SEASONAL: @@ -1542,6 +1588,7 @@ int print_calc( graphelement = 1; break; case GF_COMMENT: + case GF_TEXTALIGN: case GF_DEF: case GF_CDEF: case GF_VDEF: @@ -1578,6 +1625,7 @@ int leg_place( 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)) { @@ -1595,6 +1643,10 @@ int leg_place( /* 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 @@ -1632,22 +1684,24 @@ int leg_place( 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, @@ -1664,10 +1718,25 @@ int leg_place( 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) { @@ -1675,11 +1744,10 @@ int leg_place( i--; fill = fill_last; leg_c--; - prt_fctn = 'j'; - } else { - prt_fctn = 'l'; } - + } + if (leg_c == 1 && prt_fctn == 'j') { + prt_fctn = 'l'; } } @@ -1971,38 +2039,10 @@ double frexp10( 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( @@ -2564,6 +2604,11 @@ void grid_paint( 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); } @@ -2844,11 +2889,38 @@ int graph_size_location( 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, @@ -2930,29 +3002,38 @@ int graph_paint( break; case IF_PDF: im->gridfit = 0; - im->surface = - cairo_pdf_surface_create(im->graphfile, im->ximg * im->zoom, - im->yimg * im->zoom); + 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->gridfit = 0; - im->surface = - cairo_ps_surface_create(im->graphfile, im->ximg * im->zoom, - im->yimg * im->zoom); + 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->gridfit = 0; - im->surface = - cairo_svg_surface_create(im->graphfile, im->ximg * im->zoom, - im->yimg * im->zoom); + 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, @@ -2983,6 +3064,7 @@ int graph_paint( case GF_PRINT: case GF_GPRINT: case GF_COMMENT: + case GF_TEXTALIGN: case GF_HRULE: case GF_VRULE: case GF_XPORT: @@ -3047,6 +3129,12 @@ int graph_paint( 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 @@ -3107,10 +3195,14 @@ int graph_paint( 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++) { @@ -3253,20 +3345,36 @@ int graph_paint( switch (im->gdes[i].gf) { case GF_HRULE: if (im->gdes[i].yrule >= im->minval - && im->gdes[i].yrule <= im->maxval) + && im->gdes[i].yrule <= im->maxval) { + 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) + && im->gdes[i].xrule <= im->end) { + 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; @@ -3276,14 +3384,34 @@ int graph_paint( 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; @@ -3322,7 +3450,11 @@ int gdes_alloc( 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; @@ -3334,7 +3466,6 @@ int gdes_alloc( 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; @@ -3385,9 +3516,6 @@ int rrd_graph( /* 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); @@ -3396,11 +3524,17 @@ int rrd_graph( 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'; @@ -3457,6 +3591,60 @@ int rrd_graph( 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) { @@ -3479,6 +3667,8 @@ void rrd_graph_init( 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'; @@ -3491,6 +3681,7 @@ void rrd_graph_init( im->symbol = ' '; im->viewfactor = 1.0; im->imgformat = IF_PNG; + im->graphfile[0] = '\0'; im->cr = NULL; im->surface = NULL; im->extra_flags = 0; @@ -3574,6 +3765,55 @@ void rrd_graph_options( 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 */ @@ -3581,55 +3821,7 @@ void rrd_graph_options( 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; @@ -3669,6 +3861,7 @@ void rrd_graph_options( 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) @@ -3686,10 +3879,14 @@ void rrd_graph_options( 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; @@ -3749,8 +3946,9 @@ void rrd_graph_options( 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; @@ -3759,6 +3957,7 @@ void rrd_graph_options( return; } } else { + setlocale(LC_NUMERIC, old_locale); rrd_set_error("invalid y-grid format"); return; } @@ -3768,10 +3967,14 @@ void rrd_graph_options( 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); @@ -3868,19 +4071,30 @@ void rrd_graph_options( 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 ((int) 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; @@ -3890,13 +4104,16 @@ void rrd_graph_options( 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; @@ -3956,11 +4173,6 @@ void rrd_graph_options( } } - 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"); @@ -4092,10 +4304,8 @@ int bad_format( 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. @@ -4103,9 +4313,12 @@ int vdef_parse( 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 { @@ -4125,6 +4338,8 @@ int vdef_parse( 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)) @@ -4164,6 +4379,7 @@ int vdef_parse( break; case VDEF_MAXIMUM: case VDEF_AVERAGE: + case VDEF_STDEV: case VDEF_MINIMUM: case VDEF_TOTAL: case VDEF_FIRST: @@ -4187,10 +4403,8 @@ int vdef_parse( 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; @@ -4254,9 +4468,11 @@ int vdef_calc( } 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])) { @@ -4268,9 +4484,19 @@ int vdef_calc( 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; @@ -4390,9 +4616,8 @@ int vdef_calc( /* 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.