new operand on rrdgraph CDEFs: PREV(xxxx) -- Gonzalo Augusto Arana Tagle <garana...
[rrdtool.git] / src / rrd_rpncalc.c
1 /****************************************************************************
2  * RRDtool 1.0.28  Copyright Tobias Oetiker, 1997 - 2002
3  ****************************************************************************
4  * rrd_rpncalc.c  RPN calculator functions
5  ****************************************************************************/
6
7 #include "rrd_tool.h"
8 #include "rrd_rpncalc.h"
9 #include <limits.h>
10
11 short addop2str(enum op_en op, enum op_en op_type, char *op_str, 
12             char **result_str, unsigned short *offset);
13 int tzoffset(time_t); /* used to implement LTIME */
14
15 short rpn_compact(rpnp_t *rpnp, rpn_cdefds_t **rpnc, short *count)
16 {
17     short i;
18     *count = 0;
19     /* count the number of rpn nodes */
20     while(rpnp[*count].op != OP_END) (*count)++;
21     if (++(*count) > DS_CDEF_MAX_RPN_NODES) {
22         rrd_set_error("Maximum %d RPN nodes permitted",
23                       DS_CDEF_MAX_RPN_NODES);
24         return -1;
25     }
26     
27     /* allocate memory */
28     *rpnc = (rpn_cdefds_t *) calloc(*count,sizeof(rpn_cdefds_t));
29     for (i = 0; rpnp[i].op != OP_END; i++)
30     {
31         (*rpnc)[i].op = (char) rpnp[i].op;
32         if (rpnp[i].op == OP_NUMBER) {
33             /* rpnp.val is a double, rpnc.val is a short */
34             double temp = floor(rpnp[i].val);
35             if (temp < SHRT_MIN || temp > SHRT_MAX) {
36                 rrd_set_error(
37                     "constants must be integers in the interval (%d, %d)",
38                     SHRT_MIN, SHRT_MAX);
39                 free(*rpnc);    
40                 return -1;
41             }
42             (*rpnc)[i].val = (short) temp;
43           } else if (rpnp[i].op == OP_VARIABLE ||
44                                  rpnp[i].op == OP_PREV_OTHER) {
45             (*rpnc)[i].val = (short) rpnp[i].ptr;
46         }
47     }
48     /* terminate the sequence */
49     (*rpnc)[(*count) - 1].op = OP_END;
50     return 0;
51 }
52
53 rpnp_t * rpn_expand(rpn_cdefds_t *rpnc)
54 {
55     short i;
56     rpnp_t *rpnp;
57     
58     /* DS_CDEF_MAX_RPN_NODES is small, so at the expense of some wasted
59      * memory we avoid any reallocs */
60     rpnp = (rpnp_t *) calloc(DS_CDEF_MAX_RPN_NODES,sizeof(rpnp_t));
61     if (rpnp == NULL) return NULL;
62     for (i = 0; rpnc[i].op != OP_END; ++i)
63     {
64         rpnp[i].op = (long) rpnc[i].op;
65         if (rpnp[i].op == OP_NUMBER) {
66             rpnp[i].val = (double) rpnc[i].val;
67           } else if (rpnp[i].op == OP_VARIABLE ||
68                                  rpnp[i].op == OP_PREV_OTHER) {
69             rpnp[i].ptr = (long) rpnc[i].val;
70         }
71     }
72     /* terminate the sequence */
73     rpnp[i].op = OP_END;
74     return rpnp;
75 }
76
77 /* rpn_compact2str: convert a compact sequence of RPN operator nodes back
78  * into a CDEF string. This function is used by rrd_dump.
79  * arguments:
80  *  rpnc: an array of compact RPN operator nodes
81  *  ds_def: a pointer to the data source definition section of an RRD header
82  *   for lookup of data source names by index
83  *  str: out string, memory is allocated by the function, must be freed by the
84  *   the caller */
85 void rpn_compact2str(rpn_cdefds_t *rpnc,ds_def_t *ds_def,char **str)
86 {
87     unsigned short i,offset = 0;
88     char buffer[7]; /* short as a string */
89     
90     for (i = 0; rpnc[i].op != OP_END; i++)
91     {
92         if (i > 0) (*str)[offset++] = ',';
93         
94 #define add_op(VV,VVV) \
95           if (addop2str(rpnc[i].op, VV, VVV, str, &offset) == 1) continue;
96         
97         if (rpnc[i].op == OP_NUMBER) {
98             /* convert a short into a string */
99 #ifdef WIN32
100             _itoa(rpnc[i].val,buffer,10);
101 #else
102             sprintf(buffer,"%d",rpnc[i].val);
103 #endif
104             add_op(OP_NUMBER,buffer)
105                 }
106         
107         if (rpnc[i].op == OP_VARIABLE) {
108             char *ds_name = ds_def[rpnc[i].val].ds_nam;
109             add_op(OP_VARIABLE, ds_name)
110                 }
111
112           if (rpnc[i].op == OP_PREV_OTHER) {
113                 char *ds_name = ds_def[rpnc[i].val].ds_nam;
114                 add_op(OP_VARIABLE, ds_name)
115           }
116
117 #undef add_op
118         
119 #define add_op(VV,VVV) \
120           if (addop2str(rpnc[i].op, VV, #VVV, str, &offset) == 1) continue;
121         
122           add_op(OP_ADD,+)
123           add_op(OP_SUB,-)
124           add_op(OP_MUL,*)
125           add_op(OP_DIV,/)
126           add_op(OP_MOD,%)
127           add_op(OP_SIN,SIN)
128           add_op(OP_COS,COS)
129           add_op(OP_LOG,LOG)
130           add_op(OP_FLOOR,FLOOR)
131           add_op(OP_CEIL,CEIL)
132           add_op(OP_EXP,EXP)
133           add_op(OP_DUP,DUP)
134           add_op(OP_EXC,EXC)
135           add_op(OP_POP,POP)
136           add_op(OP_LT,LT)
137           add_op(OP_LE,LE)
138           add_op(OP_GT,GT)
139           add_op(OP_GE,GE)
140           add_op(OP_EQ,EQ)
141           add_op(OP_IF,IF)
142           add_op(OP_MIN,MIN)
143           add_op(OP_MAX,MAX)
144           add_op(OP_LIMIT,LIMIT)
145           add_op(OP_UNKN,UNKN)
146           add_op(OP_UN,UN)
147           add_op(OP_NEGINF,NEGINF)
148           add_op(OP_NE,NE)
149           add_op(OP_PREV,PREV)
150           add_op(OP_INF,INF)
151           add_op(OP_ISINF,ISINF)
152           add_op(OP_NOW,NOW)
153           add_op(OP_LTIME,LTIME)
154           add_op(OP_TIME,TIME)
155
156 #undef add_op
157               }
158     (*str)[offset] = '\0';
159
160 }
161
162 short addop2str(enum op_en op, enum op_en op_type, char *op_str, 
163                 char **result_str, unsigned short *offset)
164 {
165     if (op == op_type) {
166         short op_len;
167         op_len = strlen(op_str);
168         *result_str =  (char *) rrd_realloc(*result_str,
169                                             (op_len + 1 + *offset)*sizeof(char));
170         if (*result_str == NULL) {
171             rrd_set_error("failed to alloc memory in addop2str");
172             return -1;
173         }
174         strncpy(&((*result_str)[*offset]),op_str,op_len);
175         *offset += op_len;
176         return 1;
177     }
178     return 0;
179 }
180
181 void parseCDEF_DS(char *def,rrd_t *rrd, int ds_idx)
182 {
183     rpnp_t *rpnp = NULL;
184     rpn_cdefds_t *rpnc = NULL;
185     short count, i;
186     
187     rpnp = rpn_parse((void*) rrd, def, &lookup_DS);
188     if (rpnp == NULL) {
189         rrd_set_error("failed to parse computed data source %s", def);
190         return;
191     }
192     /* Check for OP nodes not permitted in COMPUTE DS.
193      * Moved this check from within rpn_compact() because it really is
194      * COMPUTE DS specific. This is less efficient, but creation doesn't
195      * occur too often. */
196     for (i = 0; rpnp[i].op != OP_END; i++) {
197         if (rpnp[i].op == OP_TIME || rpnp[i].op == OP_LTIME || 
198             rpnp[i].op == OP_PREV)
199         {
200             rrd_set_error(
201                 "operators time, ltime and prev not supported with DS COMPUTE");
202             free(rpnp);
203             return;
204         }
205     }
206     if (rpn_compact(rpnp,&rpnc,&count) == -1) {
207         free(rpnp);
208         return;
209     }
210     /* copy the compact rpn representation over the ds_def par array */
211     memcpy((void*) &(rrd -> ds_def[ds_idx].par[DS_cdef]),
212            (void*) rpnc, count*sizeof(rpn_cdefds_t));
213     free(rpnp);
214     free(rpnc);
215 }
216
217 /* lookup a data source name in the rrd struct and return the index,
218  * should use ds_match() here except:
219  * (1) need a void * pointer to the rrd
220  * (2) error handling is left to the caller
221  */
222 long lookup_DS(void *rrd_vptr,char *ds_name)
223 {
224     int i;
225     rrd_t *rrd; 
226     
227     rrd = (rrd_t *) rrd_vptr;
228     
229     for (i = 0; i < rrd -> stat_head -> ds_cnt; ++i)
230     {
231         if(strcmp(ds_name,rrd -> ds_def[i].ds_nam) == 0)
232             return i;
233     }
234     /* the caller handles a bad data source name in the rpn string */
235     return -1;
236 }
237
238 /* rpn_parse : parse a string and generate a rpnp array; modified
239  * str2rpn() originally included in rrd_graph.c
240  * arguments:
241  * key_hash: a transparent argument passed to lookup(); conceptually this
242  *    is a hash object for lookup of a numeric key given a variable name
243  * expr: the string RPN expression, including variable names
244  * lookup(): a function that retrieves a numeric key given a variable name
245  */
246 rpnp_t * 
247 rpn_parse(void *key_hash,char *expr,long (*lookup)(void *,char*)){
248     int pos=0;
249     long steps=-1;    
250     rpnp_t  *rpnp;
251     char vname[30];
252     
253     rpnp=NULL;
254     
255     while(*expr){
256         if ((rpnp = (rpnp_t *) rrd_realloc(rpnp, (++steps + 2)* 
257                                        sizeof(rpnp_t)))==NULL){
258             return NULL;
259         }
260         
261         else if((sscanf(expr,"%lf%n",&rpnp[steps].val,&pos) == 1) && (expr[pos] == ',')){
262             rpnp[steps].op = OP_NUMBER;
263             expr+=pos;
264         } 
265         
266 #define match_op(VV,VVV) \
267         else if (strncmp(expr, #VVV, strlen(#VVV))==0){ \
268             rpnp[steps].op = VV; \
269             expr+=strlen(#VVV); \
270         }
271
272
273 #define match_op_param(VV,VVV) \
274         else if (sscanf(expr, #VVV "(%[a-z0-9]s)",vname) == 1) { \
275           int length = 0; \
276           if ((length = strlen(#VVV)+strlen(vname)+2, \
277               expr[length] == ',' || expr[length] == '\0') ) { \
278              rpnp[steps].op = VV; \
279              rpnp[steps].ptr = (*lookup)(key_hash,vname); \
280              if (rpnp[steps].ptr < 0) { \
281                            free(rpnp); \
282                            return NULL; \
283                          } else expr+=length; \
284           } \
285         }
286
287         match_op(OP_ADD,+)
288         match_op(OP_SUB,-)
289         match_op(OP_MUL,*)
290         match_op(OP_DIV,/)
291         match_op(OP_MOD,%)
292         match_op(OP_SIN,SIN)
293         match_op(OP_COS,COS)
294         match_op(OP_LOG,LOG)
295         match_op(OP_FLOOR,FLOOR)
296         match_op(OP_CEIL,CEIL)
297         match_op(OP_EXP,EXP)
298         match_op(OP_DUP,DUP)
299         match_op(OP_EXC,EXC)
300         match_op(OP_POP,POP)
301         match_op(OP_LT,LT)
302         match_op(OP_LE,LE)
303         match_op(OP_GT,GT)
304         match_op(OP_GE,GE)
305         match_op(OP_EQ,EQ)
306         match_op(OP_IF,IF)
307         match_op(OP_MIN,MIN)
308         match_op(OP_MAX,MAX)
309         match_op(OP_LIMIT,LIMIT)
310           /* order is important here ! .. match longest first */
311         match_op(OP_UNKN,UNKN)
312         match_op(OP_UN,UN)
313         match_op(OP_NEGINF,NEGINF)
314         match_op(OP_NE,NE)
315           match_op_param(OP_PREV_OTHER,PREV)
316         match_op(OP_PREV,PREV)
317         match_op(OP_INF,INF)
318         match_op(OP_ISINF,ISINF)
319         match_op(OP_NOW,NOW)
320         match_op(OP_LTIME,LTIME)
321         match_op(OP_TIME,TIME)
322
323 #undef match_op
324
325
326             else if ((sscanf(expr,"%29[_A-Za-z0-9]%n",
327                              vname,&pos) == 1) 
328                      && ((rpnp[steps].ptr = (*lookup)(key_hash,vname)) != -1)){
329                 rpnp[steps].op = OP_VARIABLE;
330                 expr+=pos;
331             }      
332         
333         else {
334             free(rpnp);
335             return NULL;
336         }
337         if (*expr == 0)
338             break;
339         if (*expr == ',')
340             expr++;
341         else {
342             free(rpnp);
343             return NULL;
344         }  
345     }
346     rpnp[steps+1].op = OP_END;
347     return rpnp;
348 }
349
350 void
351 rpnstack_init(rpnstack_t *rpnstack)
352 {
353     rpnstack -> s = NULL;
354     rpnstack -> dc_stacksize = 0;
355     rpnstack -> dc_stackblock = 100;
356 }
357
358 void
359 rpnstack_free(rpnstack_t *rpnstack)
360 {
361    if (rpnstack -> s != NULL)
362           free(rpnstack -> s);
363    rpnstack -> dc_stacksize = 0;
364 }
365
366 /* rpn_calc: run the RPN calculator; also performs variable substitution;
367  * moved and modified from data_calc() originally included in rrd_graph.c 
368  * arguments:
369  * rpnp : an array of RPN operators (including variable references)
370  * rpnstack : the initialized stack
371  * data_idx : when data_idx is a multiple of rpnp.step, the rpnp.data pointer
372  *   is advanced by rpnp.ds_cnt; used only for variable substitution
373  * output : an array of output values; OP_PREV assumes this array contains
374  *   the "previous" value at index position output_idx-1; the definition of
375  *   "previous" depends on the calling environment
376  * output_idx : an index into the output array in which to store the output
377  *   of the RPN calculator
378  * returns: -1 if the computation failed (also calls rrd_set_error)
379  *           0 on success
380  */
381 short
382 rpn_calc(rpnp_t *rpnp, rpnstack_t *rpnstack, long data_idx, 
383                 rrd_value_t *output, int output_idx)
384 {
385     int rpi;
386     long stptr = -1;
387    
388     /* process each op from the rpn in turn */
389     for (rpi=0; rpnp[rpi].op != OP_END; rpi++){
390         /* allocate or grow the stack */
391         if (stptr + 5 > rpnstack -> dc_stacksize){
392             /* could move this to a separate function */
393             rpnstack -> dc_stacksize += rpnstack -> dc_stackblock;              
394             rpnstack -> s = rrd_realloc(rpnstack -> s,
395                         (rpnstack -> dc_stacksize)*sizeof(*(rpnstack -> s)));
396             if (rpnstack -> s == NULL){
397                 rrd_set_error("RPN stack overflow");
398                 return -1;
399             }
400         }
401
402 #define stackunderflow(MINSIZE)                         \
403         if(stptr<MINSIZE){                              \
404             rrd_set_error("RPN stack underflow");       \
405             return -1;                                  \
406         }
407
408         switch (rpnp[rpi].op){
409             case OP_NUMBER:
410                 rpnstack -> s[++stptr] = rpnp[rpi].val;
411                 break;
412             case OP_VARIABLE:
413                 /* Sanity check: VDEFs shouldn't make it here */
414                 if (rpnp[rpi].ds_cnt == 0) {
415                     rrd_set_error("VDEF made it into rpn_calc... aborting");
416                     return -1;
417                 } else {
418                     /* make sure we pull the correct value from
419                      * the *.data array. Adjust the pointer into
420                      * the array acordingly. Advance the ptr one
421                      * row in the rra (skip over non-relevant
422                      * data sources)
423                      */
424                     rpnstack -> s[++stptr] =  *(rpnp[rpi].data);
425                     if (data_idx % rpnp[rpi].step == 0){
426                         rpnp[rpi].data += rpnp[rpi].ds_cnt;
427                     }
428                 }
429                 break;
430             case OP_PREV:
431                 if ((output_idx-1) <= 0) {
432                     rpnstack -> s[++stptr] = DNAN;
433                 } else {
434                     rpnstack -> s[++stptr] = output[output_idx-1];
435                 }
436                 break;
437         case OP_PREV_OTHER:
438           if ((output_idx-1) <= 0) {
439                 rpnstack -> s[++stptr] = DNAN;
440           } else {
441                 rpnstack -> s[++stptr] = rpnp[rpnp[rpi].ptr].data[output_idx-1];
442           }
443           break;
444             case OP_UNKN:
445                 rpnstack -> s[++stptr] = DNAN; 
446                 break;
447             case OP_INF:
448                 rpnstack -> s[++stptr] = DINF; 
449                 break;
450             case OP_NEGINF:
451                 rpnstack -> s[++stptr] = -DINF; 
452                 break;
453             case OP_NOW:
454                 rpnstack -> s[++stptr] = (double)time(NULL);
455                 break;
456             case OP_TIME:
457                 /* HACK: this relies on the data_idx being the time,
458                 ** which the within-function scope is unaware of */
459                 rpnstack -> s[++stptr] = (double) data_idx;
460                 break;
461             case OP_LTIME:
462                 rpnstack -> s[++stptr] =
463                         (double) tzoffset(data_idx) + (double)data_idx;
464                 break;
465             case OP_ADD:
466                 stackunderflow(1);
467                 rpnstack -> s[stptr-1]  = rpnstack -> s[stptr-1] 
468                                         + rpnstack -> s[stptr];
469                 stptr--;
470                 break;
471             case OP_SUB:
472                 stackunderflow(1);
473                 rpnstack -> s[stptr-1]  = rpnstack -> s[stptr-1]
474                                         - rpnstack -> s[stptr];
475                 stptr--;
476                 break;
477             case OP_MUL:
478                 stackunderflow(1);
479                 rpnstack -> s[stptr-1]  = (rpnstack -> s[stptr-1])
480                                         * (rpnstack -> s[stptr]);
481                 stptr--;
482                 break;
483             case OP_DIV:
484                 stackunderflow(1);
485                 rpnstack -> s[stptr-1]  = rpnstack -> s[stptr-1]
486                                         / rpnstack -> s[stptr];
487                 stptr--;
488                 break;
489             case OP_MOD:
490                 stackunderflow(1);
491                 rpnstack -> s[stptr-1]= fmod(   rpnstack -> s[stptr-1]
492                                                 ,rpnstack -> s[stptr]);
493                 stptr--;
494                 break;
495             case OP_SIN:
496                 stackunderflow(0);
497                 rpnstack -> s[stptr] = sin(rpnstack -> s[stptr]);
498                 break;
499             case OP_COS:
500                 stackunderflow(0);
501                 rpnstack -> s[stptr] = cos(rpnstack -> s[stptr]);
502                 break;
503             case OP_CEIL:
504                 stackunderflow(0);
505                 rpnstack -> s[stptr] = ceil(rpnstack -> s[stptr]);
506                 break;
507             case OP_FLOOR:
508                 stackunderflow(0);
509                 rpnstack -> s[stptr] = floor(rpnstack -> s[stptr]);
510                 break;
511             case OP_LOG:
512                 stackunderflow(0);
513                 rpnstack -> s[stptr] = log(rpnstack -> s[stptr]);
514                 break;
515             case OP_DUP:
516                 stackunderflow(0);
517                 rpnstack -> s[stptr+1] = rpnstack -> s[stptr];
518                 stptr++;
519                 break;
520             case OP_POP:
521                 stackunderflow(0);
522                 stptr--;
523                 break;
524             case OP_EXC:
525                 stackunderflow(1);
526                 {
527                     double dummy;
528                     dummy = rpnstack -> s[stptr] ;
529                     rpnstack -> s[stptr] = rpnstack -> s[stptr-1];
530                     rpnstack -> s[stptr-1] = dummy;
531                 }
532                 break;
533             case OP_EXP:
534                 stackunderflow(0);
535                 rpnstack -> s[stptr] = exp(rpnstack -> s[stptr]);
536                 break;
537             case OP_LT:
538                 stackunderflow(1);
539                 if (isnan(rpnstack -> s[stptr-1])) 
540                     ;
541                 else if (isnan(rpnstack -> s[stptr]))
542                     rpnstack -> s[stptr-1] = rpnstack -> s[stptr];
543                 else
544                     rpnstack -> s[stptr-1] = rpnstack -> s[stptr-1] <
545                                         rpnstack -> s[stptr] ? 1.0 : 0.0;
546                 stptr--;
547                 break;
548             case OP_LE:
549                 stackunderflow(1);
550                 if (isnan(rpnstack -> s[stptr-1])) 
551                     ;
552                 else if (isnan(rpnstack -> s[stptr]))
553                     rpnstack -> s[stptr-1] = rpnstack -> s[stptr];
554                 else
555                     rpnstack -> s[stptr-1] = rpnstack -> s[stptr-1] <=
556                                         rpnstack -> s[stptr] ? 1.0 : 0.0;
557                 stptr--;
558                 break;
559             case OP_GT:
560                 stackunderflow(1);
561                 if (isnan(rpnstack -> s[stptr-1])) 
562                     ;
563                 else if (isnan(rpnstack -> s[stptr]))
564                     rpnstack -> s[stptr-1] = rpnstack -> s[stptr];
565                 else
566                     rpnstack -> s[stptr-1] = rpnstack -> s[stptr-1] >
567                                         rpnstack -> s[stptr] ? 1.0 : 0.0;
568                 stptr--;
569                 break;
570             case OP_GE:
571                 stackunderflow(1);
572                 if (isnan(rpnstack -> s[stptr-1])) 
573                     ;
574                 else if (isnan(rpnstack -> s[stptr]))
575                     rpnstack -> s[stptr-1] = rpnstack -> s[stptr];
576                 else
577                     rpnstack -> s[stptr-1] = rpnstack -> s[stptr-1] >=
578                                         rpnstack -> s[stptr] ? 1.0 : 0.0;
579                 stptr--;
580                 break;
581             case OP_NE:
582                 stackunderflow(1);
583                 if (isnan(rpnstack -> s[stptr-1]))
584                     ;
585                 else if (isnan(rpnstack -> s[stptr]))
586                     rpnstack -> s[stptr-1] = rpnstack -> s[stptr];
587                 else
588                     rpnstack -> s[stptr-1] = rpnstack -> s[stptr-1] ==
589                                         rpnstack -> s[stptr] ? 0.0 : 1.0;
590                 stptr--;
591                 break;
592             case OP_EQ:
593                 stackunderflow(1);
594                 if (isnan(rpnstack -> s[stptr-1])) 
595                     ;
596                 else if (isnan(rpnstack -> s[stptr]))
597                     rpnstack -> s[stptr-1] = rpnstack -> s[stptr];
598                 else
599                     rpnstack -> s[stptr-1] = rpnstack -> s[stptr-1] ==
600                                         rpnstack -> s[stptr] ? 1.0 : 0.0;
601                 stptr--;
602                 break;
603             case OP_IF:
604                 stackunderflow(2);
605                 rpnstack->s[stptr-2] = rpnstack->s[stptr-2] != 0.0 ?
606                                 rpnstack->s[stptr-1] : rpnstack->s[stptr];
607                 stptr--;
608                 stptr--;
609                 break;
610             case OP_MIN:
611                 stackunderflow(1);
612                 if (isnan(rpnstack->s[stptr-1])) 
613                     ;
614                 else if (isnan(rpnstack->s[stptr]))
615                     rpnstack->s[stptr-1] = rpnstack->s[stptr];
616                 else if (rpnstack->s[stptr-1] > rpnstack->s[stptr])
617                     rpnstack->s[stptr-1] = rpnstack->s[stptr];
618                 stptr--;
619                 break;
620             case OP_MAX:
621                 stackunderflow(1);
622                 if (isnan(rpnstack->s[stptr-1])) 
623                ;
624                 else if (isnan(rpnstack->s[stptr]))
625                     rpnstack->s[stptr-1] = rpnstack->s[stptr];
626                 else if (rpnstack->s[stptr-1] < rpnstack->s[stptr])
627                     rpnstack->s[stptr-1] = rpnstack->s[stptr];
628                 stptr--;
629                 break;
630             case OP_LIMIT:
631                 stackunderflow(2);
632                 if (isnan(rpnstack->s[stptr-2])) 
633                     ;
634                 else if (isnan(rpnstack->s[stptr-1]))
635                     rpnstack->s[stptr-2] = rpnstack->s[stptr-1];
636                 else if (isnan(rpnstack->s[stptr]))
637                     rpnstack->s[stptr-2] = rpnstack->s[stptr];
638                 else if (rpnstack->s[stptr-2] < rpnstack->s[stptr-1])
639                     rpnstack->s[stptr-2] = DNAN;
640                 else if (rpnstack->s[stptr-2] > rpnstack->s[stptr])
641                     rpnstack->s[stptr-2] = DNAN;
642                 stptr-=2;
643                 break;
644             case OP_UN:
645                 stackunderflow(0);
646                 rpnstack->s[stptr] = isnan(rpnstack->s[stptr]) ? 1.0 : 0.0;
647                 break;
648             case OP_ISINF:
649                 stackunderflow(0);
650                 rpnstack->s[stptr] = isinf(rpnstack->s[stptr]) ? 1.0 : 0.0;
651                 break;
652             case OP_END:
653                 break;
654        }
655 #undef stackunderflow
656    }
657    if(stptr!=0){
658        rrd_set_error("RPN final stack size != 1");
659        return -1;
660    }
661    
662    output[output_idx] = rpnstack->s[0];
663    return 0;
664 }
665
666 /* figure out what the local timezone offset for any point in
667    time was. Return it in seconds */
668 int
669 tzoffset( time_t now ){
670     int gm_sec, gm_min, gm_hour, gm_yday, gm_year,
671         l_sec, l_min, l_hour, l_yday, l_year;
672     struct tm *t;
673     int off;
674     t = gmtime(&now);
675     gm_sec = t->tm_sec;
676     gm_min = t->tm_min;
677     gm_hour = t->tm_hour;
678     gm_yday = t->tm_yday;
679     gm_year = t->tm_year;
680     t = localtime(&now);
681     l_sec = t->tm_sec;
682     l_min = t->tm_min;
683     l_hour = t->tm_hour;
684     l_yday = t->tm_yday;
685     l_year = t->tm_year;
686     off = (l_sec-gm_sec)+(l_min-gm_min)*60+(l_hour-gm_hour)*3600; 
687     if ( l_yday > gm_yday || l_year > gm_year){
688         off += 24*3600;
689     } else if ( l_yday < gm_yday || l_year < gm_year){
690         off -= 24*3600;
691     }
692    return off;
693 }
694