~ubuntu-branches/ubuntu/natty/ntop/natty

« back to all changes in this revision

Viewing changes to myrrd/parsetime.c

  • Committer: Bazaar Package Importer
  • Author(s): Ola Lundqvist
  • Date: 2005-01-30 21:59:13 UTC
  • mfrom: (2.1.1 warty)
  • Revision ID: james.westby@ubuntu.com-20050130215913-xc3ke963bw49b3k4
Tags: 2:3.0-5
* Updated README.Debian file so users will understand what to do at
  install, closes: #291794, #287802.
* Updated ntop init script to give better output.
* Also changed log directory from /var/lib/ntop to /var/log/ntop,
  closes: #252352.
* Quoted the interface list to allow whitespace, closes: #267248.
* Added a couple of logcheck ignores, closes: #269321, #269319.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*  
 
2
 *  parsetime.c - parse time for at(1)
 
3
 *  Copyright (C) 1993, 1994  Thomas Koenig
 
4
 *
 
5
 *  modifications for english-language times
 
6
 *  Copyright (C) 1993  David Parsons
 
7
 *
 
8
 *  A lot of modifications and extensions 
 
9
 *  (including the new syntax being useful for RRDB)
 
10
 *  Copyright (C) 1999  Oleg Cherevko (aka Olwi Deer)
 
11
 *
 
12
 *  severe structural damage inflicted by Tobi Oetiker in 1999
 
13
 *
 
14
 * Redistribution and use in source and binary forms, with or without
 
15
 * modification, are permitted provided that the following conditions
 
16
 * are met:
 
17
 * 1. Redistributions of source code must retain the above copyright
 
18
 *    notice, this list of conditions and the following disclaimer.
 
19
 * 2. The name of the author(s) may not be used to endorse or promote
 
20
 *    products derived from this software without specific prior written
 
21
 *    permission.
 
22
 *
 
23
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 
24
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
25
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 
26
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 
27
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 
28
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
29
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
30
 * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
31
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 
32
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
33
 */
 
34
 
 
35
/*
 
36
 * The BNF-like specification of the time syntax parsed is below:
 
37
 *                                                               
 
38
 * As usual, [ X ] means that X is optional, { X } means that X may
 
39
 * be either omitted or specified as many times as needed,
 
40
 * alternatives are separated by |, brackets are used for grouping.
 
41
 * (# marks the beginning of comment that extends to the end of line)
 
42
 *
 
43
 * TIME-SPECIFICATION ::= TIME-REFERENCE [ OFFSET-SPEC ] |
 
44
 *                                         OFFSET-SPEC   |
 
45
 *                         ( START | END ) OFFSET-SPEC 
 
46
 *
 
47
 * TIME-REFERENCE ::= NOW | TIME-OF-DAY-SPEC [ DAY-SPEC-1 ] |
 
48
 *                        [ TIME-OF-DAY-SPEC ] DAY-SPEC-2
 
49
 *
 
50
 * TIME-OF-DAY-SPEC ::= NUMBER (':') NUMBER [am|pm] | # HH:MM
 
51
 *                     'noon' | 'midnight' | 'teatime'
 
52
 *
 
53
 * DAY-SPEC-1 ::= NUMBER '/' NUMBER '/' NUMBER |  # MM/DD/[YY]YY
 
54
 *                NUMBER '.' NUMBER '.' NUMBER |  # DD.MM.[YY]YY
 
55
 *                NUMBER                          # Seconds since 1970
 
56
 *                NUMBER                          # YYYYMMDD
 
57
 *
 
58
 * DAY-SPEC-2 ::= MONTH-NAME NUMBER [NUMBER] |    # Month DD [YY]YY
 
59
 *                'yesterday' | 'today' | 'tomorrow' |
 
60
 *                DAY-OF-WEEK
 
61
 *
 
62
 *
 
63
 * OFFSET-SPEC ::= '+'|'-' NUMBER TIME-UNIT { ['+'|'-'] NUMBER TIME-UNIT }
 
64
 *
 
65
 * TIME-UNIT ::= SECONDS | MINUTES | HOURS |
 
66
 *               DAYS | WEEKS | MONTHS | YEARS
 
67
 *
 
68
 * NOW ::= 'now' | 'n'
 
69
 *
 
70
 * START ::= 'start' | 's'
 
71
 * END   ::= 'end' | 'e'
 
72
 *
 
73
 * SECONDS ::= 'seconds' | 'second' | 'sec' | 's'
 
74
 * MINUTES ::= 'minutes' | 'minute' | 'min' | 'm'
 
75
 * HOURS   ::= 'hours' | 'hour' | 'hr' | 'h'
 
76
 * DAYS    ::= 'days' | 'day' | 'd'
 
77
 * WEEKS   ::= 'weeks' | 'week' | 'wk' | 'w'
 
78
 * MONTHS  ::= 'months' | 'month' | 'mon' | 'm'
 
79
 * YEARS   ::= 'years' | 'year' | 'yr' | 'y'
 
80
 *
 
81
 * MONTH-NAME ::= 'jan' | 'january' | 'feb' | 'february' | 'mar' | 'march' |
 
82
 *                'apr' | 'april' | 'may' | 'jun' | 'june' | 'jul' | 'july' |
 
83
 *                'aug' | 'august' | 'sep' | 'september' | 'oct' | 'october' |
 
84
 *                'nov' | 'november' | 'dec' | 'december'
 
85
 *
 
86
 * DAY-OF-WEEK ::= 'sunday' | 'sun' | 'monday' | 'mon' | 'tuesday' | 'tue' |
 
87
 *                 'wednesday' | 'wed' | 'thursday' | 'thu' | 'friday' | 'fri' |
 
88
 *                 'saturday' | 'sat'
 
89
 *
 
90
 *
 
91
 * As you may note, there is an ambiguity with respect to
 
92
 * the 'm' time unit (which can mean either minutes or months).
 
93
 * To cope with this, code tries to read users mind :) by applying
 
94
 * certain heuristics. There are two of them:
 
95
 *
 
96
 * 1. If 'm' is used in context of (i.e. right after the) years,
 
97
 *    months, weeks, or days it is assumed to mean months, while
 
98
 *    in the context of hours, minutes, and seconds it means minutes.
 
99
 *    (e.g., in -1y6m or +3w1m 'm' means 'months', while in
 
100
 *    -3h20m or +5s2m 'm' means 'minutes')
 
101
 *
 
102
 * 2. Out of context (i.e. right after the '+' or '-' sign) the
 
103
 *    meaning of 'm' is guessed from the number it directly follows.
 
104
 *    Currently, if the number absolute value is below 25 it is assumed
 
105
 *    that 'm' means months, otherwise it is treated as minutes.
 
106
 *    (e.g., -25m == -25 minutes, while +24m == +24 months)
 
107
 *
 
108
 */
 
109
 
 
110
/* System Headers */
 
111
 
 
112
/* Local headers */
 
113
 
 
114
#include "rrd_tool.h"
 
115
#include <stdarg.h>
 
116
 
 
117
/* Structures and unions */
 
118
 
 
119
enum {  /* symbols */
 
120
    MIDNIGHT, NOON, TEATIME,
 
121
    PM, AM, YESTERDAY, TODAY, TOMORROW, NOW, START, END,
 
122
    SECONDS, MINUTES, HOURS, DAYS, WEEKS, MONTHS, YEARS,
 
123
    MONTHS_MINUTES,
 
124
    NUMBER, PLUS, MINUS, DOT, COLON, SLASH, ID, JUNK,
 
125
    JAN, FEB, MAR, APR, MAY, JUN,
 
126
    JUL, AUG, SEP, OCT, NOV, DEC,
 
127
    SUN, MON, TUE, WED, THU, FRI, SAT
 
128
    };
 
129
 
 
130
/* the below is for plus_minus() */
 
131
#define PREVIOUS_OP     (-1)
 
132
 
 
133
/* parse translation table - table driven parsers can be your FRIEND!
 
134
 */
 
135
struct SpecialToken {
 
136
    char *name; /* token name */
 
137
    int value;  /* token id */
 
138
};
 
139
static struct SpecialToken VariousWords[] = {
 
140
    { "midnight", MIDNIGHT },   /* 00:00:00 of today or tomorrow */
 
141
    { "noon", NOON },           /* 12:00:00 of today or tomorrow */
 
142
    { "teatime", TEATIME },     /* 16:00:00 of today or tomorrow */
 
143
    { "am", AM },               /* morning times for 0-12 clock */
 
144
    { "pm", PM },               /* evening times for 0-12 clock */
 
145
    { "tomorrow", TOMORROW },
 
146
    { "yesterday", YESTERDAY },
 
147
    { "today", TODAY },
 
148
    { "now", NOW },
 
149
    { "n", NOW },
 
150
    { "start", START },
 
151
    { "s", START },     
 
152
    { "end", END },
 
153
    { "e", END },
 
154
 
 
155
    { "jan", JAN },
 
156
    { "feb", FEB },
 
157
    { "mar", MAR },
 
158
    { "apr", APR },
 
159
    { "may", MAY },
 
160
    { "jun", JUN },
 
161
    { "jul", JUL },
 
162
    { "aug", AUG },
 
163
    { "sep", SEP },
 
164
    { "oct", OCT },
 
165
    { "nov", NOV },
 
166
    { "dec", DEC },
 
167
    { "january", JAN },
 
168
    { "february", FEB },
 
169
    { "march", MAR },
 
170
    { "april", APR },
 
171
    { "may", MAY },
 
172
    { "june", JUN },
 
173
    { "july", JUL },
 
174
    { "august", AUG },
 
175
    { "september", SEP },
 
176
    { "october", OCT },
 
177
    { "november", NOV },
 
178
    { "december", DEC },
 
179
    { "sunday", SUN },
 
180
    { "sun", SUN },
 
181
    { "monday", MON },
 
182
    { "mon", MON },
 
183
    { "tuesday", TUE },
 
184
    { "tue", TUE },
 
185
    { "wednesday", WED },
 
186
    { "wed", WED },
 
187
    { "thursday", THU },
 
188
    { "thu", THU },
 
189
    { "friday", FRI },
 
190
    { "fri", FRI },
 
191
    { "saturday", SAT },
 
192
    { "sat", SAT },
 
193
    { NULL, 0 }                 /*** SENTINEL ***/
 
194
};
 
195
 
 
196
static struct SpecialToken TimeMultipliers[] = {
 
197
    { "second", SECONDS },      /* seconds multiplier */
 
198
    { "seconds", SECONDS },     /* (pluralized) */
 
199
    { "sec", SECONDS },         /* (generic) */
 
200
    { "s", SECONDS },           /* (short generic) */
 
201
    { "minute", MINUTES },      /* minutes multiplier */
 
202
    { "minutes", MINUTES },     /* (pluralized) */
 
203
    { "min", MINUTES },         /* (generic) */
 
204
    { "m", MONTHS_MINUTES },    /* (short generic) */
 
205
    { "hour", HOURS },          /* hours ... */
 
206
    { "hours", HOURS },         /* (pluralized) */
 
207
    { "hr", HOURS },            /* (generic) */
 
208
    { "h", HOURS },             /* (short generic) */
 
209
    { "day", DAYS },            /* days ... */
 
210
    { "days", DAYS },           /* (pluralized) */
 
211
    { "d", DAYS },              /* (short generic) */
 
212
    { "week", WEEKS },          /* week ... */
 
213
    { "weeks", WEEKS },         /* (pluralized) */
 
214
    { "wk", WEEKS },            /* (generic) */
 
215
    { "w", WEEKS },             /* (short generic) */
 
216
    { "month", MONTHS },        /* week ... */
 
217
    { "months", MONTHS },       /* (pluralized) */
 
218
    { "mon", MONTHS },          /* (generic) */
 
219
    { "year", YEARS },          /* year ... */
 
220
    { "years", YEARS },         /* (pluralized) */
 
221
    { "yr", YEARS },            /* (generic) */
 
222
    { "y", YEARS },             /* (short generic) */
 
223
    { NULL, 0 }                 /*** SENTINEL ***/
 
224
};
 
225
 
 
226
/* File scope variables */
 
227
 
 
228
/* context dependant list of specials for parser to recognize,
 
229
 * required for us to be able distinguish between 'mon' as 'month'
 
230
 * and 'mon' as 'monday'
 
231
 */
 
232
static struct SpecialToken *Specials;
 
233
 
 
234
static char **scp;      /* scanner - pointer at arglist */
 
235
static char scc;        /* scanner - count of remaining arguments */
 
236
static char *sct;       /* scanner - next char pointer in current argument */
 
237
static int need;        /* scanner - need to advance to next argument */
 
238
 
 
239
static char *sc_token=NULL;     /* scanner - token buffer */
 
240
static size_t sc_len;   /* scanner - lenght of token buffer */
 
241
static int sc_tokid;    /* scanner - token id */
 
242
 
 
243
static int need_to_free = 0; /* means that we need deallocating memory */
 
244
 
 
245
/* Local functions */
 
246
 
 
247
void EnsureMemFree ()
 
248
{
 
249
  if( need_to_free )
 
250
    {
 
251
    free(sc_token);
 
252
    need_to_free = 0;
 
253
    }
 
254
}
 
255
 
 
256
/*
 
257
 * A hack to compensate for the lack of the C++ exceptions
 
258
 *
 
259
 * Every function func that might generate parsing "exception"
 
260
 * should return TIME_OK (aka NULL) or pointer to the error message,
 
261
 * and should be called like this: try(func(args));
 
262
 *
 
263
 * if the try is not successfull it will reset the token pointer ...
 
264
 *
 
265
 * [NOTE: when try(...) is used as the only statement in the "if-true"
 
266
 *  part of the if statement that also has an "else" part it should be
 
267
 *  either enclosed in the curly braces (despite the fact that it looks
 
268
 *  like a single statement) or NOT follwed by the ";"]
 
269
 */
 
270
#define try(b)          { \
 
271
                        char *_e; \
 
272
                        if((_e=(b))) \
 
273
                          { \
 
274
                          EnsureMemFree(); \
 
275
                          return _e; \
 
276
                          } \
 
277
                        }
 
278
 
 
279
/*
 
280
 * The panic() function was used in the original code to die, we redefine
 
281
 * it as macro to start the chain of ascending returns that in conjunction
 
282
 * with the try(b) above will simulate a sort of "exception handling"
 
283
 */
 
284
 
 
285
#define panic(e)        { \
 
286
                        return (e); \
 
287
                        }
 
288
 
 
289
/*
 
290
 * ve() and e() are used to set the return error,
 
291
 * the most aprropriate use for these is inside panic(...) 
 
292
 */
 
293
#define MAX_ERR_MSG_LEN 1024
 
294
static char errmsg[ MAX_ERR_MSG_LEN ];
 
295
 
 
296
static char *
 
297
ve ( char *fmt, va_list ap )
 
298
{
 
299
#ifdef HAVE_VSNPRINTF
 
300
  vsnprintf( errmsg, MAX_ERR_MSG_LEN, fmt, ap );
 
301
#else
 
302
  vsprintf( errmsg, fmt, ap );
 
303
#endif
 
304
  EnsureMemFree();
 
305
  return( errmsg );
 
306
}
 
307
 
 
308
static char *
 
309
e ( char *fmt, ... )
 
310
{
 
311
  char *err;
 
312
  va_list ap;
 
313
  va_start( ap, fmt );
 
314
  err = ve( fmt, ap );
 
315
  va_end( ap );
 
316
  return( err );
 
317
}
 
318
 
 
319
/* Compare S1 and S2, ignoring case, returning less than, equal to or
 
320
   greater than zero if S1 is lexiographically less than,
 
321
   equal to or greater than S2.  -- copied from GNU libc*/
 
322
static int
 
323
mystrcasecmp (s1, s2)
 
324
     const char *s1;
 
325
     const char *s2;
 
326
{
 
327
  const unsigned char *p1 = (const unsigned char *) s1;
 
328
  const unsigned char *p2 = (const unsigned char *) s2;
 
329
  unsigned char c1, c2;
 
330
 
 
331
  if (p1 == p2)
 
332
    return 0;
 
333
 
 
334
  do
 
335
    {
 
336
      c1 = tolower (*p1++);
 
337
      c2 = tolower (*p2++);
 
338
      if (c1 == '\0')
 
339
        break;
 
340
    }
 
341
  while (c1 == c2);
 
342
 
 
343
  return c1 - c2;
 
344
}
 
345
 
 
346
/*
 
347
 * parse a token, checking if it's something special to us
 
348
 */
 
349
static int
 
350
parse_token(char *arg)
 
351
{
 
352
    int i;
 
353
 
 
354
    for (i=0; Specials[i].name != NULL; i++)
 
355
        if (mystrcasecmp(Specials[i].name, arg) == 0)
 
356
            return sc_tokid = Specials[i].value;
 
357
 
 
358
    /* not special - must be some random id */
 
359
    return sc_tokid = ID;
 
360
} /* parse_token */
 
361
 
 
362
 
 
363
 
 
364
/*
 
365
 * init_scanner() sets up the scanner to eat arguments
 
366
 */
 
367
static char *
 
368
init_scanner(int argc, char **argv)
 
369
{
 
370
    scp = argv;
 
371
    scc = argc;
 
372
    need = 1;
 
373
    sc_len = 1;
 
374
    while (argc-- > 0)
 
375
        sc_len += strlen(*argv++);
 
376
 
 
377
    sc_token = (char *) malloc(sc_len*sizeof(char));
 
378
    if( sc_token == NULL )
 
379
      return "Failed to allocate memory";
 
380
    need_to_free = 1;
 
381
    return TIME_OK;
 
382
} /* init_scanner */
 
383
 
 
384
/*
 
385
 * token() fetches a token from the input stream
 
386
 */
 
387
static int
 
388
token()
 
389
{
 
390
    int idx;
 
391
 
 
392
    while (1) {
 
393
        memset(sc_token, '\0', sc_len);
 
394
        sc_tokid = EOF;
 
395
        idx = 0;
 
396
 
 
397
        /* if we need to read another argument, walk along the argument list;
 
398
         * when we fall off the arglist, we'll just return EOF forever
 
399
         */
 
400
        if (need) {
 
401
            if (scc < 1)
 
402
                return sc_tokid;
 
403
            sct = *scp;
 
404
            scp++;
 
405
            scc--;
 
406
            need = 0;
 
407
        }
 
408
        /* eat whitespace now - if we walk off the end of the argument,
 
409
         * we'll continue, which puts us up at the top of the while loop
 
410
         * to fetch the next argument in
 
411
         */
 
412
        while (isspace((unsigned char)*sct) || *sct == '_' || *sct == ',' )
 
413
            ++sct;
 
414
        if (!*sct) {
 
415
            need = 1;
 
416
            continue;
 
417
        }
 
418
 
 
419
        /* preserve the first character of the new token
 
420
         */
 
421
        sc_token[0] = *sct++;
 
422
 
 
423
        /* then see what it is
 
424
         */
 
425
        if (isdigit((unsigned char)(sc_token[0]))) {
 
426
            while (isdigit((unsigned char)(*sct)))
 
427
                sc_token[++idx] = *sct++;
 
428
            sc_token[++idx] = '\0';
 
429
            return sc_tokid = NUMBER;
 
430
        }
 
431
        else if (isalpha((unsigned char)(sc_token[0]))) {
 
432
            while (isalpha((unsigned char)(*sct)))
 
433
                sc_token[++idx] = *sct++;
 
434
            sc_token[++idx] = '\0';
 
435
            return parse_token(sc_token);
 
436
        }
 
437
        else switch(sc_token[0]) {
 
438
            case ':': return sc_tokid = COLON;
 
439
            case '.': return sc_tokid = DOT;
 
440
            case '+': return sc_tokid = PLUS;
 
441
            case '-': return sc_tokid = MINUS;
 
442
            case '/': return sc_tokid = SLASH;
 
443
        default:
 
444
        /*OK, we did not make it ... */
 
445
            sct--;
 
446
            return sc_tokid = EOF;
 
447
        }
 
448
    } /* while (1) */
 
449
} /* token */
 
450
 
 
451
 
 
452
/* 
 
453
 * expect2() gets a token and complins if it's not the token we want
 
454
 */
 
455
static char *
 
456
expect2(int desired, char *complain_fmt, ...)
 
457
{
 
458
    va_list ap;
 
459
    va_start( ap, complain_fmt );
 
460
    if (token() != desired) {
 
461
        panic(ve( complain_fmt, ap ));
 
462
    }
 
463
    va_end( ap );
 
464
    return TIME_OK;
 
465
    
 
466
} /* expect2 */
 
467
 
 
468
 
 
469
/*
 
470
 * plus_minus() is used to parse a single NUMBER TIME-UNIT pair
 
471
 *              for the OFFSET-SPEC.
 
472
 *              It allso applies those m-guessing euristics.
 
473
 */
 
474
static char *
 
475
plus_minus(struct time_value *ptv, int doop)
 
476
{
 
477
    static int op = PLUS;
 
478
    static int prev_multiplier = -1;
 
479
    int delta;
 
480
 
 
481
    if( doop >= 0 ) 
 
482
      {
 
483
      op = doop;
 
484
      try(expect2(NUMBER,"There should be number after '%c'", op == PLUS ? '+' : '-'));
 
485
      prev_multiplier = -1; /* reset months-minutes guessing mechanics */
 
486
      }
 
487
    /* if doop is < 0 then we repeat the previous op
 
488
     * with the prefetched number */
 
489
 
 
490
    delta = atoi(sc_token);
 
491
 
 
492
    if( token() == MONTHS_MINUTES )
 
493
      {
 
494
      /* hard job to guess what does that -5m means: -5mon or -5min? */
 
495
      switch(prev_multiplier)
 
496
        {
 
497
        case DAYS:
 
498
        case WEEKS:
 
499
        case MONTHS:
 
500
        case YEARS:
 
501
             sc_tokid = MONTHS;
 
502
             break;
 
503
 
 
504
        case SECONDS:
 
505
        case MINUTES:
 
506
        case HOURS:
 
507
             sc_tokid = MINUTES;
 
508
             break;
 
509
 
 
510
        default:
 
511
             if( delta < 6 ) /* it may be some other value but in the context
 
512
                               * of RRD who needs less than 6 min deltas? */
 
513
               sc_tokid = MONTHS;
 
514
             else
 
515
               sc_tokid = MINUTES;
 
516
        }
 
517
      }
 
518
    prev_multiplier = sc_tokid;
 
519
    switch (sc_tokid) {
 
520
    case YEARS:
 
521
            ptv->tm.tm_year += (op == PLUS) ? delta : -delta;
 
522
            return TIME_OK;
 
523
    case MONTHS:
 
524
            ptv->tm.tm_mon += (op == PLUS) ? delta : -delta;
 
525
            return TIME_OK;
 
526
    case WEEKS:
 
527
            delta *= 7;
 
528
            /* FALLTHRU */
 
529
    case DAYS:
 
530
            ptv->tm.tm_mday += (op == PLUS) ? delta : -delta;
 
531
            return TIME_OK;
 
532
    case HOURS:
 
533
            ptv->offset += (op == PLUS) ? delta*60*60 : -delta*60*60;
 
534
            return TIME_OK;
 
535
    case MINUTES:
 
536
            ptv->offset += (op == PLUS) ? delta*60 : -delta*60;
 
537
            return TIME_OK;
 
538
    case SECONDS:
 
539
            ptv->offset += (op == PLUS) ? delta : -delta;
 
540
            return TIME_OK;
 
541
    default: /*default unit is seconds */
 
542
        ptv->offset += (op == PLUS) ? delta : -delta;
 
543
        return TIME_OK;
 
544
    }
 
545
    panic(e("well-known time unit expected after %d", delta));
 
546
    /* NORETURN */
 
547
    return TIME_OK; /* to make compiler happy :) */
 
548
} /* plus_minus */
 
549
 
 
550
 
 
551
/*
 
552
 * tod() computes the time of day (TIME-OF-DAY-SPEC)
 
553
 */
 
554
static char *
 
555
tod(struct time_value *ptv)
 
556
{
 
557
    int hour, minute = 0;
 
558
    int tlen;
 
559
    /* save token status in  case we must abort */
 
560
    int scc_sv = scc; 
 
561
    char *sct_sv = sct; 
 
562
    int sc_tokid_sv = sc_tokid;
 
563
 
 
564
    tlen = strlen(sc_token);
 
565
    
 
566
    /* first pick out the time of day - we assume a HH (COLON|DOT) MM time
 
567
     */    
 
568
    if (tlen > 2) {
 
569
      return TIME_OK;
 
570
    }
 
571
    
 
572
    hour = atoi(sc_token);
 
573
 
 
574
    token();
 
575
    if (sc_tokid == SLASH || sc_tokid == DOT) {
 
576
      /* guess we are looking at a date */
 
577
      scc = scc_sv;
 
578
      sct = sct_sv;
 
579
      sc_tokid = sc_tokid_sv;
 
580
      sprintf (sc_token,"%d", hour);
 
581
      return TIME_OK;
 
582
    }
 
583
    if (sc_tokid == COLON ) {
 
584
       try(expect2(NUMBER,
 
585
            "Parsing HH:MM syntax, expecting MM as number, got none"));
 
586
        minute = atoi(sc_token);
 
587
        if (minute > 59) {
 
588
            panic(e("parsing HH:MM syntax, got MM = %d (>59!)", minute ));
 
589
        }
 
590
        token();
 
591
    }
 
592
 
 
593
    /* check if an AM or PM specifier was given
 
594
     */
 
595
    if (sc_tokid == AM || sc_tokid == PM) {
 
596
        if (hour > 12) {
 
597
            panic(e("there cannot be more than 12 AM or PM hours"));
 
598
        }
 
599
        if (sc_tokid == PM) {
 
600
            if (hour != 12)     /* 12:xx PM is 12:xx, not 24:xx */
 
601
                        hour += 12;
 
602
        } else {
 
603
            if (hour == 12)     /* 12:xx AM is 00:xx, not 12:xx */
 
604
                        hour = 0;
 
605
        }
 
606
        token();
 
607
    } 
 
608
    else if (hour > 23) {
 
609
      /* guess it was not a time then ... */
 
610
      scc = scc_sv;
 
611
      sct = sct_sv;
 
612
      sc_tokid = sc_tokid_sv;
 
613
      sprintf (sc_token,"%d", hour);
 
614
      return TIME_OK;
 
615
    }
 
616
    ptv->tm.tm_hour = hour;
 
617
    ptv->tm.tm_min = minute;
 
618
    ptv->tm.tm_sec = 0;
 
619
    if (ptv->tm.tm_hour == 24) {
 
620
        ptv->tm.tm_hour = 0;
 
621
        ptv->tm.tm_mday++;
 
622
    }
 
623
  return TIME_OK;
 
624
} /* tod */
 
625
 
 
626
 
 
627
/*
 
628
 * assign_date() assigns a date, adjusting year as appropriate
 
629
 */
 
630
static char *
 
631
assign_date(struct time_value *ptv, long mday, long mon, long year)
 
632
{
 
633
    if (year > 138) {
 
634
        if (year > 1970)
 
635
            year -= 1900;
 
636
        else {
 
637
            panic(e("invalid year %d (should be either 00-99 or >1900)",
 
638
                    year));
 
639
        }
 
640
    } else if( year >= 0 && year < 38 ) {
 
641
        year += 100;         /* Allow year 2000-2037 to be specified as   */
 
642
    }                        /* 00-37 until the problem of 2038 year will */
 
643
                             /* arise for unices with 32-bit time_t :)    */
 
644
    if (year < 70) {
 
645
      panic(e("won't handle dates before epoch (01/01/1970), sorry"));
 
646
    }
 
647
 
 
648
    ptv->tm.tm_mday = mday;
 
649
    ptv->tm.tm_mon = mon;
 
650
    ptv->tm.tm_year = year;
 
651
  return TIME_OK;
 
652
} /* assign_date */
 
653
 
 
654
 
 
655
/* 
 
656
 * day() picks apart DAY-SPEC-[12]
 
657
 */
 
658
static char *
 
659
day(struct time_value *ptv)
 
660
{
 
661
    long mday=0, wday, mon, year = ptv->tm.tm_year;
 
662
    int tlen;
 
663
 
 
664
    switch (sc_tokid) {
 
665
    case YESTERDAY:
 
666
            ptv->tm.tm_mday--;
 
667
            /* FALLTRHU */
 
668
    case TODAY: /* force ourselves to stay in today - no further processing */
 
669
            token();
 
670
            break;
 
671
    case TOMORROW:
 
672
            ptv->tm.tm_mday++;
 
673
            token();
 
674
            break;
 
675
 
 
676
    case JAN: case FEB: case MAR: case APR: case MAY: case JUN:
 
677
    case JUL: case AUG: case SEP: case OCT: case NOV: case DEC:
 
678
            /* do month mday [year]
 
679
             */
 
680
            mon = (sc_tokid-JAN);
 
681
           try(expect2(NUMBER,
 
682
                "the day of the month should follow month name"));
 
683
            mday = atol(sc_token);
 
684
            if (token() == NUMBER) {
 
685
                year = atol(sc_token);
 
686
                token();
 
687
            }
 
688
            else
 
689
                year = ptv->tm.tm_year;
 
690
            try(assign_date(ptv, mday, mon, year));
 
691
            break;
 
692
 
 
693
    case SUN: case MON: case TUE:
 
694
    case WED: case THU: case FRI:
 
695
    case SAT:
 
696
            /* do a particular day of the week
 
697
             */
 
698
            wday = (sc_tokid-SUN);
 
699
            ptv->tm.tm_mday += (wday - ptv->tm.tm_wday);
 
700
            break;
 
701
            /*
 
702
            mday = ptv->tm.tm_mday;
 
703
            mday += (wday - ptv->tm.tm_wday);
 
704
            ptv->tm.tm_wday = wday;
 
705
 
 
706
            try(assign_date(ptv, mday, ptv->tm.tm_mon, ptv->tm.tm_year));
 
707
            break;
 
708
            */
 
709
 
 
710
    case NUMBER:
 
711
            /* get numeric <sec since 1970>, MM/DD/[YY]YY, or DD.MM.[YY]YY
 
712
             */
 
713
            tlen = strlen(sc_token);
 
714
            mon = atol(sc_token);
 
715
            if (mon > 10*356*24*60*60) {
 
716
                ptv->tm=*localtime(&mon);
 
717
                token();
 
718
                break;
 
719
            }
 
720
 
 
721
            if (mon > 19700101 && mon < 24000101){ /*works between 1900 and 2400 */
 
722
                char  cmon[3],cmday[3],cyear[5];
 
723
                strncpy(cyear,sc_token,4);cyear[4]='\0';              
 
724
                year = atol(cyear);           
 
725
                strncpy(cmon,&(sc_token[4]),2);cmon[2]='\0';
 
726
                mon = atol(cmon);
 
727
                strncpy(cmday,&(sc_token[6]),2);cmday[2]='\0';
 
728
                mday = atol(cmday);
 
729
                token();
 
730
            } else { 
 
731
              token();
 
732
              
 
733
              if (mon <= 31 && (sc_tokid == SLASH || sc_tokid == DOT)) {
 
734
                int sep;                    
 
735
                sep = sc_tokid;
 
736
               try(expect2(NUMBER,"there should be %s number after '%c'",
 
737
                           sep == DOT ? "month" : "day", sep == DOT ? '.' : '/'));
 
738
                mday = atol(sc_token);
 
739
                if (token() == sep) {
 
740
                 try(expect2(NUMBER,"there should be year number after '%c'",
 
741
                             sep == DOT ? '.' : '/'));
 
742
                  year = atol(sc_token);
 
743
                  token();
 
744
                }
 
745
                
 
746
                /* flip months and days for european timing
 
747
                 */
 
748
                if (sep == DOT) {
 
749
                  long x = mday;
 
750
                  mday = mon;
 
751
                  mon = x;
 
752
                }
 
753
              }
 
754
            }
 
755
 
 
756
            mon--;
 
757
            if(mon < 0 || mon > 11 ) {
 
758
                panic(e("did you really mean month %d?", mon+1));
 
759
            }
 
760
            if(mday < 1 || mday > 31) {
 
761
                panic(e("I'm afraid that %d is not a valid day of the month",
 
762
                        mday));
 
763
            }      
 
764
            try(assign_date(ptv, mday, mon, year));
 
765
            break;
 
766
    } /* case */
 
767
    return TIME_OK;
 
768
} /* month */
 
769
 
 
770
 
 
771
/* Global functions */
 
772
 
 
773
 
 
774
/*
 
775
 * parsetime() is the external interface that takes tspec, parses
 
776
 * it and puts the result in the time_value structure *ptv.
 
777
 * It can return either absolute times (these are ensured to be
 
778
 * correct) or relative time references that are expected to be
 
779
 * added to some absolute time value and then normalized by
 
780
 * mktime() The return value is either TIME_OK (aka NULL) or
 
781
 * the pointer to the error message in the case of problems
 
782
 */
 
783
char *
 
784
parsetime(char *tspec, struct time_value *ptv)
 
785
{
 
786
    time_t now = time(NULL);
 
787
    int hr = 0;
 
788
    /* this MUST be initialized to zero for midnight/noon/teatime */
 
789
 
 
790
    Specials = VariousWords; /* initialize special words context */
 
791
 
 
792
    try(init_scanner( 1, &tspec ));
 
793
 
 
794
    /* establish the default time reference */
 
795
    ptv->type = ABSOLUTE_TIME;
 
796
    ptv->offset = 0;
 
797
    ptv->tm = *localtime(&now);
 
798
    ptv->tm.tm_isdst = -1; /* mk time can figure this out for us ... */
 
799
 
 
800
    token();
 
801
    switch (sc_tokid) {
 
802
    case PLUS:
 
803
    case MINUS:
 
804
            break; /* jump to OFFSET-SPEC part */
 
805
 
 
806
    case START:
 
807
            ptv->type = RELATIVE_TO_START_TIME;
 
808
            goto KeepItRelative;
 
809
    case END:
 
810
            ptv->type = RELATIVE_TO_END_TIME;
 
811
         KeepItRelative:
 
812
            ptv->tm.tm_sec  = 0;
 
813
            ptv->tm.tm_min  = 0;
 
814
            ptv->tm.tm_hour = 0;
 
815
            ptv->tm.tm_mday = 0;
 
816
            ptv->tm.tm_mon  = 0;
 
817
            ptv->tm.tm_year = 0;
 
818
            /* FALLTHRU */
 
819
    case NOW:
 
820
            {
 
821
            int time_reference = sc_tokid;
 
822
            token();
 
823
            if( sc_tokid == PLUS || sc_tokid == MINUS )
 
824
              break;
 
825
            if( time_reference != NOW ) {
 
826
              panic(e("'start' or 'end' MUST be followed by +|- offset"));
 
827
            }
 
828
            else
 
829
              if( sc_tokid != EOF ) {
 
830
                panic(e("if 'now' is followed by a token it must be +|- offset"));      
 
831
              }
 
832
            };
 
833
            break;
 
834
 
 
835
    /* Only absolute time specifications below */
 
836
    case NUMBER:
 
837
            try(tod(ptv))
 
838
            if (sc_tokid != NUMBER) break; 
 
839
    /* fix month parsing */
 
840
    case JAN: case FEB: case MAR: case APR: case MAY: case JUN:
 
841
    case JUL: case AUG: case SEP: case OCT: case NOV: case DEC:
 
842
            try(day(ptv));
 
843
            if (sc_tokid != NUMBER) break;
 
844
            try(tod(ptv))
 
845
            break;
 
846
 
 
847
            /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialised
 
848
             * hr to zero up above, then fall into this case in such a
 
849
             * way so we add +12 +4 hours to it for teatime, +12 hours
 
850
             * to it for noon, and nothing at all for midnight, then
 
851
             * set our rettime to that hour before leaping into the
 
852
             * month scanner
 
853
             */
 
854
    case TEATIME:
 
855
            hr += 4;
 
856
            /* FALLTHRU */
 
857
    case NOON:
 
858
            hr += 12;
 
859
            /* FALLTHRU */
 
860
    case MIDNIGHT:
 
861
            /* if (ptv->tm.tm_hour >= hr) {
 
862
                ptv->tm.tm_mday++;
 
863
                ptv->tm.tm_wday++;
 
864
            } */ /* shifting does not makes sense here ... noon is noon */ 
 
865
            ptv->tm.tm_hour = hr;
 
866
            ptv->tm.tm_min = 0;
 
867
            ptv->tm.tm_sec = 0;
 
868
            token();
 
869
            try(day(ptv));
 
870
            break;
 
871
    default:
 
872
            panic(e("unparsable time: %s%s",sc_token,sct));
 
873
            break;
 
874
    } /* ugly case statement */
 
875
 
 
876
    /*
 
877
     * the OFFSET-SPEC part
 
878
     *
 
879
     * (NOTE, the sc_tokid was prefetched for us by the previous code)
 
880
     */
 
881
    if( sc_tokid == PLUS || sc_tokid == MINUS ) {
 
882
        Specials = TimeMultipliers; /* switch special words context */
 
883
        while( sc_tokid == PLUS || sc_tokid == MINUS ||
 
884
                               sc_tokid == NUMBER ) {
 
885
            if( sc_tokid == NUMBER ) {
 
886
                try(plus_minus(ptv, PREVIOUS_OP ));
 
887
            } else
 
888
                try(plus_minus(ptv, sc_tokid));
 
889
            token(); /* We will get EOF eventually but that's OK, since
 
890
                            token() will return us as many EOFs as needed */
 
891
        }
 
892
    }
 
893
 
 
894
    /* now we should be at EOF */
 
895
    if( sc_tokid != EOF ) {
 
896
      panic(e("unparsable trailing text: '...%s%s'", sc_token, sct));
 
897
    }
 
898
 
 
899
    ptv->tm.tm_isdst = -1; /* for mktime to guess DST status */
 
900
    if( ptv->type == ABSOLUTE_TIME )
 
901
      if( mktime( &ptv->tm ) == -1 ) { /* normalize & check */
 
902
        /* can happen for "nonexistent" times, e.g. around 3am */
 
903
        /* when winter -> summer time correction eats a hour */
 
904
        panic(e("the specified time is incorrect (out of range?)"));
 
905
      }
 
906
    EnsureMemFree();
 
907
    return TIME_OK;
 
908
} /* parsetime */
 
909
 
 
910
 
 
911
int proc_start_end (struct time_value *start_tv, 
 
912
                    struct time_value *end_tv, 
 
913
                    time_t *start, 
 
914
                    time_t *end){
 
915
    if (start_tv->type == RELATIVE_TO_END_TIME  && /* same as the line above */
 
916
        end_tv->type == RELATIVE_TO_START_TIME) {
 
917
        rrd_set_error("the start and end times cannot be specified "
 
918
                      "relative to each other");
 
919
        return -1;
 
920
    }
 
921
 
 
922
    if (start_tv->type == RELATIVE_TO_START_TIME) {
 
923
        rrd_set_error("the start time cannot be specified relative to itself");
 
924
        return -1;
 
925
    }
 
926
 
 
927
    if (end_tv->type == RELATIVE_TO_END_TIME) {
 
928
        rrd_set_error("the end time cannot be specified relative to itself");
 
929
        return -1;
 
930
    }
 
931
 
 
932
    if( start_tv->type == RELATIVE_TO_END_TIME) {
 
933
        struct tm tmtmp;
 
934
        *end = mktime(&(end_tv->tm)) + end_tv->offset;    
 
935
        tmtmp = *localtime(end); /* reinit end including offset */
 
936
        tmtmp.tm_mday += start_tv->tm.tm_mday;
 
937
        tmtmp.tm_mon += start_tv->tm.tm_mon;
 
938
        tmtmp.tm_year += start_tv->tm.tm_year;  
 
939
        *start = mktime(&tmtmp) + start_tv->offset;
 
940
    } else {
 
941
        *start = mktime(&(start_tv->tm)) + start_tv->offset;
 
942
    }
 
943
    if (end_tv->type == RELATIVE_TO_START_TIME) {
 
944
        struct tm tmtmp;
 
945
        *start = mktime(&(start_tv->tm)) + start_tv->offset;
 
946
        tmtmp = *localtime(start);
 
947
        tmtmp.tm_mday += end_tv->tm.tm_mday;
 
948
        tmtmp.tm_mon += end_tv->tm.tm_mon;
 
949
        tmtmp.tm_year += end_tv->tm.tm_year;    
 
950
        *end = mktime(&tmtmp) + end_tv->offset;
 
951
    } else {
 
952
        *end = mktime(&(end_tv->tm)) + end_tv->offset;
 
953
    }    
 
954
    return 0;
 
955
} /* proc_start_end */
 
956
 
 
957
 
 
958
 
 
959
 
 
960
 
 
961
 
 
962