reindented
[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    return off;
688 }
689