+int
+vdef_parse(gdes,str)
+struct graph_desc_t *gdes;
+char *str;
+{
+ /* A VDEF currently is either "func" or "param,func"
+ * so the parsing is rather simple. Change if needed.
+ */
+ double param;
+ char func[30];
+ int n;
+
+ n=0;
+ sscanf(str,"%le,%29[A-Z]%n",¶m,func,&n);
+ if (n==strlen(str)) { /* matched */
+ ;
+ } else {
+ n=0;
+ sscanf(str,"%29[A-Z]%n",func,&n);
+ if (n==strlen(str)) { /* matched */
+ param=DNAN;
+ } else {
+ 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("MAXIMUM",func)) gdes->vf.op = VDEF_MAXIMUM;
+ else if (!strcmp("AVERAGE",func)) gdes->vf.op = VDEF_AVERAGE;
+ else if (!strcmp("MINIMUM",func)) gdes->vf.op = VDEF_MINIMUM;
+ else if (!strcmp("FIRST", func)) gdes->vf.op = VDEF_FIRST;
+ else if (!strcmp("LAST", func)) gdes->vf.op = VDEF_LAST;
+ else {
+ rrd_set_error("Unknown function '%s' in VDEF '%s'\n"
+ ,func
+ ,gdes->vname
+ );
+ return -1;
+ };
+
+ switch (gdes->vf.op) {
+ case VDEF_PERCENT:
+ if (isnan(param)) { /* no parameter given */
+ rrd_set_error("Function '%s' needs parameter in VDEF '%s'\n"
+ ,func
+ ,gdes->vname
+ );
+ return -1;
+ };
+ if (param>=0.0 && param<=100.0) {
+ gdes->vf.param = param;
+ 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
+ );
+ return -1;
+ };
+ break;
+ case VDEF_MAXIMUM:
+ case VDEF_AVERAGE:
+ case VDEF_MINIMUM:
+ case VDEF_FIRST:
+ case VDEF_LAST:
+ if (isnan(param)) {
+ gdes->vf.param = DNAN;
+ gdes->vf.val = DNAN;
+ gdes->vf.when = 0;
+ } else {
+ rrd_set_error("Function '%s' needs no parameter in VDEF '%s'\n"
+ ,func
+ ,gdes->vname
+ );
+ return -1;
+ };
+ break;
+ };
+ return 0;
+}
+int
+vdef_calc(im,gdi)
+image_desc_t *im;
+int gdi;
+{
+ graph_desc_t *src,*dst;
+ rrd_value_t *data;
+ long step,steps;
+
+ dst = &im->gdes[gdi];
+ src = &im->gdes[dst->vidx];
+ data = src->data + src->ds + src->ds_cnt; /* skip first value! */
+ steps = (src->end - src->start) / src->step;
+
+#if 0
+printf("DEBUG: start == %lu, end == %lu, %lu steps\n"
+ ,src->start
+ ,src->end
+ ,steps
+ );
+#endif
+
+ switch (im->gdes[gdi].vf.op) {
+ case VDEF_PERCENT: {
+ rrd_value_t * array;
+ int field;
+
+
+ if ((array = malloc(steps*sizeof(double)))==NULL) {
+ rrd_set_error("malloc VDEV_PERCENT");
+ return -1;
+ }
+ for (step=0;step < steps; step++) {
+ 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 */
+#if 0
+for(step=0;step<steps;step++)
+printf("DEBUG: %3i:%10.2f %c\n",step,array[step],step==field?'*':' ');
+#endif
+ }
+ break;
+ case VDEF_MAXIMUM:
+ step=0;
+ while (step != steps && isnan(data[step*src->ds_cnt])) step++;
+ if (step == steps) {
+ dst->vf.val = DNAN;
+ dst->vf.when = 0;
+ } else {
+ dst->vf.val = data[steps*src->ds_cnt];
+ dst->vf.when = src->start + (step+1)*src->step;
+ }
+ while (step != steps) {
+ if (finite(data[step*src->ds_cnt])) {
+ if (data[step*src->ds_cnt] > dst->vf.val) {
+ dst->vf.val = data[steps*src->ds_cnt];
+ dst->vf.when = src->start + (step+1)*src->step;
+ }
+ }
+ step++;
+ }
+ break;
+ case VDEF_AVERAGE: {
+ int cnt=0;
+ double sum=0.0;
+ for (step=0;step<steps;step++) {
+ if (finite(data[step*src->ds_cnt])) {
+ sum += data[step*src->ds_cnt];
+ cnt ++;
+ }
+ step++;
+ }
+ if (cnt) {
+ dst->vf.val = sum/cnt;
+ dst->vf.when = 0; /* no time component */
+ } else {
+ dst->vf.val = DNAN;
+ dst->vf.when = 0;
+ }
+ }
+ break;
+ case VDEF_MINIMUM:
+ step=0;
+ while (step != steps && isnan(data[step*src->ds_cnt])) step++;
+ if (step == steps) {
+ dst->vf.val = DNAN;
+ dst->vf.when = 0;
+ } else {
+ dst->vf.val = data[steps*src->ds_cnt];
+ dst->vf.when = src->start + (step+1)*src->step;
+ }
+ while (step != steps) {
+ if (finite(data[step*src->ds_cnt])) {
+ if (data[step*src->ds_cnt] < dst->vf.val) {
+ dst->vf.val = data[steps*src->ds_cnt];
+ dst->vf.when = src->start + (step+1)*src->step;
+ }
+ }
+ step++;
+ }
+ break;
+ case VDEF_FIRST:
+ /* The time value returned here is one step before the
+ * actual time value. This is the start of the first
+ * non-NaN interval.
+ */
+ step=0;
+ while (step != steps && isnan(data[step*src->ds_cnt])) step++;
+ if (step == steps) { /* all entries were NaN */
+ dst->vf.val = DNAN;
+ dst->vf.when = 0;
+ } else {
+ dst->vf.val = data[step*src->ds_cnt];
+ dst->vf.when = src->start + step*src->step;
+ }
+ break;
+ case VDEF_LAST:
+ /* The time value returned here is the
+ * actual time value. This is the end of the last
+ * non-NaN interval.
+ */
+ step=steps-1;
+ while (step >= 0 && isnan(data[step*src->ds_cnt])) step--;
+ if (step < 0) { /* all entries were NaN */
+ dst->vf.val = DNAN;
+ dst->vf.when = 0;
+ } else {
+ dst->vf.val = data[step*src->ds_cnt];
+ dst->vf.when = src->start + (step+1)*src->step;
+ }
+ break;
+ }
+ return 0;
+}
+
+/* NaN <= -INF <= finite_values <= INF */
+int
+vdef_percent_compar(a,b)
+const void *a,*b;
+{
+ /* Equality is not returned; this doesn't hurt except
+ * (maybe) for a little performance.
+ */