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