X-Git-Url: https://git.octo.it/?p=rrdtool.git;a=blobdiff_plain;f=src%2Frrd_graph.c;h=594c9f6df525375f6dda0d5deea4d780c21172c2;hp=7fbe8651146c94b54e48ed47e33c7c44002969f9;hb=afdc1dabafc566180b57ec64c9c5504c2c8e2775;hpb=7d0d6b07c9f5bd5dfd99aa7fe9826eebf2181f1f diff --git a/src/rrd_graph.c b/src/rrd_graph.c index 7fbe865..594c9f6 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.3.2 Copyright by Tobi Oetiker, 1997-2008 **************************************************************************** * rrd__graph.c produce graphs from data in rrdfiles ****************************************************************************/ @@ -7,9 +7,17 @@ #include +/* for basename */ +#ifdef HAVE_LIBGEN_H +# include +#eles +#include "plbasename.h" +#endif + #ifdef WIN32 #include "strftime.h" #endif + #include "rrd_tool.h" #if defined(WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__) @@ -26,6 +34,7 @@ #endif #include "rrd_graph.h" +#include "rrd_client.h" /* some constant definitions */ @@ -33,19 +42,21 @@ #ifndef RRD_DEFAULT_FONT /* there is special code later to pick Cour.ttf when running on windows */ -#define RRD_DEFAULT_FONT "DejaVuSansMono-Roman.ttf" +#define RRD_DEFAULT_FONT "DejaVu Sans Mono,Bitstream Vera Sans Mono,monospace,Courier" #endif text_prop_t text_prop[] = { - {8.0, RRD_DEFAULT_FONT} + {8.0, RRD_DEFAULT_FONT,NULL} , /* default */ - {9.0, RRD_DEFAULT_FONT} + {9.0, RRD_DEFAULT_FONT,NULL} , /* title */ - {7.0, RRD_DEFAULT_FONT} + {7.0, RRD_DEFAULT_FONT,NULL} , /* axis */ - {8.0, RRD_DEFAULT_FONT} + {8.0, RRD_DEFAULT_FONT,NULL} , /* unit */ - {8.0, RRD_DEFAULT_FONT} /* legend */ + {8.0, RRD_DEFAULT_FONT,NULL} /* legend */ + , + {5.5, RRD_DEFAULT_FONT,NULL} /* watermark */ }; xlab_t xlab[] = { @@ -61,7 +72,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"} , @@ -201,16 +212,6 @@ double ytr( yval = im->yorigin - pixie * (log10(value) - log10(im->minval)); } } - /* make sure we don't return anything too unreasonable. GD lib can - get terribly slow when drawing lines outside its scope. This is - especially problematic in connection with the rigid option */ - if (!im->rigid) { - /* keep yval as-is */ - } else if (yval > im->yorigin) { - yval = im->yorigin + 0.00001; - } else if (yval < im->yorigin - im->ysize) { - yval = im->yorigin - im->ysize - 0.00001; - } return yval; } @@ -242,7 +243,7 @@ enum gf_en gf_conv( conv_if(XPORT, GF_XPORT); conv_if(SHIFT, GF_SHIFT); - return (-1); + return (enum gf_en)(-1); } enum gfx_if_en if_conv( @@ -254,7 +255,7 @@ enum gfx_if_en if_conv( conv_if(EPS, IF_EPS); conv_if(PDF, IF_PDF); - return (-1); + return (enum gfx_if_en)(-1); } enum tmt_en tmt_conv( @@ -268,7 +269,7 @@ enum tmt_en tmt_conv( conv_if(WEEK, TMT_WEEK); conv_if(MONTH, TMT_MONTH); conv_if(YEAR, TMT_YEAR); - return (-1); + return (enum tmt_en)(-1); } enum grc_en grc_conv( @@ -286,7 +287,7 @@ enum grc_en grc_conv( conv_if(AXIS, GRC_AXIS); conv_if(FRAME, GRC_FRAME); - return -1; + return (enum grc_en)(-1); } enum text_prop_en text_prop_conv( @@ -298,7 +299,8 @@ enum text_prop_en text_prop_conv( conv_if(AXIS, TEXT_PROP_AXIS); conv_if(UNIT, TEXT_PROP_UNIT); conv_if(LEGEND, TEXT_PROP_LEGEND); - return -1; + conv_if(WATERMARK, TEXT_PROP_WATERMARK); + return (enum text_prop_en)(-1); } @@ -308,10 +310,14 @@ int im_free( image_desc_t *im) { unsigned long i, ii; - cairo_status_t status; + cairo_status_t status = (cairo_status_t) 0; if (im == NULL) return 0; + + if (im->daemon_addr != NULL) + free(im->daemon_addr); + 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 */ @@ -322,6 +328,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); } @@ -329,16 +339,25 @@ 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->rendered_image) { + free(im->rendered_image); + } + + if (im->layout) { + g_object_unref (im->layout); + } + 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; } @@ -415,7 +434,7 @@ void si_unit( if (im->unitsexponent != 9999) { /* unitsexponent = 9, 6, 3, 0, -3, -6, -9, etc */ - viewdigits = floor(im->unitsexponent / 3); + viewdigits = floor((double)(im->unitsexponent / 3)); } else { viewdigits = digits; } @@ -827,6 +846,36 @@ int data_fetch( if (!skip) { unsigned long ft_step = im->gdes[i].step; /* ft_step will record what we got from fetch */ + /* Flush the file if + * - a connection to the daemon has been established + * - this is the first occurrence of that RRD file + */ + if (rrdc_is_connected(im->daemon_addr)) + { + int status; + + status = 0; + for (ii = 0; ii < i; ii++) + { + if (strcmp (im->gdes[i].rrd, im->gdes[ii].rrd) == 0) + { + status = 1; + break; + } + } + + if (status == 0) + { + status = rrdc_flush (im->gdes[i].rrd); + if (status != 0) + { + rrd_set_error ("rrdc_flush (%s) failed with status %i.", + im->gdes[i].rrd, status); + return (-1); + } + } + } /* if (rrdc_is_connected()) */ + if ((rrd_fetch_fn(im->gdes[i].rrd, im->gdes[i].cf, &im->gdes[i].start, @@ -897,7 +946,7 @@ long find_var( return -1; } -/* find the largest common denominator for all the numbers +/* find the greatest common divisor for all the numbers in the 0 terminated num array */ long lcd( long *num) @@ -1011,7 +1060,7 @@ int data_calc( /* add one entry to the array that keeps track of the step sizes of the * data sources going into the CDEF. */ if ((steparray = - rrd_realloc(steparray, + (long*)rrd_realloc(steparray, (++stepcnt + 1) * sizeof(*steparray))) == NULL) { rrd_set_error("realloc steparray"); @@ -1079,7 +1128,7 @@ int data_calc( */ im->gdes[gdi].step = lcd(steparray); free(steparray); - if ((im->gdes[gdi].data = malloc(((im->gdes[gdi].end - + if ((im->gdes[gdi].data = (rrd_value_t*)malloc(((im->gdes[gdi].end - im->gdes[gdi].start) / im->gdes[gdi].step) * sizeof(double))) == NULL) { @@ -1115,6 +1164,43 @@ int data_calc( 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 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) @@ -1132,7 +1218,7 @@ int data_proc( for (i = 0; i < im->gdes_c; i++) { if ((im->gdes[i].gf == GF_LINE) || (im->gdes[i].gf == GF_AREA) || (im->gdes[i].gf == GF_TICK)) { - if ((im->gdes[i].p_data = malloc((im->xsize + 1) + if ((im->gdes[i].p_data = (rrd_value_t*)malloc((im->xsize + 1) * sizeof(rrd_value_t))) == NULL) { rrd_set_error("malloc data_proc"); return -1; @@ -1215,24 +1301,29 @@ int data_proc( lets set these to dummy values then ... */ if (im->logarithmic) { - if (isnan(minval)) - minval = 0.2; - if (isnan(maxval)) + if (isnan(minval) || isnan(maxval) || maxval <= 0) { + minval = 0.0; /* catching this right away below */ maxval = 5.1; + } + /* in logarithm mode, where minval is smaller or equal + to 0 make the beast just way smaller than maxval */ + if (minval <= 0) { + minval = maxval / 10e8; + } } else { - if (isnan(minval)) + if (isnan(minval) || isnan(maxval)) { minval = 0.0; - if (isnan(maxval)) maxval = 1.0; + } } - /* adjust min and max values */ + /* adjust min and max values given by the user */ + /* 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) - im->minval = minval * 0.5; + im->minval = minval / 2.0; else im->minval = minval; } @@ -1244,19 +1335,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; } } @@ -1385,8 +1481,7 @@ time_t find_next_time( /* calculate values required for PRINT and GPRINT functions */ int print_calc( - image_desc_t *im, - char ***prdata) + image_desc_t *im) { long i, ii, validsteps; double printval; @@ -1397,24 +1492,16 @@ int print_calc( double magfact = -1; char *si_symb = ""; char *percent_s; - int prlines = 1; + int prline_cnt = 0; /* wow initializing tmvdef is quite a task :-) */ time_t now = time(NULL); localtime_r(&now, &tmvdef); - if (im->imginfo) - prlines++; for (i = 0; i < im->gdes_c; i++) { vidx = im->gdes[i].vidx; switch (im->gdes[i].gf) { case GF_PRINT: - prlines++; - if (((*prdata) = - rrd_realloc((*prdata), prlines * sizeof(char *))) == NULL) { - rrd_set_error("realloc prdata"); - return 0; - } case GF_GPRINT: /* PRINT and GPRINT can now print VDEF generated values. * There's no need to do any calculations on them as these @@ -1485,44 +1572,44 @@ int print_calc( } if (im->gdes[i].gf == GF_PRINT) { - (*prdata)[prlines - 2] = - malloc((FMT_LEG_LEN + 2) * sizeof(char)); - (*prdata)[prlines - 1] = NULL; + rrd_infoval_t prline; + if (im->gdes[i].strftm) { - strftime((*prdata)[prlines - 2], FMT_LEG_LEN, - im->gdes[i].format, &tmvdef); + prline.u_str = (char*)malloc((FMT_LEG_LEN + 2) * sizeof(char)); + strftime(prline.u_str, + FMT_LEG_LEN, im->gdes[i].format, &tmvdef); + } else if (bad_format(im->gdes[i].format)) { + rrd_set_error + ("bad format for PRINT in '%s'", im->gdes[i].format); + return -1; } else { - if (bad_format(im->gdes[i].format)) { - rrd_set_error("bad format for PRINT in '%s'", - im->gdes[i].format); - return -1; - } -#ifdef HAVE_SNPRINTF - snprintf((*prdata)[prlines - 2], FMT_LEG_LEN, - im->gdes[i].format, printval, si_symb); -#else - sprintf((*prdata)[prlines - 2], im->gdes[i].format, - printval, si_symb); -#endif + prline.u_str = + sprintf_alloc(im->gdes[i].format, printval, si_symb); } + grinfo_push(im, + sprintf_alloc + ("print[%ld]", prline_cnt++), RD_I_STR, prline); + free(prline.u_str); } else { /* GF_GPRINT */ if (im->gdes[i].strftm) { - strftime(im->gdes[i].legend, FMT_LEG_LEN, - im->gdes[i].format, &tmvdef); + strftime(im->gdes[i].legend, + FMT_LEG_LEN, im->gdes[i].format, &tmvdef); } else { if (bad_format(im->gdes[i].format)) { - rrd_set_error("bad format for GPRINT in '%s'", - im->gdes[i].format); + rrd_set_error + ("bad format for GPRINT in '%s'", + im->gdes[i].format); return -1; } #ifdef HAVE_SNPRINTF - snprintf(im->gdes[i].legend, FMT_LEG_LEN - 2, + snprintf(im->gdes[i].legend, + FMT_LEG_LEN - 2, im->gdes[i].format, printval, si_symb); #else - sprintf(im->gdes[i].legend, im->gdes[i].format, printval, - si_symb); + sprintf(im->gdes[i].legend, + im->gdes[i].format, printval, si_symb); #endif } graphelement = 1; @@ -1567,62 +1654,75 @@ int print_calc( } + /* place legends with color spots */ int leg_place( image_desc_t *im, - int *gY) + int calc_width) { /* graph labels */ int interleg = im->text_prop[TEXT_PROP_LEGEND].size * 2.0; int border = im->text_prop[TEXT_PROP_LEGEND].size * 2.0; int fill = 0, fill_last; + double legendwidth; // = im->ximg - 2 * border; int leg_c = 0; - int leg_x = border, leg_y = im->yimg; - int leg_y_prev = im->yimg; + double leg_x = border; + int leg_y = 0; //im->yimg; + int leg_y_prev = 0; // im->yimg; int leg_cc; - int glue = 0; + double glue = 0; int i, ii, mark = 0; - char prt_fctn; /*special printfunctions */ char default_txtalign = TXA_JUSTIFIED; /*default line orientation */ int *legspace; + char *tab; + char saved_legend[FMT_LEG_LEN + 5]; + + if(calc_width){ + legendwidth = 0; + } + else{ + legendwidth = im->legendwidth - 2 * border; + } + if (!(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH)) { - if ((legspace = malloc(im->gdes_c * sizeof(int))) == NULL) { + if ((legspace = (int*)malloc(im->gdes_c * sizeof(int))) == NULL) { rrd_set_error("malloc for legspace"); return -1; } - if (im->extra_flags & FULL_SIZE_MODE) - leg_y = leg_y_prev = - leg_y - (int) (im->text_prop[TEXT_PROP_LEGEND].size * 1.8); - for (i = 0; i < im->gdes_c; i++) { - fill_last = fill; + char prt_fctn; /*special printfunctions */ + if(calc_width){ + strcpy(saved_legend, im->gdes[i].legend); + } + fill_last = fill; /* 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 - || im->gdes[i].yrule > im->maxval)) + if (im->gdes[i].gf == GF_HRULE + && (im->gdes[i].yrule < + im->minval || im->gdes[i].yrule > im->maxval)) im->gdes[i].legend[0] = '\0'; - - if (im->gdes[i].gf == GF_VRULE && - (im->gdes[i].xrule < im->start - || im->gdes[i].xrule > im->end)) + if (im->gdes[i].gf == GF_VRULE + && (im->gdes[i].xrule < + im->start || im->gdes[i].xrule > im->end)) im->gdes[i].legend[0] = '\0'; } - leg_cc = strlen(im->gdes[i].legend); + /* turn \\t into tab */ + while ((tab = strstr(im->gdes[i].legend, "\\t"))) { + memmove(tab, tab + 1, strlen(tab)); + tab[0] = (char) 9; + } - /* is there a controle code ant the end of the legend string ? */ - /* and it is not a tab \\t */ - if (leg_cc >= 2 && im->gdes[i].legend[leg_cc - 2] == '\\' - && im->gdes[i].legend[leg_cc - 1] != 't') { + leg_cc = strlen(im->gdes[i].legend); + /* is there a controle code at the end of the legend string ? */ + if (leg_cc >= 2 && im->gdes[i].legend[leg_cc - 2] == '\\') { prt_fctn = im->gdes[i].legend[leg_cc - 1]; leg_cc -= 2; im->gdes[i].legend[leg_cc] = '\0'; @@ -1634,13 +1734,12 @@ int leg_place( prt_fctn != 'r' && prt_fctn != 'j' && prt_fctn != 'c' && - prt_fctn != 's' && - prt_fctn != 't' && prt_fctn != '\0' && prt_fctn != 'g') { + prt_fctn != 's' && prt_fctn != '\0' && prt_fctn != 'g') { free(legspace); - rrd_set_error("Unknown control code at the end of '%s\\%c'", - im->gdes[i].legend, prt_fctn); + rrd_set_error + ("Unknown control code at the end of '%s\\%c'", + im->gdes[i].legend, prt_fctn); return -1; - } /* \n -> \l */ if (prt_fctn == 'n') { @@ -1658,16 +1757,17 @@ int leg_place( /* no interleg space if string ends in \g */ legspace[i] = (prt_fctn == 'g' ? 0 : interleg); - if (fill > 0) { fill += legspace[i]; } - fill += gfx_get_text_width(im, fill + border, - im->text_prop[TEXT_PROP_LEGEND]. - font, - im->text_prop[TEXT_PROP_LEGEND]. - size, im->tabwidth, - im->gdes[i].legend); + fill += + gfx_get_text_width(im, + fill + border, + im-> + text_prop + [TEXT_PROP_LEGEND]. + font_desc, + im->tabwidth, im->gdes[i].legend); leg_c++; } else { legspace[i] = 0; @@ -1678,7 +1778,10 @@ int leg_place( } if (prt_fctn == '\0') { - if (i == im->gdes_c - 1 || fill > im->ximg - 2 * border) { + if(calc_width && (fill > legendwidth)){ + legendwidth = fill; + } + if (i == im->gdes_c - 1 || fill > legendwidth) { /* just one legend item is left right or center */ switch (default_txtalign) { case TXA_RIGHT: @@ -1696,7 +1799,7 @@ int leg_place( } } /* is it time to place the legends ? */ - if (fill > im->ximg - 2 * border) { + if (fill > legendwidth) { if (leg_c > 1) { /* go back one */ i--; @@ -1709,64 +1812,56 @@ int leg_place( } } - if (prt_fctn != '\0') { leg_x = border; if (leg_c >= 2 && prt_fctn == 'j') { - glue = (im->ximg - fill - 2 * border) / (leg_c - 1); + glue = (double)(legendwidth - fill) / (double)(leg_c - 1); } else { glue = 0; } if (prt_fctn == 'c') - leg_x = (im->ximg - fill) / 2.0; + leg_x = (double)(legendwidth - fill) / 2.0; if (prt_fctn == 'r') - leg_x = im->ximg - fill - border; - + leg_x = legendwidth - fill - border; for (ii = mark; ii <= i; ii++) { if (im->gdes[ii].legend[0] == '\0') continue; /* skip empty legends */ im->gdes[ii].leg_x = leg_x; - im->gdes[ii].leg_y = leg_y; + im->gdes[ii].leg_y = leg_y + border; leg_x += - gfx_get_text_width(im, leg_x, - im->text_prop[TEXT_PROP_LEGEND]. - font, - im->text_prop[TEXT_PROP_LEGEND]. - size, im->tabwidth, - im->gdes[ii].legend) - + legspace[ii] + (double)gfx_get_text_width(im, leg_x, + im-> + text_prop + [TEXT_PROP_LEGEND]. + font_desc, + im->tabwidth, im->gdes[ii].legend) + +(double)legspace[ii] + glue; } leg_y_prev = leg_y; - if (im->extra_flags & FULL_SIZE_MODE) { - /* only add y space if there was text on the line */ - if (leg_x > border || prt_fctn == 's') - leg_y -= im->text_prop[TEXT_PROP_LEGEND].size * 1.8; - if (prt_fctn == 's') - leg_y += im->text_prop[TEXT_PROP_LEGEND].size; - } else { - if (leg_x > border || prt_fctn == 's') - 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 (leg_x > border || prt_fctn == 's') + 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(calc_width && (fill > legendwidth)){ + legendwidth = fill; } fill = 0; leg_c = 0; mark = ii; } - } - if (im->extra_flags & FULL_SIZE_MODE) { - if (leg_y != leg_y_prev) { - *gY = leg_y - im->text_prop[TEXT_PROP_LEGEND].size * 1.8; - im->yorigin = - leg_y - im->text_prop[TEXT_PROP_LEGEND].size * 1.8; + if(calc_width){ + strcpy(im->gdes[i].legend, saved_legend); } - } else { - im->yimg = leg_y_prev; - /* if we did place some legends we have to add vertical space */ - if (leg_y != im->yimg) - im->yimg += im->text_prop[TEXT_PROP_LEGEND].size * 1.8; + } + + if(calc_width){ + im->legendwidth = legendwidth + 2 * border; + } + else{ + im->legendheight = leg_y + border * 0.6; } free(legspace); } @@ -1779,10 +1874,9 @@ int leg_place( /* the xaxis labels are determined from the number of seconds per pixel in the requested graph */ - - int calc_horizontal_grid( - image_desc_t *im) + image_desc_t + *im) { double range; double scaledrange; @@ -1793,7 +1887,6 @@ int calc_horizontal_grid( im->ygrid_scale.labfact = 2; range = im->maxval - im->minval; scaledrange = range / im->magfact; - /* does the scale of this graph make it impossible to put lines on it? If so, give up. */ if (isnan(scaledrange)) { @@ -1811,22 +1904,23 @@ int calc_horizontal_grid( im->viewfactor / im->magfact)); if (decimals <= 0) /* everything is small. make place for zero */ decimals = 1; - im->ygrid_scale.gridstep = pow((double) 10, floor(log10(range * im->viewfactor / im->magfact))) / im->viewfactor * im->magfact; - if (im->ygrid_scale.gridstep == 0) /* range is one -> 0.1 is reasonable scale */ im->ygrid_scale.gridstep = 0.1; /* should have at least 5 lines but no more then 15 */ - if (range / im->ygrid_scale.gridstep < 5) + if (range / im->ygrid_scale.gridstep < 5 + && im->ygrid_scale.gridstep >= 30) im->ygrid_scale.gridstep /= 10; if (range / im->ygrid_scale.gridstep > 15) im->ygrid_scale.gridstep *= 10; if (range / im->ygrid_scale.gridstep > 5) { im->ygrid_scale.labfact = 1; - if (range / im->ygrid_scale.gridstep > 8) + if (range / im->ygrid_scale.gridstep > 8 + || im->ygrid_scale.gridstep < + 1.8 * im->text_prop[TEXT_PROP_AXIS].size) im->ygrid_scale.labfact = 2; } else { im->ygrid_scale.gridstep /= 5; @@ -1842,27 +1936,28 @@ int calc_horizontal_grid( if (im->unitslength < len + 2) im->unitslength = len + 2; - sprintf(im->ygrid_scale.labfmt, "%%%d.%df%s", len, + sprintf(im->ygrid_scale.labfmt, + "%%%d.%df%s", len, -fractionals, (im->symbol != ' ' ? " %c" : "")); } else { int len = decimals + 1; if (im->unitslength < len + 2) im->unitslength = len + 2; - sprintf(im->ygrid_scale.labfmt, "%%%d.0f%s", len, - (im->symbol != ' ' ? " %c" : "")); + sprintf(im->ygrid_scale.labfmt, + "%%%d.0f%s", len, (im->symbol != ' ' ? " %c" : "")); } - } else { + } else { /* classic rrd grid */ for (i = 0; ylab[i].grid > 0; i++) { pixel = im->ysize / (scaledrange / ylab[i].grid); gridind = i; - if (pixel > 7) + if (pixel >= 5) break; } for (i = 0; i < 4; i++) { if (pixel * ylab[gridind].lfac[i] >= - 2.5 * im->text_prop[TEXT_PROP_AXIS].size) { + 1.8 * im->text_prop[TEXT_PROP_AXIS].size) { im->ygrid_scale.labfact = ylab[gridind].lfac[i]; break; } @@ -1878,7 +1973,8 @@ int calc_horizontal_grid( } int draw_horizontal_grid( - image_desc_t *im) + image_desc_t + *im) { int i; double scaledstep; @@ -1886,21 +1982,24 @@ int draw_horizontal_grid( int nlabels = 0; double X0 = im->xorigin; double X1 = im->xorigin + im->xsize; - int sgrid = (int) (im->minval / im->ygrid_scale.gridstep - 1); int egrid = (int) (im->maxval / im->ygrid_scale.gridstep + 1); double MaxY; - + double second_axis_magfact = 0; + char *second_axis_symb = ""; + scaledstep = - im->ygrid_scale.gridstep / (double) im->magfact * - (double) im->viewfactor; + im->ygrid_scale.gridstep / + (double) im->magfact * (double) im->viewfactor; MaxY = scaledstep * (double) egrid; for (i = sgrid; i <= egrid; i++) { - double Y0 = ytr(im, im->ygrid_scale.gridstep * i); - double YN = ytr(im, im->ygrid_scale.gridstep * (i + 1)); + double Y0 = ytr(im, + im->ygrid_scale.gridstep * i); + double YN = ytr(im, + im->ygrid_scale.gridstep * (i + 1)); - if (floor(Y0 + 0.5) >= im->yorigin - im->ysize - && floor(Y0 + 0.5) <= im->yorigin) { + if (floor(Y0 + 0.5) >= + im->yorigin - im->ysize && floor(Y0 + 0.5) <= im->yorigin) { /* Make sure at least 2 grid labels are shown, even if it doesn't agree with the chosen settings. Add a label if required by settings, or if there is only one label so far and the next grid line is out of bounds. */ @@ -1909,7 +2008,8 @@ int draw_horizontal_grid( && (YN < im->yorigin - im->ysize || YN > im->yorigin))) { if (im->symbol == ' ') { if (im->extra_flags & ALTYGRID) { - sprintf(graph_label, im->ygrid_scale.labfmt, + sprintf(graph_label, + im->ygrid_scale.labfmt, scaledstep * (double) i); } else { if (MaxY < 10) { @@ -1924,7 +2024,8 @@ int draw_horizontal_grid( char sisym = (i == 0 ? ' ' : im->symbol); if (im->extra_flags & ALTYGRID) { - sprintf(graph_label, im->ygrid_scale.labfmt, + sprintf(graph_label, + im->ygrid_scale.labfmt, scaledstep * (double) i, sisym); } else { if (MaxY < 10) { @@ -1937,39 +2038,67 @@ int draw_horizontal_grid( } } nlabels++; - + if (im->second_axis_scale != 0){ + char graph_label_right[100]; + double sval = im->ygrid_scale.gridstep*(double)i*im->second_axis_scale+im->second_axis_shift; + if (im->second_axis_format[0] == '\0'){ + if (!second_axis_magfact){ + double dummy = im->ygrid_scale.gridstep*(double)(sgrid+egrid)/2.0*im->second_axis_scale+im->second_axis_shift; + auto_scale(im,&dummy,&second_axis_symb,&second_axis_magfact); + } + sval /= second_axis_magfact; + + if(MaxY < 10) { + sprintf(graph_label_right,"%5.1f %s",sval,second_axis_symb); + } else { + sprintf(graph_label_right,"%5.0f %s",sval,second_axis_symb); + } + } + else { + sprintf(graph_label_right,im->second_axis_format,sval); + } + gfx_text ( im, + X1+7, Y0, + im->graph_col[GRC_FONT], + im->text_prop[TEXT_PROP_AXIS].font_desc, + im->tabwidth,0.0, GFX_H_LEFT, GFX_V_CENTER, + graph_label_right ); + } + gfx_text(im, - X0 - im->text_prop[TEXT_PROP_AXIS].size, Y0, + X0 - + im-> + text_prop[TEXT_PROP_AXIS]. + size, Y0, im->graph_col[GRC_FONT], - im->text_prop[TEXT_PROP_AXIS].font, - im->text_prop[TEXT_PROP_AXIS].size, - im->tabwidth, 0.0, GFX_H_RIGHT, GFX_V_CENTER, - graph_label); - gfx_line(im, - X0 - 2, Y0, - X0, Y0, MGRIDWIDTH, im->graph_col[GRC_MGRID]); - gfx_line(im, - X1, Y0, - X1 + 2, Y0, MGRIDWIDTH, im->graph_col[GRC_MGRID]); - gfx_dashed_line(im, - X0 - 2, Y0, + im-> + text_prop[TEXT_PROP_AXIS]. + font_desc, + im->tabwidth, 0.0, + GFX_H_RIGHT, GFX_V_CENTER, graph_label); + gfx_line(im, X0 - 2, Y0, X0, Y0, + MGRIDWIDTH, im->graph_col[GRC_MGRID]); + gfx_line(im, X1, Y0, X1 + 2, Y0, + MGRIDWIDTH, im->graph_col[GRC_MGRID]); + gfx_dashed_line(im, X0 - 2, Y0, X1 + 2, Y0, - MGRIDWIDTH, im->graph_col[GRC_MGRID], + MGRIDWIDTH, + im-> + graph_col + [GRC_MGRID], im->grid_dash_on, im->grid_dash_off); - } else if (!(im->extra_flags & NOMINOR)) { gfx_line(im, X0 - 2, Y0, X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); - gfx_line(im, - X1, Y0, - X1 + 2, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); - gfx_dashed_line(im, - X0 - 1, Y0, + gfx_line(im, X1, Y0, X1 + 2, Y0, + GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_dashed_line(im, X0 - 1, Y0, X1 + 1, Y0, - GRIDWIDTH, im->graph_col[GRC_GRID], + GRIDWIDTH, + im-> + graph_col[GRC_GRID], im->grid_dash_on, im->grid_dash_off); - } } } @@ -1987,7 +2116,7 @@ double frexp10( double mnt; int iexp; - iexp = floor(log(fabs(x)) / log(10)); + iexp = floor(log((double)fabs(x)) / log((double)10)); mnt = x / pow(10.0, iexp); if (mnt >= 10.0) { iexp++; @@ -1997,56 +2126,37 @@ double frexp10( return mnt; } -/* 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 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; -} /* logaritmic horizontal grid */ int horizontal_log_grid( - image_desc_t *im) + image_desc_t + *im) { double yloglab[][10] = { - {1.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, - {1.0, 5.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, - {1.0, 2.0, 5.0, 7.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0}, - {1.0, 2.0, 4.0, 6.0, 8.0, 10., 0.0, 0.0, 0.0, 0.0}, - {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} /* last line */ + { + 1.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0}, { + 1.0, 5.0, 10., 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0}, { + 1.0, 2.0, 5.0, 7.0, 10., 0.0, 0.0, + 0.0, 0.0, 0.0}, { + 1.0, 2.0, 4.0, + 6.0, 8.0, 10., + 0.0, + 0.0, 0.0, 0.0}, { + 1.0, + 2.0, + 3.0, + 4.0, + 5.0, + 6.0, + 7.0, + 8.0, + 9.0, + 10.}, + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} /* last line */ }; - int i, j, val_exp, min_exp; double nex; /* number of decades in data */ double logscale; /* scale in logarithmic space */ @@ -2060,7 +2170,6 @@ int horizontal_log_grid( nex = log10(im->maxval / im->minval); logscale = im->ysize / nex; - /* major spacing for data with high dynamic range */ while (logscale * exfrac < 3 * im->text_prop[TEXT_PROP_LEGEND].size) { if (exfrac == 1) @@ -2075,11 +2184,11 @@ int horizontal_log_grid( mid++; for (i = 0; yloglab[mid][i + 1] < 10.0; i++); mspac = logscale * log10(10.0 / yloglab[mid][i]); - } while (mspac > 2 * im->text_prop[TEXT_PROP_LEGEND].size - && yloglab[mid][0] > 0); + } + while (mspac > + 2 * im->text_prop[TEXT_PROP_LEGEND].size && yloglab[mid][0] > 0); if (mid) mid--; - /* find first value in yloglab */ for (flab = 0; yloglab[mid][flab] < 10 @@ -2091,10 +2200,8 @@ int horizontal_log_grid( val_exp = tmp; if (val_exp % exfrac) val_exp += abs(-val_exp % exfrac); - X0 = im->xorigin; X1 = im->xorigin + im->xsize; - /* draw grid */ pre_value = DNAN; while (1) { @@ -2102,27 +2209,21 @@ int horizontal_log_grid( value = yloglab[mid][flab] * pow(10.0, val_exp); if (AlmostEqual2sComplement(value, pre_value, 4)) break; /* it seems we are not converging */ - pre_value = value; - Y0 = ytr(im, value); if (floor(Y0 + 0.5) <= im->yorigin - im->ysize) break; - /* major grid line */ - gfx_line(im, X0 - 2, Y0, X0, Y0, MGRIDWIDTH, im->graph_col[GRC_MGRID]); - gfx_line(im, - X1, Y0, X1 + 2, Y0, MGRIDWIDTH, im->graph_col[GRC_MGRID]); - - - gfx_dashed_line(im, - X0 - 2, Y0, + gfx_line(im, X1, Y0, X1 + 2, Y0, + MGRIDWIDTH, im->graph_col[GRC_MGRID]); + gfx_dashed_line(im, X0 - 2, Y0, X1 + 2, Y0, - MGRIDWIDTH, im->graph_col[GRC_MGRID], - im->grid_dash_on, im->grid_dash_off); - + MGRIDWIDTH, + im-> + graph_col + [GRC_MGRID], im->grid_dash_on, im->grid_dash_off); /* label */ if (im->extra_flags & FORCE_UNITS_SI) { int scale; @@ -2135,23 +2236,52 @@ int horizontal_log_grid( else pvalue = pow(10.0, ((val_exp + 1) % 3) + 2); pvalue *= yloglab[mid][flab]; - - if (((scale + si_symbcenter) < (int) sizeof(si_symbol)) && - ((scale + si_symbcenter) >= 0)) + if (((scale + si_symbcenter) < (int) sizeof(si_symbol)) + && ((scale + si_symbcenter) >= 0)) symbol = si_symbol[scale + si_symbcenter]; else symbol = '?'; - sprintf(graph_label, "%3.0f %c", pvalue, symbol); - } else + } else { sprintf(graph_label, "%3.0e", value); + } + if (im->second_axis_scale != 0){ + char graph_label_right[100]; + double sval = value*im->second_axis_scale+im->second_axis_shift; + if (im->second_axis_format[0] == '\0'){ + if (im->extra_flags & FORCE_UNITS_SI) { + double mfac = 1; + char *symb = ""; + auto_scale(im,&sval,&symb,&mfac); + sprintf(graph_label_right,"%4.0f %s", sval,symb); + } + else { + sprintf(graph_label_right,"%3.0e", sval); + } + } + else { + sprintf(graph_label_right,im->second_axis_format,sval); + } + + gfx_text ( im, + X1+7, Y0, + im->graph_col[GRC_FONT], + im->text_prop[TEXT_PROP_AXIS].font_desc, + im->tabwidth,0.0, GFX_H_LEFT, GFX_V_CENTER, + graph_label_right ); + } + gfx_text(im, - X0 - im->text_prop[TEXT_PROP_AXIS].size, Y0, + X0 - + im-> + text_prop[TEXT_PROP_AXIS]. + size, Y0, im->graph_col[GRC_FONT], - im->text_prop[TEXT_PROP_AXIS].font, - im->text_prop[TEXT_PROP_AXIS].size, - im->tabwidth, 0.0, GFX_H_RIGHT, GFX_V_CENTER, graph_label); - + im-> + text_prop[TEXT_PROP_AXIS]. + font_desc, + im->tabwidth, 0.0, + GFX_H_RIGHT, GFX_V_CENTER, graph_label); /* minor grid */ if (mid < 4 && exfrac == 1) { /* find first and last minor line behind current major line @@ -2173,22 +2303,20 @@ int horizontal_log_grid( value = i * pow(10.0, min_exp); if (value < im->minval) continue; - Y0 = ytr(im, value); if (floor(Y0 + 0.5) <= im->yorigin - im->ysize) break; - /* draw lines */ gfx_line(im, X0 - 2, Y0, X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); - gfx_line(im, - X1, Y0, - X1 + 2, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); - gfx_dashed_line(im, - X0 - 1, Y0, + gfx_line(im, X1, Y0, X1 + 2, Y0, + GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_dashed_line(im, X0 - 1, Y0, X1 + 1, Y0, - GRIDWIDTH, im->graph_col[GRC_GRID], + GRIDWIDTH, + im-> + graph_col[GRC_GRID], im->grid_dash_on, im->grid_dash_off); } } else if (exfrac > 1) { @@ -2196,22 +2324,20 @@ int horizontal_log_grid( value = pow(10.0, i); if (value < im->minval) continue; - Y0 = ytr(im, value); if (floor(Y0 + 0.5) <= im->yorigin - im->ysize) break; - /* draw lines */ gfx_line(im, X0 - 2, Y0, X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); - gfx_line(im, - X1, Y0, - X1 + 2, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); - gfx_dashed_line(im, - X0 - 1, Y0, + gfx_line(im, X1, Y0, X1 + 2, Y0, + GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_dashed_line(im, X0 - 1, Y0, X1 + 1, Y0, - GRIDWIDTH, im->graph_col[GRC_GRID], + GRIDWIDTH, + im-> + graph_col[GRC_GRID], im->grid_dash_on, im->grid_dash_off); } } @@ -2244,20 +2370,19 @@ int horizontal_log_grid( value = i * pow(10.0, min_exp); if (value < im->minval) continue; - Y0 = ytr(im, value); if (floor(Y0 + 0.5) <= im->yorigin - im->ysize) break; - /* draw lines */ gfx_line(im, X0 - 2, Y0, X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); - gfx_line(im, - X1, Y0, X1 + 2, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); - gfx_dashed_line(im, - X0 - 1, Y0, + gfx_line(im, X1, Y0, X1 + 2, Y0, + GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_dashed_line(im, X0 - 1, Y0, X1 + 1, Y0, - GRIDWIDTH, im->graph_col[GRC_GRID], + GRIDWIDTH, + im-> + graph_col[GRC_GRID], im->grid_dash_on, im->grid_dash_off); } } @@ -2267,20 +2392,19 @@ int horizontal_log_grid( value = pow(10.0, i); if (value < im->minval) continue; - Y0 = ytr(im, value); if (floor(Y0 + 0.5) <= im->yorigin - im->ysize) break; - /* draw lines */ gfx_line(im, X0 - 2, Y0, X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); - gfx_line(im, - X1, Y0, X1 + 2, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); - gfx_dashed_line(im, - X0 - 1, Y0, + gfx_line(im, X1, Y0, X1 + 2, Y0, + GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_dashed_line(im, X0 - 1, Y0, X1 + 1, Y0, - GRIDWIDTH, im->graph_col[GRC_GRID], + GRIDWIDTH, + im-> + graph_col[GRC_GRID], im->grid_dash_on, im->grid_dash_off); } } @@ -2301,16 +2425,15 @@ void vertical_grid( /* the type of time grid is determined by finding the number of seconds per pixel in the graph */ - - if (im->xlab_user.minsec == -1) { factor = (im->end - im->start) / im->xsize; xlab_sel = 0; - while (xlab[xlab_sel + 1].minsec != -1 - && xlab[xlab_sel + 1].minsec <= factor) { + while (xlab[xlab_sel + 1].minsec != + -1 && xlab[xlab_sel + 1].minsec <= factor) { xlab_sel++; } /* pick the last one */ - while (xlab[xlab_sel - 1].minsec == xlab[xlab_sel].minsec + while (xlab[xlab_sel - 1].minsec == + xlab[xlab_sel].minsec && xlab[xlab_sel].length > (im->end - im->start)) { xlab_sel--; } /* go back to the smallest size */ @@ -2327,16 +2450,21 @@ void vertical_grid( /* y coords are the same for every line ... */ Y0 = im->yorigin; Y1 = im->yorigin - im->ysize; - - /* paint the minor grid */ if (!(im->extra_flags & NOMINOR)) { for (ti = find_first_time(im->start, - im->xlab_user.gridtm, - im->xlab_user.gridst), - timajor = find_first_time(im->start, - im->xlab_user.mgridtm, - im->xlab_user.mgridst); + im-> + xlab_user. + gridtm, + im-> + xlab_user. + gridst), + timajor = + find_first_time(im->start, + im->xlab_user. + mgridtm, + im->xlab_user. + mgridst); ti < im->end; ti = find_next_time(ti, im->xlab_user.gridtm, im->xlab_user.gridst) @@ -2346,27 +2474,33 @@ void vertical_grid( continue; while (timajor < ti) { timajor = find_next_time(timajor, - im->xlab_user.mgridtm, - im->xlab_user.mgridst); + im-> + xlab_user. + mgridtm, im->xlab_user.mgridst); } if (ti == timajor) continue; /* skip as falls on major grid line */ X0 = xtr(im, ti); - gfx_line(im, X0, Y1 - 2, X0, Y1, GRIDWIDTH, - im->graph_col[GRC_GRID]); - gfx_line(im, X0, Y0, X0, Y0 + 2, GRIDWIDTH, - im->graph_col[GRC_GRID]); - gfx_dashed_line(im, X0, Y0 + 1, X0, Y1 - 1, GRIDWIDTH, - im->graph_col[GRC_GRID], + gfx_line(im, X0, Y1 - 2, X0, Y1, + GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_line(im, X0, Y0, X0, Y0 + 2, + GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_dashed_line(im, X0, Y0 + 1, X0, + Y1 - 1, GRIDWIDTH, + im-> + graph_col[GRC_GRID], im->grid_dash_on, im->grid_dash_off); - } } /* paint the major grid */ for (ti = find_first_time(im->start, - im->xlab_user.mgridtm, - im->xlab_user.mgridst); + im-> + xlab_user. + mgridtm, + im-> + xlab_user. + mgridst); ti < im->end; ti = find_next_time(ti, im->xlab_user.mgridtm, im->xlab_user.mgridst) ) { @@ -2374,27 +2508,34 @@ void vertical_grid( if (ti < im->start || ti > im->end) continue; X0 = xtr(im, ti); - gfx_line(im, X0, Y1 - 2, X0, Y1, MGRIDWIDTH, - im->graph_col[GRC_MGRID]); - gfx_line(im, X0, Y0, X0, Y0 + 3, MGRIDWIDTH, - im->graph_col[GRC_MGRID]); - gfx_dashed_line(im, X0, Y0 + 3, X0, Y1 - 2, MGRIDWIDTH, - im->graph_col[GRC_MGRID], - im->grid_dash_on, im->grid_dash_off); - + gfx_line(im, X0, Y1 - 2, X0, Y1, + MGRIDWIDTH, im->graph_col[GRC_MGRID]); + gfx_line(im, X0, Y0, X0, Y0 + 3, + MGRIDWIDTH, im->graph_col[GRC_MGRID]); + gfx_dashed_line(im, X0, Y0 + 3, X0, + Y1 - 2, MGRIDWIDTH, + im-> + graph_col + [GRC_MGRID], im->grid_dash_on, im->grid_dash_off); } /* paint the labels below the graph */ - for (ti = find_first_time(im->start - im->xlab_user.precis / 2, - im->xlab_user.labtm, - im->xlab_user.labst); - ti <= im->end - im->xlab_user.precis / 2; + for (ti = + find_first_time(im->start - + im->xlab_user. + precis / 2, + im->xlab_user. + labtm, + im->xlab_user. + labst); + ti <= + im->end - + im->xlab_user.precis / 2; 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 */ /* are we inside the graph ? */ if (tilab < im->start || tilab > im->end) continue; - #if HAVE_STRFTIME localtime_r(&tilab, &tm); strftime(graph_label, 99, im->xlab_user.stst, &tm); @@ -2405,10 +2546,11 @@ void vertical_grid( xtr(im, tilab), Y0 + 3, im->graph_col[GRC_FONT], - im->text_prop[TEXT_PROP_AXIS].font, - im->text_prop[TEXT_PROP_AXIS].size, im->tabwidth, 0.0, + im-> + text_prop[TEXT_PROP_AXIS]. + font_desc, + im->tabwidth, 0.0, GFX_H_CENTER, GFX_V_TOP, graph_label); - } } @@ -2426,25 +2568,33 @@ void axis_paint( im->xorigin+im->xsize,im->yorigin-im->ysize, GRIDWIDTH, im->graph_col[GRC_AXIS]); */ - gfx_line(im, im->xorigin - 4, im->yorigin, - im->xorigin + im->xsize + 4, im->yorigin, - MGRIDWIDTH, im->graph_col[GRC_AXIS]); - - gfx_line(im, im->xorigin, im->yorigin + 4, - im->xorigin, im->yorigin - im->ysize - 4, - MGRIDWIDTH, im->graph_col[GRC_AXIS]); - - + gfx_line(im, im->xorigin - 4, + im->yorigin, + im->xorigin + im->xsize + + 4, im->yorigin, MGRIDWIDTH, im->graph_col[GRC_AXIS]); + gfx_line(im, im->xorigin, + im->yorigin + 4, + im->xorigin, + im->yorigin - im->ysize - + 4, MGRIDWIDTH, im->graph_col[GRC_AXIS]); /* arrow for X and Y axis direction */ - 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 - 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); - + if (im->second_axis_scale != 0){ + gfx_line ( im, im->xorigin+im->xsize,im->yorigin+4, + im->xorigin+im->xsize,im->yorigin-im->ysize-4, + MGRIDWIDTH, im->graph_col[GRC_AXIS]); + gfx_new_area ( im, + im->xorigin+im->xsize-2, im->yorigin-im->ysize-2, + im->xorigin+im->xsize+3, im->yorigin-im->ysize-2, + im->xorigin+im->xsize, im->yorigin-im->ysize-7, /* LINEOFFSET */ + im->graph_col[GRC_ARROW]); + gfx_close_path(im); + } } @@ -2463,19 +2613,15 @@ void grid_paint( gfx_add_point(im, im->ximg, 0); gfx_add_point(im, 0, 0); gfx_close_path(im); - gfx_new_area(im, 2, im->yimg - 2, - im->ximg - 2, im->yimg - 2, - im->ximg - 2, 2, im->graph_col[GRC_SHADEB]); + im->ximg - 2, + im->yimg - 2, im->ximg - 2, 2, im->graph_col[GRC_SHADEB]); gfx_add_point(im, im->ximg, 0); gfx_add_point(im, im->ximg, im->yimg); gfx_add_point(im, 0, im->yimg); gfx_close_path(im); - - if (im->draw_x_grid == 1) vertical_grid(im); - if (im->draw_y_grid == 1) { if (im->logarithmic) { res = horizontal_log_grid(im); @@ -2488,46 +2634,72 @@ void grid_paint( char *nodata = "No Data found"; gfx_text(im, im->ximg / 2, - (2 * im->yorigin - im->ysize) / 2, + (2 * im->yorigin - + im->ysize) / 2, im->graph_col[GRC_FONT], - im->text_prop[TEXT_PROP_AXIS].font, - im->text_prop[TEXT_PROP_AXIS].size, im->tabwidth, - 0.0, GFX_H_CENTER, GFX_V_CENTER, nodata); + im-> + text_prop[TEXT_PROP_AXIS]. + font_desc, + im->tabwidth, 0.0, + GFX_H_CENTER, GFX_V_CENTER, nodata); } } /* yaxis unit description */ - gfx_text(im, - 10, (im->yorigin - im->ysize / 2), - im->graph_col[GRC_FONT], - im->text_prop[TEXT_PROP_UNIT].font, - im->text_prop[TEXT_PROP_UNIT].size, im->tabwidth, - RRDGRAPH_YLEGEND_ANGLE, GFX_H_CENTER, GFX_V_CENTER, im->ylegend); + if (im->ylegend[0] != '\0'){ + gfx_text(im, + im->xOriginLegendY+10, + im->yOriginLegendY, + im->graph_col[GRC_FONT], + im-> + text_prop[TEXT_PROP_UNIT]. + font_desc, + im->tabwidth, + RRDGRAPH_YLEGEND_ANGLE, GFX_H_CENTER, GFX_V_CENTER, im->ylegend); + } + if (im->second_axis_legend[0] != '\0'){ + gfx_text( im, + im->xOriginLegendY2+10, + im->yOriginLegendY2, + im->graph_col[GRC_FONT], + im->text_prop[TEXT_PROP_UNIT].font_desc, + im->tabwidth, + RRDGRAPH_YLEGEND_ANGLE, + GFX_H_CENTER, GFX_V_CENTER, + im->second_axis_legend); + } + /* graph title */ gfx_text(im, - im->ximg / 2, 6, + im->xOriginTitle, im->yOriginTitle+6, im->graph_col[GRC_FONT], - im->text_prop[TEXT_PROP_TITLE].font, - im->text_prop[TEXT_PROP_TITLE].size, im->tabwidth, 0.0, - GFX_H_CENTER, GFX_V_TOP, im->title); + im-> + text_prop[TEXT_PROP_TITLE]. + font_desc, + im->tabwidth, 0.0, GFX_H_CENTER, GFX_V_TOP, im->title); /* rrdtool 'logo' */ - water_color = im->graph_col[GRC_FONT]; - water_color.alpha = 0.3; - gfx_text(im, - im->ximg - 4, 5, - water_color, - im->text_prop[TEXT_PROP_AXIS].font, - 5.5, im->tabwidth, -90, - GFX_H_LEFT, GFX_V_TOP, "RRDTOOL / TOBI OETIKER"); - + if (!(im->extra_flags & NO_RRDTOOL_TAG)){ + water_color = im->graph_col[GRC_FONT]; + water_color.alpha = 0.3; + double xpos = im->legendposition == EAST ? im->xOriginLegendY : im->ximg - 4; + gfx_text(im, xpos, 5, + water_color, + im-> + text_prop[TEXT_PROP_WATERMARK]. + font_desc, im->tabwidth, + -90, GFX_H_LEFT, GFX_V_TOP, "RRDTOOL / TOBI OETIKER"); + } /* graph watermark */ if (im->watermark[0] != '\0') { + water_color = im->graph_col[GRC_FONT]; + water_color.alpha = 0.3; gfx_text(im, im->ximg / 2, im->yimg - 6, water_color, - im->text_prop[TEXT_PROP_AXIS].font, - 5.5, im->tabwidth, 0, + im-> + text_prop[TEXT_PROP_WATERMARK]. + font_desc, im->tabwidth, 0, GFX_H_CENTER, GFX_V_BOTTOM, im->watermark); } @@ -2536,16 +2708,16 @@ void grid_paint( for (i = 0; i < im->gdes_c; i++) { if (im->gdes[i].legend[0] == '\0') continue; - /* im->gdes[i].leg_y is the bottom of the legend */ - X0 = im->gdes[i].leg_x; - Y0 = im->gdes[i].leg_y; + X0 = im->xOriginLegend + im->gdes[i].leg_x; + Y0 = im->legenddirection == TOP_DOWN ? im->yOriginLegend + im->gdes[i].leg_y : im->yOriginLegend + im->legendheight - im->gdes[i].leg_y; gfx_text(im, X0, Y0, im->graph_col[GRC_FONT], - im->text_prop[TEXT_PROP_LEGEND].font, - im->text_prop[TEXT_PROP_LEGEND].size, - im->tabwidth, 0.0, GFX_H_LEFT, GFX_V_BOTTOM, - im->gdes[i].legend); + im-> + text_prop + [TEXT_PROP_LEGEND].font_desc, + im->tabwidth, 0.0, + GFX_H_LEFT, GFX_V_BOTTOM, im->gdes[i].legend); /* The legend for GRAPH items starts with "M " to have enough space for the box */ if (im->gdes[i].gf != GF_PRINT && @@ -2553,31 +2725,25 @@ void grid_paint( double boxH, boxV; double X1, Y1; - boxH = gfx_get_text_width(im, 0, - im->text_prop[TEXT_PROP_LEGEND]. - font, - im->text_prop[TEXT_PROP_LEGEND]. - size, im->tabwidth, "o") * 1.2; + im-> + text_prop + [TEXT_PROP_LEGEND]. + font_desc, + im->tabwidth, "o") * 1.2; boxV = boxH; - /* shift the box up a bit */ Y0 -= boxV * 0.4; - /* make sure transparent colors show up the same way as in the graph */ - gfx_new_area(im, X0, Y0 - boxV, X0, Y0, X0 + boxH, Y0, im->graph_col[GRC_BACK]); gfx_add_point(im, X0 + boxH, Y0 - boxV); gfx_close_path(im); - - gfx_new_area(im, - X0, Y0 - boxV, - X0, Y0, X0 + boxH, Y0, im->gdes[i].col); + gfx_new_area(im, X0, Y0 - boxV, X0, + Y0, X0 + boxH, Y0, im->gdes[i].col); gfx_add_point(im, X0 + boxH, Y0 - boxV); gfx_close_path(im); - cairo_save(im->cr); cairo_new_path(im->cr); cairo_set_line_width(im->cr, 1.0); @@ -2590,10 +2756,26 @@ void grid_paint( cairo_line_to(im->cr, X1, Y1); cairo_line_to(im->cr, X0, Y1); cairo_close_path(im->cr); - cairo_set_source_rgba(im->cr, im->graph_col[GRC_FRAME].red, - im->graph_col[GRC_FRAME].green, - im->graph_col[GRC_FRAME].blue, - im->graph_col[GRC_FRAME].alpha); + cairo_set_source_rgba(im->cr, + im-> + graph_col + [GRC_FRAME]. + red, + 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); } @@ -2615,6 +2797,8 @@ int lazy_check( if (im->lazy == 0) return 0; /* no lazy option */ + if (strlen(im->graphfile) == 0) + return 0; /* inmemory option */ if (stat(im->graphfile, &imgstat) != 0) return 0; /* can't stat */ /* one pixel in the existing graph is more then what we would @@ -2636,18 +2820,44 @@ int lazy_check( int graph_size_location( - image_desc_t *im, + image_desc_t + *im, int elements) { /* The actual size of the image to draw is determined from ** several sources. The size given on the command line is ** the graph area but we need more as we have to draw labels - ** and other things outside the graph area + ** and other things outside the graph area. If the option + ** --full-size-mode is selected the size defines the total + ** image size and the size available for the graph is + ** calculated. + */ + + /** +---+-----------------------------------+ + ** | y |...............graph title.........| + ** | +---+-------------------------------+ + ** | a | y | | + ** | x | | | + ** | i | a | | + ** | s | x | main graph area | + ** | | i | | + ** | t | s | | + ** | i | | | + ** | t | l | | + ** | l | b +-------------------------------+ + ** | e | l | x axis labels | + ** +---+---+-------------------------------+ + ** |....................legends............| + ** +---------------------------------------+ + ** | watermark | + ** +---------------------------------------+ */ - int Xvertical = 0, Ytitle = 0, Xylabel = 0, Xmain = 0, Ymain = 0, - Yxlabel = 0, Xspacing = 15, Yspacing = 15, Ywatermark = 4; + int Xvertical = 0, Xvertical2 = 0, Ytitle = + 0, Xylabel = 0, Xmain = 0, Ymain = + 0, Yxlabel = 0, Xspacing = 15, Yspacing = 15, Ywatermark = 4; + // no legends and no the shall be plotted it's easy if (im->extra_flags & ONLY_GRAPH) { im->xorigin = 0; im->ximg = im->xsize; @@ -2657,30 +2867,23 @@ int graph_size_location( return 0; } - /** +---+--------------------------------------------+ - ** | y |...............graph title..................| - ** | +---+-------------------------------+--------+ - ** | a | y | | | - ** | x | | | | - ** | i | a | | pie | - ** | s | x | main graph area | chart | - ** | | i | | area | - ** | t | s | | | - ** | i | | | | - ** | t | l | | | - ** | l | b +-------------------------------+--------+ - ** | e | l | x axis labels | | - ** +---+---+-------------------------------+--------+ - ** |....................legends.....................| - ** +------------------------------------------------+ - ** | watermark | - ** +------------------------------------------------+ - */ + if(im->watermark[0] != '\0') { + Ywatermark = im->text_prop[TEXT_PROP_WATERMARK].size * 2; + } + // calculate the width of the left vertical legend if (im->ylegend[0] != '\0') { Xvertical = im->text_prop[TEXT_PROP_UNIT].size * 2; } + // calculate the width of the right vertical legend + if (im->second_axis_legend[0] != '\0') { + Xvertical2 = im->text_prop[TEXT_PROP_UNIT].size * 2; + } + else{ + Xvertical2 = Xspacing; + } + if (im->title[0] != '\0') { /* The title is placed "inbetween" two text lines so it ** automatically has some vertical spacing. The horizontal @@ -2689,90 +2892,113 @@ int graph_size_location( /* if necessary, reduce the font size of the title until it fits the image width */ Ytitle = im->text_prop[TEXT_PROP_TITLE].size * 2.6 + 10; } + else{ + // we have no title; get a little clearing from the top + Ytitle = 1.5 * Yspacing; + } if (elements) { if (im->draw_x_grid) { + // calculate the height of the horizontal labelling Yxlabel = im->text_prop[TEXT_PROP_AXIS].size * 2.5; } if (im->draw_y_grid || im->forceleftspace) { - Xylabel = gfx_get_text_width(im, 0, - im->text_prop[TEXT_PROP_AXIS].font, - im->text_prop[TEXT_PROP_AXIS].size, - im->tabwidth, "0") * im->unitslength; + // calculate the width of the vertical labelling + Xylabel = + gfx_get_text_width(im, 0, + im->text_prop[TEXT_PROP_AXIS].font_desc, + im->tabwidth, "0") * im->unitslength; } } - if (im->extra_flags & FULL_SIZE_MODE) { - /* The actual size of the image to draw has been determined by the user. + // add some space to the labelling + Xylabel += Xspacing; + + /* If the legend is printed besides the graph the width has to be + ** calculated first. Placing the legend north or south of the + ** graph requires the width calculation first, so the legend is + ** skipped for the moment. + */ + im->legendheight = 0; + im->legendwidth = 0; + if (!(im->extra_flags & NOLEGEND)) { + if(im->legendposition == WEST || im->legendposition == EAST){ + if (leg_place(im, 1) == -1){ + return -1; + } + } + } + + if (im->extra_flags & FULL_SIZE_MODE) { + + /* The actual size of the image to draw has been determined by the user. ** The graph area is the space remaining after accounting for the legend, - ** the watermark, the pie chart, the axis labels, and the title. + ** the watermark, the axis labels, and the title. */ - im->xorigin = 0; im->ximg = im->xsize; im->yimg = im->ysize; - im->yorigin = im->ysize; Xmain = im->ximg; Ymain = im->yimg; - im->yorigin += Ytitle; - /* Now calculate the total size. Insert some spacing where desired. im->xorigin and im->yorigin need to correspond with the lower left corner of the main graph area or, if this one is not set, the imaginary box surrounding the pie chart area. */ - /* Initial size calculation for the main graph area */ - Xmain = im->ximg - (Xylabel + 2 * Xspacing); - if (Xmain) - Xmain -= Xspacing; /* put space between main graph area and right edge */ - im->xorigin = Xspacing + Xylabel; + Xmain -= Xylabel;// + Xspacing; + if((im->legendposition == WEST || im->legendposition == EAST) && !(im->extra_flags & NOLEGEND) ){ + Xmain -= im->legendwidth;// + Xspacing; + } + if (im->second_axis_scale != 0){ + Xmain -= Xylabel; + } + if (!(im->extra_flags & NO_RRDTOOL_TAG)){ + Xmain -= Xspacing; + } - /* the length of the title should not influence with width of the graph - if (Xtitle > im->ximg) im->ximg = Xtitle; */ + Xmain -= Xvertical + Xvertical2; - if (Xvertical) { /* unit description */ - Xmain -= Xvertical; - im->xorigin += Xvertical; + /* limit the remaining space to 0 */ + if(Xmain < 1){ + Xmain = 1; } im->xsize = Xmain; - xtr(im, 0); - - /* The vertical size of the image is known in advance. The main graph area - ** (Ymain) and im->yorigin must be set according to the space requirements - ** of the legend and the axis labels. - */ - if (im->extra_flags & NOLEGEND) { - /* set dimensions correctly if using full size mode with no legend */ - im->yorigin = - im->yimg - im->text_prop[TEXT_PROP_AXIS].size * 2.5 - - Yspacing; - Ymain = im->yorigin; - } else { - /* Determine where to place the legends onto the image. - ** Set Ymain and adjust im->yorigin to match the space requirements. - */ - if (leg_place(im, &Ymain) == -1) - return -1; + /* Putting the legend north or south, the height can now be calculated */ + if (!(im->extra_flags & NOLEGEND)) { + if(im->legendposition == NORTH || im->legendposition == SOUTH){ + im->legendwidth = im->ximg; + if (leg_place(im, 0) == -1){ + return -1; + } + } } + if( (im->legendposition == NORTH || im->legendposition == SOUTH) && !(im->extra_flags & NOLEGEND) ){ + Ymain -= Yxlabel + im->legendheight; + } + else{ + Ymain -= Yxlabel; + } + + /* reserve space for the title *or* some padding above the graph */ + Ymain -= Ytitle; - /* remove title space *or* some padding above the graph from the main graph area */ - if (Ytitle) { - Ymain -= Ytitle; - } else { - Ymain -= 1.5 * Yspacing; + /* reserve space for padding below the graph */ + if (im->extra_flags & NOLEGEND) { + Ymain -= Yspacing; } - /* watermark doesn't seem to effect the vertical size of the main graph area, oh well! */ if (im->watermark[0] != '\0') { Ymain -= Ywatermark; } - + /* limit the remaining height to 0 */ + if(Ymain < 1){ + Ymain = 1; + } im->ysize = Ymain; - } else { /* dimension options -width and -height refer to the dimensions of the main graph area */ /* The actual size of the image to draw is determined from @@ -2781,168 +3007,221 @@ int graph_size_location( ** and other things outside the graph area. */ - if (im->ylegend[0] != '\0') { - Xvertical = im->text_prop[TEXT_PROP_UNIT].size * 2; - } - - - if (im->title[0] != '\0') { - /* The title is placed "inbetween" two text lines so it - ** automatically has some vertical spacing. The horizontal - ** spacing is added here, on each side. - */ - /* don't care for the with of the title - Xtitle = gfx_get_text_width(im->canvas, 0, - im->text_prop[TEXT_PROP_TITLE].font, - im->text_prop[TEXT_PROP_TITLE].size, - im->tabwidth, - im->title, 0) + 2*Xspacing; */ - Ytitle = im->text_prop[TEXT_PROP_TITLE].size * 2.6 + 10; - } - if (elements) { - Xmain = im->xsize; + Xmain = im->xsize; // + Xspacing; Ymain = im->ysize; } - /* Now calculate the total size. Insert some spacing where - desired. im->xorigin and im->yorigin need to correspond - with the lower left corner of the main graph area or, if - this one is not set, the imaginary box surrounding the - pie chart area. */ - - /* The legend width cannot yet be determined, as a result we - ** have problems adjusting the image to it. For now, we just - ** forget about it at all; the legend will have to fit in the - ** size already allocated. - */ - im->ximg = Xylabel + Xmain + 2 * Xspacing; - if (Xmain) + im->ximg = Xmain + Xylabel; + if (!(im->extra_flags & NO_RRDTOOL_TAG)){ im->ximg += Xspacing; + } - im->xorigin = Xspacing + Xylabel; - - /* the length of the title should not influence with width of the graph - if (Xtitle > im->ximg) im->ximg = Xtitle; */ - - if (Xvertical) { /* unit description */ - im->ximg += Xvertical; - im->xorigin += Xvertical; + if( (im->legendposition == WEST || im->legendposition == EAST) && !(im->extra_flags & NOLEGEND) ){ + im->ximg += im->legendwidth;// + Xspacing; + } + if (im->second_axis_scale != 0){ + im->ximg += Xylabel; } - xtr(im, 0); - - /* The vertical size is interesting... we need to compare - ** the sum of {Ytitle, Ymain, Yxlabel, Ylegend, Ywatermark} with - ** Yvertical however we need to know {Ytitle+Ymain+Yxlabel} - ** in order to start even thinking about Ylegend or Ywatermark. - ** - ** Do it in three portions: First calculate the inner part, - ** then do the legend, then adjust the total height of the img, - ** adding space for a watermark if one exists; - */ - /* reserve space for main and/or pie */ + im->ximg += Xvertical + Xvertical2; + if (!(im->extra_flags & NOLEGEND)) { + if(im->legendposition == NORTH || im->legendposition == SOUTH){ + im->legendwidth = im->ximg; + if (leg_place(im, 0) == -1){ + return -1; + } + } + } + im->yimg = Ymain + Yxlabel; - - - im->yorigin = im->yimg - Yxlabel; - + if( (im->legendposition == NORTH || im->legendposition == SOUTH) && !(im->extra_flags & NOLEGEND) ){ + im->yimg += im->legendheight; + } + /* reserve space for the title *or* some padding above the graph */ if (Ytitle) { im->yimg += Ytitle; - im->yorigin += Ytitle; } else { im->yimg += 1.5 * Yspacing; - im->yorigin += 1.5 * Yspacing; } /* reserve space for padding below the graph */ - im->yimg += Yspacing; - - /* Determine where to place the legends onto the image. - ** Adjust im->yimg to match the space requirements. - */ - if (leg_place(im, 0) == -1) - return -1; + if (im->extra_flags & NOLEGEND) { + im->yimg += Yspacing; + } if (im->watermark[0] != '\0') { im->yimg += Ywatermark; } } - ytr(im, DNAN); - return 0; -} + /* In case of putting the legend in west or east position the first + ** legend calculation might lead to wrong positions if some items + ** are not aligned on the left hand side (e.g. centered) as the + ** legendwidth wight have been increased after the item was placed. + ** In this case the positions have to be recalculated. + */ + if (!(im->extra_flags & NOLEGEND)) { + if(im->legendposition == WEST || im->legendposition == EAST){ + if (leg_place(im, 0) == -1){ + return -1; + } + } + } + /* After calculating all dimensions + ** it is now possible to calculate + ** all offsets. + */ + switch(im->legendposition){ + case NORTH: + im->xOriginTitle = Xvertical + Xylabel + (im->xsize / 2); + im->yOriginTitle = 0; -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; + im->xOriginLegend = 0; + im->yOriginLegend = Ytitle; + + im->xOriginLegendY = 0; + im->yOriginLegendY = Ytitle + im->legendheight + (Ymain / 2) + Yxlabel; + + im->xorigin = Xvertical + Xylabel; + im->yorigin = Ytitle + im->legendheight + Ymain; + + im->xOriginLegendY2 = Xvertical + Xylabel + Xmain; + if (im->second_axis_scale != 0){ + im->xOriginLegendY2 += Xylabel; + } + im->yOriginLegendY2 = Ytitle + im->legendheight + (Ymain / 2) + Yxlabel; + + break; + + case WEST: + im->xOriginTitle = im->legendwidth + Xvertical + Xylabel + im->xsize / 2; + im->yOriginTitle = 0; + + im->xOriginLegend = 0; + im->yOriginLegend = Ytitle; + + im->xOriginLegendY = im->legendwidth; + im->yOriginLegendY = Ytitle + (Ymain / 2); + + im->xorigin = im->legendwidth + Xvertical + Xylabel; + im->yorigin = Ytitle + Ymain; + + im->xOriginLegendY2 = im->legendwidth + Xvertical + Xylabel + Xmain; + if (im->second_axis_scale != 0){ + im->xOriginLegendY2 += Xylabel; + } + im->yOriginLegendY2 = Ytitle + (Ymain / 2); + + break; + + case SOUTH: + im->xOriginTitle = Xvertical + Xylabel + im->xsize / 2; + im->yOriginTitle = 0; + + im->xOriginLegend = 0; + im->yOriginLegend = Ytitle + Ymain + Yxlabel; + + im->xOriginLegendY = 0; + im->yOriginLegendY = Ytitle + (Ymain / 2); + + im->xorigin = Xvertical + Xylabel; + im->yorigin = Ytitle + Ymain; + + im->xOriginLegendY2 = Xvertical + Xylabel + Xmain; + if (im->second_axis_scale != 0){ + im->xOriginLegendY2 += Xylabel; + } + im->yOriginLegendY2 = Ytitle + (Ymain / 2); + + break; + + case EAST: + im->xOriginTitle = Xvertical + Xylabel + im->xsize / 2; + im->yOriginTitle = 0; + + im->xOriginLegend = Xvertical + Xylabel + Xmain + Xvertical2; + if (im->second_axis_scale != 0){ + im->xOriginLegend += Xylabel; + } + im->yOriginLegend = Ytitle; + + im->xOriginLegendY = 0; + im->yOriginLegendY = Ytitle + (Ymain / 2); + + im->xorigin = Xvertical + Xylabel; + im->yorigin = Ytitle + Ymain; + + im->xOriginLegendY2 = Xvertical + Xylabel + Xmain; + if (im->second_axis_scale != 0){ + im->xOriginLegendY2 += Xylabel; + } + im->yOriginLegendY2 = Ytitle + (Ymain / 2); + + if (!(im->extra_flags & NO_RRDTOOL_TAG)){ + im->xOriginTitle += Xspacing; + im->xOriginLegend += Xspacing; + im->xOriginLegendY += Xspacing; + im->xorigin += Xspacing; + im->xOriginLegendY2 += Xspacing; + } + break; + } + + xtr(im, 0); + ytr(im, DNAN); + return 0; } -static cairo_status_t cairo_copy_to_buffer( +static cairo_status_t cairo_output( void *closure, - const unsigned char *data, + const unsigned char + *data, unsigned int length) { - image_desc_t *im = closure; + image_desc_t *im = (image_desc_t*)closure; im->rendered_image = - realloc(im->rendered_image, im->rendered_image_size + length); - if (im->rendered_image == NULL) { + (unsigned char*)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, - char ***calcpr) + image_desc_t *im) { int i, ii; int lazy = lazy_check(im); - double areazero = 0.0; graph_desc_t *lastgdes = NULL; + rrd_infoval_t info; - PangoFontMap *font_map = pango_cairo_font_map_get_default(); - - - /* if we are lazy and there is nothing to PRINT ... quit now */ - if (lazy && im->prt_c == 0) - return 0; +// PangoFontMap *font_map = pango_cairo_font_map_get_default(); /* pull the data from the rrd files ... */ - if (data_fetch(im) == -1) return -1; - /* evaluate VDEF and CDEF operations ... */ if (data_calc(im) == -1) return -1; - - /* calculate and PRINT and GPRINT definitions. We have to do it at * this point because it will affect the length of the legends - * if there are no graph elements we stop here ... + * if there are no graph elements (i==0) we stop here ... * if we are lazy, try to quit ... */ - i = print_calc(im, calcpr); + i = print_calc(im); if (i < 0) return -1; - if ((i == 0) || lazy) + + /* if we want and can be lazy ... quit now */ + if (i == 0) return 0; /************************************************************** @@ -2952,32 +3231,54 @@ int graph_paint( if (graph_size_location(im, i) == -1) return -1; + info.u_cnt = im->xorigin; + grinfo_push(im, sprintf_alloc("graph_left"), RD_I_CNT, info); + info.u_cnt = im->yorigin - im->ysize; + grinfo_push(im, sprintf_alloc("graph_top"), RD_I_CNT, info); + info.u_cnt = im->xsize; + grinfo_push(im, sprintf_alloc("graph_width"), RD_I_CNT, info); + info.u_cnt = im->ysize; + grinfo_push(im, sprintf_alloc("graph_height"), RD_I_CNT, info); + info.u_cnt = im->ximg; + grinfo_push(im, sprintf_alloc("image_width"), RD_I_CNT, info); + info.u_cnt = im->yimg; + grinfo_push(im, sprintf_alloc("image_height"), RD_I_CNT, info); + info.u_cnt = im->start; + grinfo_push(im, sprintf_alloc("graph_start"), RD_I_CNT, info); + info.u_cnt = im->end; + grinfo_push(im, sprintf_alloc("graph_end"), RD_I_CNT, info); + + /* if we want and can be lazy ... quit now */ + if (lazy) + return 0; + /* get actual drawing data and find min and max values */ if (data_proc(im) == -1) return -1; - if (!im->logarithmic) { si_unit(im); } + /* identify si magnitude Kilo, Mega Giga ? */ if (!im->rigid && !im->logarithmic) expand_range(im); /* make sure the upper and lower limit are sensible values */ + info.u_val = im->minval; + grinfo_push(im, sprintf_alloc("value_min"), RD_I_VAL, info); + info.u_val = im->maxval; + grinfo_push(im, sprintf_alloc("value_max"), RD_I_VAL, info); + + if (!calc_horizontal_grid(im)) return -1; - /* reset precalc */ ytr(im, DNAN); - /* if (im->gridfit) apply_gridfit(im); */ - - /* the actual graph is created by going through the individual graph elements and then drawing them */ cairo_surface_destroy(im->surface); - switch (im->imgformat) { case IF_PNG: im->surface = @@ -2990,57 +3291,56 @@ int graph_paint( 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); + : cairo_pdf_surface_create_for_stream + (&cairo_output, im, im->ximg * im->zoom, im->yimg * im->zoom); break; case IF_EPS: 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); + ? + cairo_ps_surface_create(im->graphfile, im->ximg * im->zoom, + im->yimg * im->zoom) + : cairo_ps_surface_create_for_stream + (&cairo_output, im, im->ximg * im->zoom, im->yimg * im->zoom); break; case IF_SVG: 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); + ? + cairo_svg_surface_create(im-> + graphfile, + im->ximg * im->zoom, im->yimg * im->zoom) + : cairo_svg_surface_create_for_stream + (&cairo_output, im, im->ximg * im->zoom, im->yimg * im->zoom); + cairo_svg_surface_restrict_to_version + (im->surface, CAIRO_SVG_VERSION_1_1); break; }; + cairo_destroy(im->cr); 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); - - gfx_new_area(im, - 0, 0, - 0, im->yimg, im->ximg, im->yimg, im->graph_col[GRC_BACK]); - +// pango_cairo_font_map_set_resolution(PANGO_CAIRO_FONT_MAP(font_map), 100); + gfx_new_area(im, 0, 0, 0, im->yimg, + im->ximg, im->yimg, im->graph_col[GRC_BACK]); gfx_add_point(im, im->ximg, 0); gfx_close_path(im); - - gfx_new_area(im, - im->xorigin, im->yorigin, - im->xorigin + im->xsize, im->yorigin, - im->xorigin + im->xsize, im->yorigin - im->ysize, - im->graph_col[GRC_CANVAS]); - + gfx_new_area(im, im->xorigin, + im->yorigin, + im->xorigin + + im->xsize, im->yorigin, + im->xorigin + + im->xsize, + im->yorigin - im->ysize, im->graph_col[GRC_CANVAS]); gfx_add_point(im, im->xorigin, im->yorigin - im->ysize); gfx_close_path(im); - + cairo_rectangle(im->cr, im->xorigin, im->yorigin - im->ysize - 1.0, + im->xsize, im->ysize + 2.0); + cairo_clip(im->cr); if (im->minval > 0.0) areazero = im->minval; if (im->maxval < 0.0) areazero = im->maxval; - for (i = 0; i < im->gdes_c; i++) { switch (im->gdes[i].gf) { case GF_CDEF: @@ -3057,24 +3357,25 @@ int graph_paint( break; case GF_TICK: for (ii = 0; ii < im->xsize; ii++) { - if (!isnan(im->gdes[i].p_data[ii]) && - im->gdes[i].p_data[ii] != 0.0) { + if (!isnan(im->gdes[i].p_data[ii]) + && im->gdes[i].p_data[ii] != 0.0) { if (im->gdes[i].yrule > 0) { gfx_line(im, - im->xorigin + ii, im->yorigin, + im->xorigin + ii, + im->yorigin + 1.0, im->xorigin + ii, im->yorigin - - im->gdes[i].yrule * im->ysize, 1.0, - im->gdes[i].col); + im->gdes[i].yrule * + im->ysize, 1.0, im->gdes[i].col); } else if (im->gdes[i].yrule < 0) { gfx_line(im, im->xorigin + ii, - im->yorigin - im->ysize, + im->yorigin - im->ysize - 1.0, im->xorigin + ii, - im->yorigin - (1 - - im->gdes[i].yrule) * + im->yorigin - im->ysize - + im->gdes[i]. + yrule * im->ysize, 1.0, im->gdes[i].col); - } } } @@ -3112,8 +3413,13 @@ int graph_paint( cairo_save(im->cr); 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 @@ -3135,9 +3441,8 @@ int graph_paint( cairo_line_to(im->cr, x, y); } else { double x = ii - 1 + im->xorigin; - double y = ytr(im, - im->gdes[i].p_data[ii - 1]); - + double y = + ytr(im, im->gdes[i].p_data[ii - 1]); gfx_line_fit(im, &x, &y); cairo_move_to(im->cr, x, y); x = ii + im->xorigin; @@ -3162,22 +3467,28 @@ int graph_paint( gfx_line_fit(im, &x1, &y1); cairo_line_to(im->cr, x1, y1); }; - } - cairo_set_source_rgba(im->cr, im->gdes[i].col.red, - im->gdes[i].col.green, - im->gdes[i].col.blue, - im->gdes[i].col.alpha); + cairo_set_source_rgba(im->cr, + im->gdes[i]. + col.red, + im->gdes[i]. + col.green, + im->gdes[i]. + col.blue, im->gdes[i].col.alpha); cairo_set_line_cap(im->cr, CAIRO_LINE_CAP_ROUND); cairo_set_line_join(im->cr, CAIRO_LINE_JOIN_ROUND); cairo_stroke(im->cr); 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++) { @@ -3188,29 +3499,36 @@ int graph_paint( int lastI = 0; while (cntI < idxI - && AlmostEqual2sComplement(foreY[lastI], - foreY[cntI], 4) - && AlmostEqual2sComplement(foreY[lastI], - foreY[cntI + 1], - 4)) { + && + AlmostEqual2sComplement(foreY + [lastI], + foreY[cntI], 4) + && + AlmostEqual2sComplement(foreY + [lastI], + foreY + [cntI + 1], 4)) { cntI++; } gfx_new_area(im, backX[0], backY[0], foreX[0], foreY[0], - foreX[cntI], foreY[cntI], - im->gdes[i].col); + foreX[cntI], + foreY[cntI], im->gdes[i].col); while (cntI < idxI) { lastI = cntI; cntI++; while (cntI < idxI && - AlmostEqual2sComplement(foreY[lastI], + AlmostEqual2sComplement(foreY + [lastI], foreY[cntI], 4) && - AlmostEqual2sComplement(foreY[lastI], - foreY[cntI + - 1], 4)) { + AlmostEqual2sComplement(foreY + [lastI], + foreY + [cntI + + 1], 4)) { cntI++; } gfx_add_point(im, foreX[cntI], foreY[cntI]); @@ -3221,12 +3539,15 @@ int graph_paint( idxI--; while (idxI > 1 && - AlmostEqual2sComplement(backY[lastI], + AlmostEqual2sComplement(backY + [lastI], backY[idxI], 4) && - AlmostEqual2sComplement(backY[lastI], - backY[idxI - - 1], 4)) { + AlmostEqual2sComplement(backY + [lastI], + backY + [idxI + - 1], 4)) { idxI--; } gfx_add_point(im, backX[idxI], backY[idxI]); @@ -3241,7 +3562,6 @@ int graph_paint( } if (ii == im->xsize) break; - if (im->slopemode == 0 && ii == 0) { continue; } @@ -3302,38 +3622,55 @@ int graph_paint( ("STACK should already be turned into LINE or AREA here"); return -1; break; - } /* switch */ } + cairo_reset_clip(im->cr); /* grid_paint also does the text */ if (!(im->extra_flags & ONLY_GRAPH)) grid_paint(im); - - if (!(im->extra_flags & ONLY_GRAPH)) axis_paint(im); - /* the RULES are the last thing to paint ... */ for (i = 0; i < im->gdes_c; i++) { switch (im->gdes[i].gf) { 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); + && 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, 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; @@ -3346,25 +3683,17 @@ int graph_paint( { 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); - } + status = strlen(im->graphfile) ? + cairo_surface_write_to_png(im->surface, im->graphfile) + : cairo_surface_write_to_png_stream(im->surface, &cairo_output, + im); if (status != CAIRO_STATUS_SUCCESS) { rrd_set_error("Could not save png to '%s'", im->graphfile); return 1; } - } break; + } default: if (strlen(im->graphfile)) { cairo_show_page(im->cr); @@ -3373,6 +3702,7 @@ int graph_paint( } break; } + return 0; } @@ -3386,9 +3716,9 @@ int gdes_alloc( { im->gdes_c++; - if ((im->gdes = (graph_desc_t *) rrd_realloc(im->gdes, (im->gdes_c) - * sizeof(graph_desc_t))) == - NULL) { + if ((im->gdes = (graph_desc_t *) + rrd_realloc(im->gdes, (im->gdes_c) + * sizeof(graph_desc_t))) == NULL) { rrd_set_error("realloc graph_descs"); return -1; } @@ -3409,7 +3739,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; @@ -3421,7 +3755,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; @@ -3437,9 +3770,9 @@ int scan_for_col( int inp, outp = 0; for (inp = 0; inp < len && input[inp] != ':' && input[inp] != '\0'; inp++) { - if (input[inp] == '\\' && - input[inp + 1] != '\0' && - (input[inp + 1] == '\\' || input[inp + 1] == ':')) { + if (input[inp] == '\\' + && input[inp + 1] != '\0' + && (input[inp + 1] == '\\' || input[inp + 1] == ':')) { output[outp++] = input[++inp]; } else { output[outp++] = input[inp]; @@ -3449,12 +3782,7 @@ int scan_for_col( return inp; } -/* Some surgery done on this function, it became ridiculously big. -** Things moved: -** - initializing now in rrd_graph_init() -** - options parsing now in rrd_graph_options() -** - script parsing now in rrd_graph_script() -*/ +/* Now just a wrapper around rrd_graph_v */ int rrd_graph( int argc, char **argv, @@ -3465,146 +3793,189 @@ int rrd_graph( double *ymin, double *ymax) { - image_desc_t im; + int prlines = 0; + rrd_info_t *grinfo = NULL; + rrd_info_t *walker; - rrd_graph_init(&im); + grinfo = rrd_graph_v(argc, argv); + if (grinfo == NULL) + return -1; + walker = grinfo; + (*prdata) = NULL; + while (walker) { + if (strcmp(walker->key, "image_info") == 0) { + prlines++; + if (((*prdata) = + (char**)rrd_realloc((*prdata), + (prlines + 1) * sizeof(char *))) == NULL) { + rrd_set_error("realloc prdata"); + return 0; + } + /* imginfo goes to position 0 in the prdata array */ + (*prdata)[prlines - 1] = (char*)malloc((strlen(walker->value.u_str) + + 2) * sizeof(char)); + strcpy((*prdata)[prlines - 1], walker->value.u_str); + (*prdata)[prlines] = NULL; + } + /* skip anything else */ + walker = walker->next; + } + walker = grinfo; + *xsize = 0; + *ysize = 0; + *ymin = 0; + *ymax = 0; + while (walker) { + if (strcmp(walker->key, "image_width") == 0) { + *xsize = walker->value.u_cnt; + } else if (strcmp(walker->key, "image_height") == 0) { + *ysize = walker->value.u_cnt; + } else if (strcmp(walker->key, "value_min") == 0) { + *ymin = walker->value.u_val; + } else if (strcmp(walker->key, "value_max") == 0) { + *ymax = walker->value.u_val; + } else if (strncmp(walker->key, "print", 5) == 0) { /* keys are prdate[0..] */ + prlines++; + if (((*prdata) = + (char**)rrd_realloc((*prdata), + (prlines + 1) * sizeof(char *))) == NULL) { + rrd_set_error("realloc prdata"); + return 0; + } + (*prdata)[prlines - 1] = (char*)malloc((strlen(walker->value.u_str) + + 2) * sizeof(char)); + (*prdata)[prlines] = NULL; + strcpy((*prdata)[prlines - 1], walker->value.u_str); + } else if (strcmp(walker->key, "image") == 0) { + if ( fwrite(walker->value.u_blo.ptr, walker->value.u_blo.size, 1, + (stream ? stream : stdout)) == 0 && ferror(stream ? stream : stdout)){ + rrd_set_error("writing image"); + return 0; + } + } + /* skip anything else */ + walker = walker->next; + } + rrd_info_free(grinfo); + 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); - im.graphhandle = stream; +/* Some surgery done on this function, it became ridiculously big. +** Things moved: +** - initializing now in rrd_graph_init() +** - options parsing now in rrd_graph_options() +** - script parsing now in rrd_graph_script() +*/ +rrd_info_t *rrd_graph_v( + int argc, + char **argv) +{ + image_desc_t im; + rrd_info_t *grinfo; + rrd_graph_init(&im); + /* a dummy surface so that we can measure text sizes for placements */ + rrd_graph_options(argc, argv, &im); if (rrd_test_error()) { + rrd_info_free(im.grinfo); im_free(&im); - return -1; + return NULL; } if (optind >= argc) { + rrd_info_free(im.grinfo); + im_free(&im); rrd_set_error("missing filename"); - return -1; + return NULL; } if (strlen(argv[optind]) >= MAXPATH) { rrd_set_error("filename (including path) too long"); + rrd_info_free(im.grinfo); im_free(&im); - return -1; + return NULL; } strncpy(im.graphfile, argv[optind], MAXPATH - 1); im.graphfile[MAXPATH - 1] = '\0'; + if (strcmp(im.graphfile, "-") == 0) { + im.graphfile[0] = '\0'; + } + rrd_graph_script(argc, argv, &im, 1); if (rrd_test_error()) { + rrd_info_free(im.grinfo); im_free(&im); - return -1; + return NULL; } /* Everything is now read and the actual work can start */ - (*prdata) = NULL; - if (graph_paint(&im, prdata) == -1) { + if (graph_paint(&im) == -1) { + rrd_info_free(im.grinfo); im_free(&im); - return -1; + return NULL; } + /* The image is generated and needs to be output. ** Also, if needed, print a line with information about the image. */ - *xsize = im.ximg; - *ysize = im.yimg; - *ymin = im.minval; - *ymax = im.maxval; if (im.imginfo) { + rrd_infoval_t info; + char *path; char *filename; - if (!(*prdata)) { - /* maybe prdata is not allocated yet ... lets do it now */ - if ((*prdata = calloc(2, sizeof(char *))) == NULL) { - rrd_set_error("malloc imginfo"); - return -1; - }; - } - if (((*prdata)[0] = - malloc((strlen(im.imginfo) + 200 + - strlen(im.graphfile)) * sizeof(char))) - == NULL) { - rrd_set_error("malloc imginfo"); - return -1; - } - filename = im.graphfile + strlen(im.graphfile); - while (filename > im.graphfile) { - if (*(filename - 1) == '/' || *(filename - 1) == '\\') - break; - filename--; - } - - sprintf((*prdata)[0], im.imginfo, filename, - (long) (im.zoom * im.ximg), (long) (im.zoom * im.yimg)); - } - im_free(&im); - 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; + path = strdup(im.graphfile); + filename = basename(path); + info.u_str = + sprintf_alloc(im.imginfo, + filename, + (long) (im.zoom * + im.ximg), (long) (im.zoom * im.yimg)); + grinfo_push(&im, sprintf_alloc("image_info"), RD_I_STR, info); + free(info.u_str); + free(path); } + if (im.rendered_image) { + rrd_infoval_t img; - /* 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; + img.u_blo.size = im.rendered_image_size; + img.u_blo.ptr = im.rendered_image; + grinfo_push(&im, sprintf_alloc("image"), RD_I_BLO, img); } - - *xsize = im.ximg; - *ysize = im.yimg; - *ymin = im.minval; - *ymax = im.maxval; - *img_size = im.rendered_image_size; + grinfo = im.grinfo; im_free(&im); + return grinfo; +} - return im.rendered_image; +static void +rrd_set_font_desc ( + image_desc_t *im,int prop,char *font, double size ){ + 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'; + im->text_prop[prop].font_desc = pango_font_description_from_string( font ); + }; + if (size > 0){ + im->text_prop[prop].size = size; + }; + if (im->text_prop[prop].font_desc && im->text_prop[prop].size ){ + pango_font_description_set_size(im->text_prop[prop].font_desc, im->text_prop[prop].size * PANGO_SCALE); + }; } void rrd_graph_init( - image_desc_t *im) + image_desc_t + *im) { unsigned int i; + char *deffont = getenv("RRD_DEFAULT_FONT"); + static PangoFontMap *fontmap = NULL; + PangoContext *context; #ifdef HAVE_TZSET tzset(); @@ -3615,178 +3986,195 @@ void rrd_graph_init( setlocale(LC_CTYPE, ""); #endif #endif - im->yorigin = 0; - im->xorigin = 0; + im->base = 1000; + im->daemon_addr = NULL; + im->draw_x_grid = 1; + im->draw_y_grid = 1; + im->extra_flags = 0; + im->font_options = cairo_font_options_create(); + im->forceleftspace = 0; + im->gdes_c = 0; + im->gdes = NULL; + im->graph_antialias = CAIRO_ANTIALIAS_GRAY; + im->grid_dash_off = 1; + im->grid_dash_on = 1; + im->gridfit = 1; + im->grinfo = (rrd_info_t *) NULL; + im->grinfo_current = (rrd_info_t *) NULL; + im->imgformat = IF_PNG; + im->imginfo = NULL; + im->lazy = 0; + im->legenddirection = TOP_DOWN; + im->legendheight = 0; + im->legendposition = SOUTH; + im->legendwidth = 0; + im->logarithmic = 0; + im->maxval = DNAN; im->minval = 0; - im->xlab_user.minsec = -1; - im->ximg = 0; - im->yimg = 0; - im->xsize = 400; - im->ysize = 100; + im->minval = DNAN; + im->prt_c = 0; + im->rigid = 0; im->rendered_image_size = 0; im->rendered_image = NULL; + im->slopemode = 0; im->step = 0; - im->ylegend[0] = '\0'; + im->symbol = ' '; + im->tabwidth = 40.0; im->title[0] = '\0'; - im->watermark[0] = '\0'; - im->minval = DNAN; - im->maxval = DNAN; im->unitsexponent = 9999; im->unitslength = 6; - im->forceleftspace = 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; - im->rigid = 0; - im->gridfit = 1; - im->imginfo = NULL; - im->lazy = 0; - im->slopemode = 0; - im->logarithmic = 0; + im->watermark[0] = '\0'; + im->with_markup = 0; + im->ximg = 0; + im->xlab_user.minsec = -1; + im->xorigin = 0; + im->xOriginLegend = 0; + im->xOriginLegendY = 0; + im->xOriginLegendY2 = 0; + im->xOriginTitle = 0; + im->xsize = 400; im->ygridstep = DNAN; - im->draw_x_grid = 1; - im->draw_y_grid = 1; - im->base = 1000; - im->prt_c = 0; - im->gdes_c = 0; - im->gdes = NULL; - im->grid_dash_on = 1; - im->grid_dash_off = 1; - im->tabwidth = 40.0; + im->yimg = 0; + im->ylegend[0] = '\0'; + im->second_axis_scale = 0; /* 0 disables it */ + im->second_axis_shift = 0; /* no shift by default */ + im->second_axis_legend[0] = '\0'; + im->second_axis_format[0] = '\0'; + im->yorigin = 0; + im->yOriginLegend = 0; + im->yOriginLegendY = 0; + im->yOriginLegendY2 = 0; + im->yOriginTitle = 0; + im->ysize = 100; im->zoom = 1; - im->font_options = cairo_font_options_create(); - im->graph_antialias = CAIRO_ANTIALIAS_GRAY; - cairo_font_options_set_hint_style(im->font_options, - CAIRO_HINT_STYLE_FULL); - cairo_font_options_set_hint_metrics(im->font_options, - CAIRO_HINT_METRICS_ON); + im->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 10, 10); + im->cr = cairo_create(im->surface); + + for (i = 0; i < DIM(text_prop); i++) { + im->text_prop[i].size = -1; + rrd_set_font_desc(im,i, deffont ? deffont : text_prop[i].font,text_prop[i].size); + } + + if (fontmap == NULL){ + fontmap = pango_cairo_font_map_get_default(); + } + + context = pango_cairo_font_map_create_context((PangoCairoFontMap*)fontmap); + + pango_cairo_context_set_resolution(context, 100); + + pango_cairo_update_context(im->cr,context); + + im->layout = pango_layout_new(context); + +// im->layout = pango_cairo_create_layout(im->cr); + + + cairo_font_options_set_hint_style + (im->font_options, CAIRO_HINT_STYLE_FULL); + cairo_font_options_set_hint_metrics + (im->font_options, CAIRO_HINT_METRICS_ON); cairo_font_options_set_antialias(im->font_options, CAIRO_ANTIALIAS_GRAY); + for (i = 0; i < DIM(graph_col); i++) im->graph_col[i] = graph_col[i]; -#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__) - { - char *windir; - char rrd_win_default_font[1000]; - - windir = getenv("windir"); - /* %windir% is something like D:\windows or C:\winnt */ - if (windir != NULL) { - strncpy(rrd_win_default_font, windir, 500); - rrd_win_default_font[500] = '\0'; - strcat(rrd_win_default_font, "\\fonts\\"); - strcat(rrd_win_default_font, RRD_DEFAULT_FONT); - for (i = 0; i < DIM(text_prop); i++) { - strncpy(text_prop[i].font, rrd_win_default_font, - sizeof(text_prop[i].font) - 1); - text_prop[i].font[sizeof(text_prop[i].font) - 1] = '\0'; - } - } - } -#endif - { - char *deffont; - - deffont = getenv("RRD_DEFAULT_FONT"); - if (deffont != NULL) { - for (i = 0; i < DIM(text_prop); i++) { - strncpy(text_prop[i].font, deffont, - sizeof(text_prop[i].font) - 1); - text_prop[i].font[sizeof(text_prop[i].font) - 1] = '\0'; - } - } - } - for (i = 0; i < DIM(text_prop); i++) { - im->text_prop[i].size = text_prop[i].size; - strcpy(im->text_prop[i].font, text_prop[i].font); - } + } + void rrd_graph_options( int argc, char *argv[], - image_desc_t *im) + image_desc_t + *im) { int stroff; char *parsetime_error = NULL; char scan_gtm[12], scan_mtm[12], scan_ltm[12], col_nam[12]; time_t start_tmp = 0, end_tmp = 0; long long_tmp; - struct rrd_time_value start_tv, end_tv; + 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 */ #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} - }; + +/* *INDENT-OFF* */ + 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'}, + { "legend-position", required_argument, 0, 1005}, + { "legend-direction", required_argument, 0, 1006}, + { "force-rules-legend", no_argument, 0, 'F'}, + { "only-graph", no_argument, 0, 'j'}, + { "alt-y-grid", no_argument, 0, 'Y'}, + {"disable-rrdtool-tag", no_argument, 0, 1001}, + {"right-axis", required_argument, 0, 1002}, + {"right-axis-label", required_argument, 0, 1003}, + {"right-axis-format", required_argument, 0, 1004}, + { "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 */ + { "pango-markup", no_argument, 0, 'P'}, + { "daemon", required_argument, 0, 'd'}, + { 0, 0, 0, 0} +}; +/* *INDENT-ON* */ optind = 0; opterr = 0; /* initialize getopt */ - - parsetime("end-24h", &start_tv); - parsetime("now", &end_tv); - + rrd_parsetime("end-24h", &start_tv); + rrd_parsetime("now", &end_tv); while (1) { int option_index = 0; int opt; int col_start, col_end; opt = getopt_long(argc, argv, - "s:e:x:y:v:w:h:D:iu:l:rb:oc:n:m:t:f:a:I:zgjFYAMEX:L:S:T:NR:B:W:", + "s:e:x:y:v:w:h:D:iu:l:rb:oc:n:m:t:f:a:I:zgjFYAMEX:L:S:T:NR:B:W:kPd:", long_options, &option_index); - if (opt == EOF) break; - switch (opt) { case 'I': im->extra_flags |= NOMINOR; @@ -3809,12 +4197,40 @@ void rrd_graph_options( case 'g': im->extra_flags |= NOLEGEND; break; + case 1005: + if (strcmp(optarg, "north") == 0) { + im->legendposition = NORTH; + } else if (strcmp(optarg, "west") == 0) { + im->legendposition = WEST; + } else if (strcmp(optarg, "south") == 0) { + im->legendposition = SOUTH; + } else if (strcmp(optarg, "east") == 0) { + im->legendposition = EAST; + } else { + rrd_set_error("unknown legend-position '%s'", optarg); + return; + } + break; + case 1006: + if (strcmp(optarg, "topdown") == 0) { + im->legenddirection = TOP_DOWN; + } else if (strcmp(optarg, "bottomup") == 0) { + im->legenddirection = BOTTOM_UP; + } else { + rrd_set_error("unknown legend-position '%s'", optarg); + return; + } + break; case 'F': im->extra_flags |= FORCE_RULES_LEGEND; break; + case 1001: + im->extra_flags |= NO_RRDTOOL_TAG; + 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) @@ -3832,22 +4248,29 @@ 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; break; + case 'P': + im->with_markup = 1; + break; case 's': - if ((parsetime_error = parsetime(optarg, &start_tv))) { + if ((parsetime_error = rrd_parsetime(optarg, &start_tv))) { rrd_set_error("start time: %s", parsetime_error); return; } break; case 'e': - if ((parsetime_error = parsetime(optarg, &end_tv))) { + if ((parsetime_error = rrd_parsetime(optarg, &end_tv))) { rrd_set_error("end time: %s", parsetime_error); return; } @@ -3857,7 +4280,6 @@ void rrd_graph_options( im->draw_x_grid = 0; break; }; - if (sscanf(optarg, "%10[A-Z]:%ld:%10[A-Z]:%ld:%10[A-Z]:%ld:%ld:%n", scan_gtm, @@ -3870,15 +4292,17 @@ void rrd_graph_options( strncpy(im->xlab_form, optarg + stroff, sizeof(im->xlab_form) - 1); im->xlab_form[sizeof(im->xlab_form) - 1] = '\0'; - if ((int) (im->xlab_user.gridtm = tmt_conv(scan_gtm)) == -1) { + if ((int) + (im->xlab_user.gridtm = tmt_conv(scan_gtm)) == -1) { rrd_set_error("unknown keyword %s", scan_gtm); return; - } else if ((int) (im->xlab_user.mgridtm = tmt_conv(scan_mtm)) + } else if ((int) + (im->xlab_user.mgridtm = tmt_conv(scan_mtm)) == -1) { rrd_set_error("unknown keyword %s", scan_mtm); return; - } else if ((int) (im->xlab_user.labtm = tmt_conv(scan_ltm)) == - -1) { + } else if ((int) + (im->xlab_user.labtm = tmt_conv(scan_ltm)) == -1) { rrd_set_error("unknown keyword %s", scan_ltm); return; } @@ -3895,8 +4319,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; @@ -3905,19 +4330,51 @@ void rrd_graph_options( return; } } else { + setlocale(LC_NUMERIC, old_locale); rrd_set_error("invalid y-grid format"); return; } break; + case 1002: /* right y axis */ + + if(sscanf(optarg, + "%lf:%lf", + &im->second_axis_scale, + &im->second_axis_shift) == 2) { + if(im->second_axis_scale==0){ + rrd_set_error("the second_axis_scale must not be 0"); + return; + } + } else { + rrd_set_error("invalid right-axis format expected scale:shift"); + return; + } + break; + case 1003: + strncpy(im->second_axis_legend,optarg,150); + im->second_axis_legend[150]='\0'; + break; + case 1004: + if (bad_format(optarg)){ + rrd_set_error("use either %le or %lf formats"); + return; + } + strncpy(im->second_axis_format,optarg,150); + im->second_axis_format[150]='\0'; + break; case 'v': strncpy(im->ylegend, optarg, 150); 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); @@ -3956,7 +4413,8 @@ void rrd_graph_options( im->imginfo = optarg; break; case 'a': - if ((int) (im->imgformat = if_conv(optarg)) == -1) { + if ((int) + (im->imgformat = if_conv(optarg)) == -1) { rrd_set_error("unsupported graphics format '%s'", optarg); return; } @@ -3967,7 +4425,6 @@ void rrd_graph_options( case 'E': im->slopemode = 1; break; - case 'o': im->logarithmic = 1; break; @@ -3980,15 +4437,21 @@ void rrd_graph_options( switch (col_len) { case 3: - color = (((color & 0xF00) * 0x110000) | - ((color & 0x0F0) * 0x011000) | - ((color & 0x00F) * 0x001100) | 0x000000FF); + color = + (((color & 0xF00) * 0x110000) | ((color & 0x0F0) * + 0x011000) | + ((color & 0x00F) + * 0x001100) + | 0x000000FF); break; case 4: - color = (((color & 0xF000) * 0x11000) | - ((color & 0x0F00) * 0x01100) | - ((color & 0x00F0) * 0x00110) | - ((color & 0x000F) * 0x00011) + color = + (((color & 0xF000) * + 0x11000) | ((color & 0x0F00) * + 0x01100) | ((color & + 0x00F0) * + 0x00110) | + ((color & 0x000F) * 0x00011) ); break; case 6: @@ -4014,20 +4477,31 @@ 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++) { + for (propidx = sindex; + propidx < TEXT_PROP_LAST; propidx++) { if (size > 0) { - im->text_prop[propidx].size = size; + rrd_set_font_desc(im,propidx,NULL,size); } - if (strlen(font) > 0) { - strcpy(im->text_prop[propidx].font, font); + if ((int) strlen(optarg) > end+2) { + if (optarg[end] == ':') { + rrd_set_font_desc(im,propidx,optarg + end + 1,0); + } else { + rrd_set_error + ("expected : after font size in '%s'", + optarg); + return; + } } + /* only run the for loop for DEFAULT (0) for + all others, we break here. woodo programming */ if (propidx == sindex && sindex != 0) break; } @@ -4036,13 +4510,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; @@ -4052,23 +4529,22 @@ void rrd_graph_options( strncpy(im->title, optarg, 150); im->title[150] = '\0'; break; - case 'R': if (strcmp(optarg, "normal") == 0) { - cairo_font_options_set_antialias(im->font_options, - CAIRO_ANTIALIAS_GRAY); - cairo_font_options_set_hint_style(im->font_options, - CAIRO_HINT_STYLE_FULL); + cairo_font_options_set_antialias + (im->font_options, CAIRO_ANTIALIAS_GRAY); + cairo_font_options_set_hint_style + (im->font_options, CAIRO_HINT_STYLE_FULL); } else if (strcmp(optarg, "light") == 0) { - cairo_font_options_set_antialias(im->font_options, - CAIRO_ANTIALIAS_GRAY); - cairo_font_options_set_hint_style(im->font_options, - CAIRO_HINT_STYLE_SLIGHT); + cairo_font_options_set_antialias + (im->font_options, CAIRO_ANTIALIAS_GRAY); + cairo_font_options_set_hint_style + (im->font_options, CAIRO_HINT_STYLE_SLIGHT); } else if (strcmp(optarg, "mono") == 0) { - cairo_font_options_set_antialias(im->font_options, - CAIRO_ANTIALIAS_NONE); - cairo_font_options_set_hint_style(im->font_options, - CAIRO_HINT_STYLE_FULL); + cairo_font_options_set_antialias + (im->font_options, CAIRO_ANTIALIAS_NONE); + cairo_font_options_set_hint_style + (im->font_options, CAIRO_HINT_STYLE_FULL); } else { rrd_set_error("unknown font-render-mode '%s'", optarg); return; @@ -4087,12 +4563,28 @@ void rrd_graph_options( case 'B': /* not supported curently */ break; - case 'W': strncpy(im->watermark, optarg, 100); im->watermark[99] = '\0'; break; + case 'd': + { + if (im->daemon_addr != NULL) + { + rrd_set_error ("You cannot specify --daemon " + "more than once."); + return; + } + + im->daemon_addr = strdup(optarg); + if (im->daemon_addr == NULL) + { + rrd_set_error("strdup failed"); + return; + } + break; + } case '?': if (optopt != 0) rrd_set_error("unknown option '%c'", optopt); @@ -4100,28 +4592,39 @@ void rrd_graph_options( rrd_set_error("unknown option '%s'", argv[optind - 1]); return; } + } /* while (1) */ + + { /* try to connect to rrdcached */ + int status = rrdc_connect(im->daemon_addr); + if (status != 0) return; } + + pango_cairo_context_set_font_options(pango_layout_get_context(im->layout), im->font_options); + pango_layout_context_changed(im->layout); - if (im->logarithmic == 1 && im->minval <= 0) { + + + if (im->logarithmic && im->minval <= 0) { rrd_set_error ("for a logarithmic yaxis you must specify a lower-limit > 0"); return; } - if (proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) { - /* error string is set in parsetime.c */ + if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) { + /* error string is set in rrd_parsetime.c */ return; } if (start_tmp < 3600 * 24 * 365 * 10) { - rrd_set_error("the first entry to fetch should be after 1980 (%ld)", - start_tmp); + rrd_set_error + ("the first entry to fetch should be after 1980 (%ld)", + start_tmp); return; } if (end_tmp < start_tmp) { - rrd_set_error("start (%ld) should be less than end (%ld)", - start_tmp, end_tmp); + rrd_set_error + ("start (%ld) should be less than end (%ld)", start_tmp, end_tmp); return; } @@ -4131,7 +4634,8 @@ void rrd_graph_options( } int rrd_graph_color( - image_desc_t *im, + image_desc_t + *im, char *var, char *err, int optional) @@ -4156,7 +4660,6 @@ int rrd_graph_color( n = rest - color; else n = strlen(color); - switch (n) { case 7: sscanf(color, "#%6lx%n", &col, &n); @@ -4192,11 +4695,9 @@ int bad_format( /* line cannot end with percent char */ if (*ptr == '\0') return 1; - /* '%s', '%S' and '%%' are allowed */ if (*ptr == 's' || *ptr == 'S' || *ptr == '%') ptr++; - /* %c is allowed (but use only with vdef!) */ else if (*ptr == 'c') { ptr++; @@ -4208,7 +4709,6 @@ int bad_format( /* optional padding character */ if (*ptr == ' ' || *ptr == '+' || *ptr == '-') ptr++; - /* This should take care of 'm.n' with all three optional */ while (*ptr >= '0' && *ptr <= '9') ptr++; @@ -4216,7 +4716,6 @@ int bad_format( ptr++; while (*ptr >= '0' && *ptr <= '9') ptr++; - /* Either 'le', 'lf' or 'lg' must follow here */ if (*ptr++ != 'l') return 1; @@ -4233,7 +4732,8 @@ int bad_format( int vdef_parse( - struct graph_desc_t *gdes, + struct graph_desc_t + *gdes, const char *const str) { /* A VDEF currently is either "func" or "param,func" @@ -4242,9 +4742,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 { @@ -4253,17 +4756,22 @@ int vdef_parse( if (n == (int) strlen(str)) { /* matched */ param = DNAN; } else { - rrd_set_error("Unknown function string '%s' in VDEF '%s'", str, - gdes->vname); + rrd_set_error + ("Unknown function string '%s' in VDEF '%s'", + str, gdes->vname); return -1; } } if (!strcmp("PERCENT", func)) gdes->vf.op = VDEF_PERCENT; + else if (!strcmp("PERCENTNAN", func)) + gdes->vf.op = VDEF_PERCENTNAN; else if (!strcmp("MAXIMUM", func)) 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)) @@ -4279,16 +4787,17 @@ int vdef_parse( else if (!strcmp("LSLCORREL", func)) gdes->vf.op = VDEF_LSLCORREL; else { - rrd_set_error("Unknown function '%s' in VDEF '%s'\n", func, - gdes->vname); + rrd_set_error + ("Unknown function '%s' in VDEF '%s'\n", func, gdes->vname); return -1; }; - switch (gdes->vf.op) { case VDEF_PERCENT: + case VDEF_PERCENTNAN: if (isnan(param)) { /* no parameter given */ - rrd_set_error("Function '%s' needs parameter in VDEF '%s'\n", - func, gdes->vname); + rrd_set_error + ("Function '%s' needs parameter in VDEF '%s'\n", + func, gdes->vname); return -1; }; if (param >= 0.0 && param <= 100.0) { @@ -4296,13 +4805,15 @@ int vdef_parse( gdes->vf.val = DNAN; /* undefined */ gdes->vf.when = 0; /* undefined */ } else { - rrd_set_error("Parameter '%f' out of range in VDEF '%s'\n", param, - gdes->vname); + rrd_set_error + ("Parameter '%f' out of range in VDEF '%s'\n", + param, gdes->vname); return -1; }; break; case VDEF_MAXIMUM: case VDEF_AVERAGE: + case VDEF_STDEV: case VDEF_MINIMUM: case VDEF_TOTAL: case VDEF_FIRST: @@ -4315,8 +4826,9 @@ int vdef_parse( gdes->vf.val = DNAN; gdes->vf.when = 0; } else { - rrd_set_error("Function '%s' needs no parameter in VDEF '%s'\n", - func, gdes->vname); + rrd_set_error + ("Function '%s' needs no parameter in VDEF '%s'\n", + func, gdes->vname); return -1; }; break; @@ -4336,20 +4848,18 @@ int vdef_calc( dst = &im->gdes[gdi]; src = &im->gdes[dst->vidx]; data = src->data + src->ds; - steps = (src->end - src->start) / src->step; + steps = (src->end - src->start) / src->step; #if 0 - printf("DEBUG: start == %lu, end == %lu, %lu steps\n", src->start, - src->end, steps); + printf + ("DEBUG: start == %lu, end == %lu, %lu steps\n", + src->start, src->end, steps); #endif - switch (dst->vf.op) { case VDEF_PERCENT:{ rrd_value_t *array; int field; - - - if ((array = malloc(steps * sizeof(double))) == NULL) { + if ((array = (rrd_value_t*)malloc(steps * sizeof(double))) == NULL) { rrd_set_error("malloc VDEV_PERCENT"); return -1; } @@ -4357,18 +4867,45 @@ int vdef_calc( array[step] = data[step * src->ds_cnt]; } qsort(array, step, sizeof(double), vdef_percent_compar); - field = (steps - 1) * dst->vf.param / 100; dst->vf.val = array[field]; dst->vf.when = 0; /* no time component */ free(array); #if 0 for (step = 0; step < steps; step++) - printf("DEBUG: %3li:%10.2f %c\n", step, array[step], - step == field ? '*' : ' '); + printf("DEBUG: %3li:%10.2f %c\n", + step, array[step], step == field ? '*' : ' '); #endif } break; + case VDEF_PERCENTNAN:{ + rrd_value_t *array; + int field; + /* count number of "valid" values */ + int nancount=0; + for (step = 0; step < steps; step++) { + if (!isnan(data[step * src->ds_cnt])) { nancount++; } + } + /* and allocate it */ + if ((array = (rrd_value_t*)malloc(nancount * sizeof(double))) == NULL) { + rrd_set_error("malloc VDEV_PERCENT"); + return -1; + } + /* and fill it in */ + field=0; + for (step = 0; step < steps; step++) { + if (!isnan(data[step * src->ds_cnt])) { + array[field] = data[step * src->ds_cnt]; + field++; + } + } + qsort(array, nancount, sizeof(double), vdef_percent_compar); + field = (nancount - 1) * dst->vf.param / 100; + dst->vf.val = array[field]; + dst->vf.when = 0; /* no time component */ + free(array); + } + break; case VDEF_MAXIMUM: step = 0; while (step != steps && isnan(data[step * src->ds_cnt])) @@ -4391,9 +4928,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])) { @@ -4405,9 +4944,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; @@ -4481,7 +5030,6 @@ int vdef_calc( SUMxy = 0; SUMxx = 0; SUMyy = 0; - for (step = 0; step < steps; step++) { if (finite(data[step * src->ds_cnt])) { cnt++; @@ -4497,12 +5045,9 @@ int vdef_calc( y_intercept = (SUMy - slope * SUMx) / cnt; correl = (SUMxy - - (SUMx * SUMy) / cnt) / sqrt((SUMxx - - (SUMx * SUMx) / cnt) * (SUMyy - - (SUMy * - SUMy) / - cnt)); - + (SUMx * SUMy) / cnt) / + sqrt((SUMxx - + (SUMx * SUMx) / cnt) * (SUMyy - (SUMy * SUMy) / cnt)); if (cnt) { if (dst->vf.op == VDEF_LSLSLOPE) { dst->vf.val = slope; @@ -4514,7 +5059,6 @@ int vdef_calc( dst->vf.val = correl; dst->vf.when = 0; }; - } else { dst->vf.val = DNAN; dst->vf.when = 0; @@ -4527,7 +5071,10 @@ int vdef_calc( /* NaN < -INF < finite_values < INF */ int vdef_percent_compar( - const void *a, const void *b) + const void + *a, + const void + *b) { /* Equality is not returned; this doesn't hurt except * (maybe) for a little performance. @@ -4538,7 +5085,6 @@ int vdef_percent_compar( return -1; if (isnan(*(double *) b)) return 1; - /* NaN doesn't reach this part so INF and -INF are extremes. * The sign from isinf() is compatible with the sign we return */ @@ -4546,10 +5092,21 @@ int vdef_percent_compar( return isinf(*(double *) a); if (isinf(*(double *) b)) return isinf(*(double *) b); - /* If we reach this, both values must be finite */ if (*(double *) a < *(double *) b) return -1; else return 1; } + +void grinfo_push( + image_desc_t *im, + char *key, + rrd_info_type_t type, + rrd_infoval_t value) +{ + im->grinfo_current = rrd_info_push(im->grinfo_current, key, type, value); + if (im->grinfo == NULL) { + im->grinfo = im->grinfo_current; + } +}