Code

From: Sebastian Harl sh tokkee.org
authoroetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa>
Sun, 8 Jun 2008 15:55:09 +0000 (15:55 +0000)
committeroetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa>
Sun, 8 Jun 2008 15:55:09 +0000 (15:55 +0000)
Date: Sun, 8 Jun 2008 15:01:05 +0200
Subject: [PATCH] Cleanup the symbols exported by librrd.

Up to know librrd exported a lot of symbols, most of which are to be
regarded as private symbols. This somewhat pollutes the API as the symbols
could, in theory, be used by external software and, more importantly,
makes symbol based dependencies (as recently introduced in e.g. Debian)
somewhat harder to implement.

This patch does a somewhat large-scale cleanup of the exported symbols:

 * Introduced a librrd.sym file which contains all symbols that are to be
   exported. This file is then passed to libtool using the -export-symbols
   option which tells the linker to export the given symbols only (note:
   according to the libtool manual, this has no effect on some
   architectures - however, I assume that most architectures in use today
   do support it).

   librrd.sym contains all symbols originally defined in rrd.h sans
   LockRRD() (which has been moved to rrd_tool.h). The following functions
   have been added to rrd.h and the list of exported symbols (some of them
   have been renamed, see below):
   - rrd_info()
   - rrd_info_free()
   - rrd_info_print()
   - rrd_info_push()
   - rrd_lastupdate()
   - rrd_update_v()
   - rrd_strerror()

 * Prefixed all public functions and types with "rrd_" to avoid name
   clashes with other libraries. Also, while I was at it, I introduced
   typedefs for all custom types and prefixed the time names with "_t" to
   improve consistency:
   - enum info_type -> rrd_info_type_t
   - enum timetype -> rrd_timetype_t

   - union infoval -> rrd_infoval_t

   - struct info_t -> rrd_info_t
   - struct rrd_context -> rrd_context_t
   - struct rrd_time_value -> rrd_time_value_t

   - info_free() -> rrd_info_free()
   - info_free() -> rrd_info_free()
   - info_print() -> rrd_info_print()
   - info_push() -> rrd_info_push()
   - LockRRD() -> rrd_lock() (not public though)
   - parsetime() -> rrd_parsetime()
     (and: src/parsetime.c -> src/rrd_parsetime.c)
   - proc_start_end() -> rrd_proc_start_end()
   - set_to_DINF() -> rrd_set_to_DINF()
   - set_to_DNAN() -> rrd_set_to_DNAN()

 * Moved readfile() from rrd_open.c to rrd_cgi.c and declared it static.
   This function is used in rrd_cgi.c only.

 * rrd_lock() (f.k.a. LockRRD()) now accepts a rrd_file_t pointer instead
   of an integer to increase encapsulation.

git-svn-id: svn://svn.oetiker.ch/rrdtool/trunk@1405 a5681a0c-68f1-0310-ab6d-d61299d08faa

33 files changed:
program/bindings/perl-shared/RRDs.xs
program/bindings/python/rrdtoolmodule.c
program/bindings/ruby/main.c
program/bindings/tcl/tclrrd.c
program/doc/rrdthreads.pod
program/netware/Makefile
program/src/Makefile.am
program/src/librrd.sym [new file with mode: 0644]
program/src/parsetime.c [deleted file]
program/src/parsetime.h [deleted file]
program/src/rrd.h
program/src/rrd_cgi.c
program/src/rrd_create.c
program/src/rrd_error.c
program/src/rrd_fetch.c
program/src/rrd_graph.c
program/src/rrd_graph.h
program/src/rrd_graph_helper.c
program/src/rrd_info.c
program/src/rrd_nan_inf.c
program/src/rrd_not_thread_safe.c
program/src/rrd_open.c
program/src/rrd_parsetime.c [new file with mode: 0644]
program/src/rrd_parsetime.h [new file with mode: 0644]
program/src/rrd_resize.c
program/src/rrd_thread_safe.c
program/src/rrd_thread_safe_nt.c
program/src/rrd_tool.c
program/src/rrd_tool.h
program/src/rrd_update.c
program/src/rrd_xport.c
program/win32/rrd.dsp
program/win32/rrd.vcproj

index 5eeba18444e55609674100e660ae4b780ad35d55..b2a70d951b8b2174abdb2d50a9f4932a9955a199 100644 (file)
@@ -307,20 +307,20 @@ rrd_times(start, end)
          char *start
          char *end
        PREINIT:
-               struct  rrd_time_value start_tv, end_tv;
+               rrd_time_value_t start_tv, end_tv;
                char    *parsetime_error = NULL;
                time_t  start_tmp, end_tmp;
        PPCODE:
                rrd_clear_error();
-               if( (parsetime_error = parsetime( start, &start_tv))) {
-                       rrd_set_error( "start time: %s", parsetime_error);
+               if ((parsetime_error = rrd_parsetime(start, &start_tv))) {
+                       rrd_set_error("start time: %s", parsetime_error);
                        XSRETURN_UNDEF;
                }
-               if( (parsetime_error = parsetime( end, &end_tv))) {
-                       rrd_set_error( "end time: %s", parsetime_error);
+               if ((parsetime_error = rrd_parsetime(end, &end_tv))) {
+                       rrd_set_error("end time: %s", parsetime_error);
                        XSRETURN_UNDEF;
                }
-               if( proc_start_end( &start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
+               if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
                        XSRETURN_UNDEF;
                }
                EXTEND(sp,2);
@@ -389,7 +389,7 @@ SV*
 rrd_info(...)
        PROTOTYPE: @    
        PREINIT:
-               info_t *data,*save;
+               rrd_info_t *data,*save;
                 int i;
                 char **argv;
                HV *hash;
@@ -402,7 +402,7 @@ SV*
 rrd_updatev(...)
        PROTOTYPE: @    
        PREINIT:
-               info_t *data,*save;
+               rrd_info_t *data,*save;
                 int i;
                 char **argv;
                HV *hash;
@@ -415,7 +415,7 @@ SV*
 rrd_graphv(...)
        PROTOTYPE: @    
        PREINIT:
-               info_t *data,*save;
+               rrd_info_t *data,*save;
                 int i;
                 char **argv;
                HV *hash;
index 4d3a0d57e031d23f2af1d58eb69f0424e26114a8..b3caf192871881dc8d437e2a4cda207c7cb5cfe1 100644 (file)
@@ -404,7 +404,7 @@ static PyObject *PyRRD_resize(
 }
 
 static PyObject *PyDict_FromInfo(
-    info_t *data)
+    rrd_info_t *data)
 {
     PyObject *r;
 
@@ -451,7 +451,7 @@ static PyObject *PyRRD_info(
     PyObject *r;
     int       argc;
     char    **argv;
-    info_t   *data;
+    rrd_info_t *data;
 
     if (create_args("info", args, &argc, &argv) < 0)
         return NULL;
@@ -462,7 +462,7 @@ static PyObject *PyRRD_info(
         return NULL;
     }
     r = PyDict_FromInfo(data);
-    info_free(data);
+    rrd_info_free(data);
     return r;
 }
 
@@ -476,7 +476,7 @@ static PyObject *PyRRD_graphv(
     PyObject *r;
     int       argc;
     char    **argv;
-    info_t   *data;
+    rrd_info_t *data;
 
     if (create_args("graphv", args, &argc, &argv) < 0)
         return NULL;
@@ -487,7 +487,7 @@ static PyObject *PyRRD_graphv(
         return NULL;
     }
     r = PyDict_FromInfo(data);
-    info_free(data);
+    rrd_info_free(data);
     return r;
 }
 
@@ -501,7 +501,7 @@ static PyObject *PyRRD_updatev(
     PyObject *r;
     int       argc;
     char    **argv;
-    info_t   *data;
+    rrd_info_t *data;
 
     if (create_args("updatev", args, &argc, &argv) < 0)
         return NULL;
@@ -512,7 +512,7 @@ static PyObject *PyRRD_updatev(
         return NULL;
     }
     r = PyDict_FromInfo(data);
-    info_free(data);
+    rrd_info_free(data);
     return r;
 }
 
index b3e512e8f074a4f56a52c0c317d78a720c24fd8f..353aa4def19a4ecab74577aec6e18f601d907bcc 100644 (file)
@@ -146,7 +146,7 @@ VALUE rb_rrd_infocall(
     VALUE args)
 {
     string_arr a;
-    info_t   *p, *data;
+    rrd_info_t *p, *data;
     VALUE     result;
 
     a = string_arr_new(args);
index c99c1b08354f46ef11efa074a590708748368f52..d4593bb41c27fb85f6383e103c61c9a2afc2be39 100644 (file)
@@ -108,7 +108,7 @@ static int Rrd_Create(
     time_t    last_up = time(NULL) - 10;
     long int  long_tmp;
     unsigned long int pdp_step = 300;
-    struct rrd_time_value last_up_tv;
+    rrd_time_value_t last_up_tv;
 
     argv2 = getopt_init(argc, argv);
 
@@ -121,7 +121,7 @@ static int Rrd_Create(
                 getopt_cleanup(argc, argv2);
                 return TCL_ERROR;
             }
-            if ((parsetime_error = parsetime(argv2[argv_i], &last_up_tv))) {
+            if ((parsetime_error = rrd_parsetime(argv2[argv_i], &last_up_tv))) {
                 Tcl_AppendResult(interp, "RRD Error: invalid time format: '",
                                  argv2[argv_i], "'", (char *) NULL);
                 getopt_cleanup(argc, argv2);
index ace7ee8c395c0d0eb4d95e1aaab036f3a03bce8b..71506e99e58beac49640be9b7313ab96da15dc84 100644 (file)
@@ -126,7 +126,7 @@ C<rrd_update_r> as an example.
 
 =item *
 
-Do not use the C<parsetime> function!
+Do not use the C<rrd_parsetime> function!
 
 It uses lots of global variables. You may use it in functions not designed
 to be thread-safe, like in functions wrapping the C<_r> version of some
index c138dfa0b06d81b0df263291aac83e25b0837e40..ec4e287b3824fb9020d612b699c4313d54887785 100644 (file)
@@ -227,7 +227,7 @@ XLIBOBJS    = \
        $(OBJDIR)/rrd_getopt1.o \
        $(OBJDIR)/art_rgba_svp.o \
        $(OBJDIR)/hash_32.o \
-       $(OBJDIR)/parsetime.o \
+       $(OBJDIR)/rrd_parsetime.o \
        $(OBJDIR)/pngsize.o \
        $(EOLIST)
 
index daaacb6bee7d6ac1321bc5fd4b4b8030ec96053f..65febf60de6bfc9a90e9830c3a2e3f28acd71923 100644 (file)
@@ -16,7 +16,7 @@ AM_CPPFLAGS = -DRRD_DEFAULT_FONT=\"$(RRD_DEFAULT_FONT)\" -DNUMVERS=@NUMVERS@
 UPD_C_FILES =          \
        rrd_getopt.c    \
        rrd_getopt1.c   \
-       parsetime.c     \
+       rrd_parsetime.c \
        rrd_hw.c        \
        rrd_hw_math.c   \
        rrd_hw_update.c \
@@ -44,13 +44,12 @@ RRD_C_FILES =               \
        rrd_gfx.c \
        rrd_dump.c      \
        rrd_fetch.c     \
-       rrd_tool.c      \
        rrd_resize.c \
        rrd_tune.c
 
 noinst_HEADERS = \
        unused.h \
-       rrd_getopt.h parsetime.h \
+       rrd_getopt.h rrd_parsetime.h \
        rrd_i18n.h \
        rrd_format.h rrd_tool.h rrd_xport.h rrd.h rrd_rpncalc.h \
        rrd_hw.h rrd_hw_math.h rrd_hw_update.h \
@@ -68,15 +67,16 @@ librrdupd_la_SOURCES      = $(UPD_C_FILES) rrd_not_thread_safe.c
 librrdupd_la_LIBADD       = $(CORE_LIBS) @LIB_LIBINTL@
 
 librrd_la_SOURCES         = $(RRD_C_FILES)
+librrd_la_DEPENDENCIES    = librrdupd.la librrd.sym
 librrd_la_LIBADD          = librrdupd.la $(ALL_LIBS)
-
-# see http://sourceware.org/autobook/autobook/autobook_91.html
-
 librrd_la_LDFLAGS         = -version-info @LIBVERS@
+librrd_la_LDFLAGS         += -export-symbols librrd.sym
 
 librrd_th_la_SOURCES         = $(UPD_C_FILES) $(RRD_C_FILES) rrd_thread_safe.c
+librrd_th_la_DEPENDENCIES    = librrd.sym
 librrd_th_la_CFLAGS          = $(MULTITHREAD_CFLAGS)
 librrd_th_la_LDFLAGS         = $(MULTITHREAD_LDFLAGS) -version-info @LIBVERS@
+librrd_th_la_LDFLAGS         += -export-symbols librrd.sym
 librrd_th_la_LIBADD          = $(ALL_LIBS)
 
 include_HEADERS        = rrd.h
@@ -93,8 +93,8 @@ rrdcgi_LDADD  = librrd.la
 rrdupdate_SOURCES = rrdupdate.c
 rrdupdate_LDADD          = librrdupd.la
 
-rrdtool_SOURCES = 
-rrdtool_DEPENDENCIES = rrd_tool.o librrd.la
+rrdtool_SOURCES = rrd_tool.c
+rrdtool_DEPENDENCIES = librrd.la
 rrdtool_LDADD  = librrd.la
 
 # strftime is here because we do not usually need it. unices have propper
diff --git a/program/src/librrd.sym b/program/src/librrd.sym
new file mode 100644 (file)
index 0000000..0e29bf0
--- /dev/null
@@ -0,0 +1,38 @@
+rrd_clear_error
+rrd_create
+rrd_create_r
+rrd_dump
+rrd_dump_r
+rrd_fetch
+rrd_fetch_r
+rrd_first
+rrd_first_r
+rrd_free_context
+rrd_get_context
+rrd_get_error
+rrd_graph
+rrd_graph_v
+rrd_info
+rrd_info_free
+rrd_info_print
+rrd_info_push
+rrd_last
+rrd_last_r
+rrd_lastupdate
+rrd_new_context
+rrd_parsetime
+rrd_proc_start_end
+rrd_resize
+rrd_restore
+rrd_set_error
+rrd_set_to_DINF
+rrd_set_to_DNAN
+rrd_strerror
+rrd_strversion
+rrd_test_error
+rrd_tune
+rrd_update
+rrd_update_r
+rrd_update_v
+rrd_version
+rrd_xport
diff --git a/program/src/parsetime.c b/program/src/parsetime.c
deleted file mode 100644 (file)
index 8818f1c..0000000
+++ /dev/null
@@ -1,1043 +0,0 @@
-/*  
- *  parsetime.c - parse time for at(1)
- *  Copyright (C) 1993, 1994  Thomas Koenig
- *
- *  modifications for English-language times
- *  Copyright (C) 1993  David Parsons
- *
- *  A lot of modifications and extensions 
- *  (including the new syntax being useful for RRDB)
- *  Copyright (C) 1999  Oleg Cherevko (aka Olwi Deer)
- *
- *  severe structural damage inflicted by Tobi Oetiker in 1999
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. The name of the author(s) may not be used to endorse or promote
- *    products derived from this software without specific prior written
- *    permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/* NOTE: nothing in here is thread-safe!!!! Not even the localtime
-   calls ... */
-
-/*
- * The BNF-like specification of the time syntax parsed is below:
- *                                                               
- * As usual, [ X ] means that X is optional, { X } means that X may
- * be either omitted or specified as many times as needed,
- * alternatives are separated by |, brackets are used for grouping.
- * (# marks the beginning of comment that extends to the end of line)
- *
- * TIME-SPECIFICATION ::= TIME-REFERENCE [ OFFSET-SPEC ] |
- *                                        OFFSET-SPEC   |
- *                        ( START | END ) OFFSET-SPEC 
- *
- * TIME-REFERENCE ::= NOW | TIME-OF-DAY-SPEC [ DAY-SPEC-1 ] |
- *                        [ TIME-OF-DAY-SPEC ] DAY-SPEC-2
- *
- * TIME-OF-DAY-SPEC ::= NUMBER (':') NUMBER [am|pm] | # HH:MM
- *                     'noon' | 'midnight' | 'teatime'
- *
- * DAY-SPEC-1 ::= NUMBER '/' NUMBER '/' NUMBER |  # MM/DD/[YY]YY
- *                NUMBER '.' NUMBER '.' NUMBER |  # DD.MM.[YY]YY
- *                NUMBER                          # Seconds since 1970
- *                NUMBER                          # YYYYMMDD
- *
- * DAY-SPEC-2 ::= MONTH-NAME NUMBER [NUMBER] |    # Month DD [YY]YY
- *                'yesterday' | 'today' | 'tomorrow' |
- *                DAY-OF-WEEK
- *
- *
- * OFFSET-SPEC ::= '+'|'-' NUMBER TIME-UNIT { ['+'|'-'] NUMBER TIME-UNIT }
- *
- * TIME-UNIT ::= SECONDS | MINUTES | HOURS |
- *               DAYS | WEEKS | MONTHS | YEARS
- *
- * NOW ::= 'now' | 'n'
- *
- * START ::= 'start' | 's'
- * END   ::= 'end' | 'e'
- *
- * SECONDS ::= 'seconds' | 'second' | 'sec' | 's'
- * MINUTES ::= 'minutes' | 'minute' | 'min' | 'm'
- * HOURS   ::= 'hours' | 'hour' | 'hr' | 'h'
- * DAYS    ::= 'days' | 'day' | 'd'
- * WEEKS   ::= 'weeks' | 'week' | 'wk' | 'w'
- * MONTHS  ::= 'months' | 'month' | 'mon' | 'm'
- * YEARS   ::= 'years' | 'year' | 'yr' | 'y'
- *
- * MONTH-NAME ::= 'jan' | 'january' | 'feb' | 'february' | 'mar' | 'march' |
- *                'apr' | 'april' | 'may' | 'jun' | 'june' | 'jul' | 'july' |
- *                'aug' | 'august' | 'sep' | 'september' | 'oct' | 'october' |
- *               'nov' | 'november' | 'dec' | 'december'
- *
- * DAY-OF-WEEK ::= 'sunday' | 'sun' | 'monday' | 'mon' | 'tuesday' | 'tue' |
- *                 'wednesday' | 'wed' | 'thursday' | 'thu' | 'friday' | 'fri' |
- *                 'saturday' | 'sat'
- *
- *
- * As you may note, there is an ambiguity with respect to
- * the 'm' time unit (which can mean either minutes or months).
- * To cope with this, code tries to read users mind :) by applying
- * certain heuristics. There are two of them:
- *
- * 1. If 'm' is used in context of (i.e. right after the) years,
- *    months, weeks, or days it is assumed to mean months, while
- *    in the context of hours, minutes, and seconds it means minutes.
- *    (e.g., in -1y6m or +3w1m 'm' means 'months', while in
- *    -3h20m or +5s2m 'm' means 'minutes')
- *
- * 2. Out of context (i.e. right after the '+' or '-' sign) the
- *    meaning of 'm' is guessed from the number it directly follows.
- *    Currently, if the number absolute value is below 25 it is assumed
- *    that 'm' means months, otherwise it is treated as minutes.
- *    (e.g., -25m == -25 minutes, while +24m == +24 months)
- *
- */
-
-/* System Headers */
-
-/* Local headers */
-
-#include "rrd_tool.h"
-#include <stdarg.h>
-
-/* Structures and unions */
-
-enum {                  /* symbols */
-    MIDNIGHT, NOON, TEATIME,
-    PM, AM, YESTERDAY, TODAY, TOMORROW, NOW, START, END,
-    SECONDS, MINUTES, HOURS, DAYS, WEEKS, MONTHS, YEARS,
-    MONTHS_MINUTES,
-    NUMBER, PLUS, MINUS, DOT, COLON, SLASH, ID, JUNK,
-    JAN, FEB, MAR, APR, MAY, JUN,
-    JUL, AUG, SEP, OCT, NOV, DEC,
-    SUN, MON, TUE, WED, THU, FRI, SAT
-};
-
-/* the below is for plus_minus() */
-#define PREVIOUS_OP    (-1)
-
-/* parse translation table - table driven parsers can be your FRIEND!
- */
-struct SpecialToken {
-    char     *name;     /* token name */
-    int       value;    /* token id */
-};
-static const struct SpecialToken VariousWords[] = {
-    {"midnight", MIDNIGHT}, /* 00:00:00 of today or tomorrow */
-    {"noon", NOON},     /* 12:00:00 of today or tomorrow */
-    {"teatime", TEATIME},   /* 16:00:00 of today or tomorrow */
-    {"am", AM},         /* morning times for 0-12 clock */
-    {"pm", PM},         /* evening times for 0-12 clock */
-    {"tomorrow", TOMORROW},
-    {"yesterday", YESTERDAY},
-    {"today", TODAY},
-    {"now", NOW},
-    {"n", NOW},
-    {"start", START},
-    {"s", START},
-    {"end", END},
-    {"e", END},
-
-    {"jan", JAN},
-    {"feb", FEB},
-    {"mar", MAR},
-    {"apr", APR},
-    {"may", MAY},
-    {"jun", JUN},
-    {"jul", JUL},
-    {"aug", AUG},
-    {"sep", SEP},
-    {"oct", OCT},
-    {"nov", NOV},
-    {"dec", DEC},
-    {"january", JAN},
-    {"february", FEB},
-    {"march", MAR},
-    {"april", APR},
-    {"may", MAY},
-    {"june", JUN},
-    {"july", JUL},
-    {"august", AUG},
-    {"september", SEP},
-    {"october", OCT},
-    {"november", NOV},
-    {"december", DEC},
-    {"sunday", SUN},
-    {"sun", SUN},
-    {"monday", MON},
-    {"mon", MON},
-    {"tuesday", TUE},
-    {"tue", TUE},
-    {"wednesday", WED},
-    {"wed", WED},
-    {"thursday", THU},
-    {"thu", THU},
-    {"friday", FRI},
-    {"fri", FRI},
-    {"saturday", SAT},
-    {"sat", SAT},
-    {NULL, 0}           /*** SENTINEL ***/
-};
-
-static const struct SpecialToken TimeMultipliers[] = {
-    {"second", SECONDS},    /* seconds multiplier */
-    {"seconds", SECONDS},   /* (pluralized) */
-    {"sec", SECONDS},   /* (generic) */
-    {"s", SECONDS},     /* (short generic) */
-    {"minute", MINUTES},    /* minutes multiplier */
-    {"minutes", MINUTES},   /* (pluralized) */
-    {"min", MINUTES},   /* (generic) */
-    {"m", MONTHS_MINUTES},  /* (short generic) */
-    {"hour", HOURS},    /* hours ... */
-    {"hours", HOURS},   /* (pluralized) */
-    {"hr", HOURS},      /* (generic) */
-    {"h", HOURS},       /* (short generic) */
-    {"day", DAYS},      /* days ... */
-    {"days", DAYS},     /* (pluralized) */
-    {"d", DAYS},        /* (short generic) */
-    {"week", WEEKS},    /* week ... */
-    {"weeks", WEEKS},   /* (pluralized) */
-    {"wk", WEEKS},      /* (generic) */
-    {"w", WEEKS},       /* (short generic) */
-    {"month", MONTHS},  /* week ... */
-    {"months", MONTHS}, /* (pluralized) */
-    {"mon", MONTHS},    /* (generic) */
-    {"year", YEARS},    /* year ... */
-    {"years", YEARS},   /* (pluralized) */
-    {"yr", YEARS},      /* (generic) */
-    {"y", YEARS},       /* (short generic) */
-    {NULL, 0}           /*** SENTINEL ***/
-};
-
-/* File scope variables */
-
-/* context dependent list of specials for parser to recognize,
- * required for us to be able distinguish between 'mon' as 'month'
- * and 'mon' as 'monday'
- */
-static const struct SpecialToken *Specials;
-
-static const char **scp;    /* scanner - pointer at arglist */
-static char scc;        /* scanner - count of remaining arguments */
-static const char *sct; /* scanner - next char pointer in current argument */
-static int need;        /* scanner - need to advance to next argument */
-
-static char *sc_token = NULL;   /* scanner - token buffer */
-static size_t sc_len;   /* scanner - length of token buffer */
-static int sc_tokid;    /* scanner - token id */
-
-/* Local functions */
-static void EnsureMemFree(
-    void);
-
-static void EnsureMemFree(
-    void)
-{
-    if (sc_token) {
-        free(sc_token);
-        sc_token = NULL;
-    }
-}
-
-/*
- * A hack to compensate for the lack of the C++ exceptions
- *
- * Every function func that might generate parsing "exception"
- * should return TIME_OK (aka NULL) or pointer to the error message,
- * and should be called like this: try(func(args));
- *
- * if the try is not successful it will reset the token pointer ...
- *
- * [NOTE: when try(...) is used as the only statement in the "if-true"
- *  part of the if statement that also has an "else" part it should be
- *  either enclosed in the curly braces (despite the fact that it looks
- *  like a single statement) or NOT followed by the ";"]
- */
-#define try(b)         { \
-                       char *_e; \
-                       if((_e=(b))) \
-                         { \
-                         EnsureMemFree(); \
-                         return _e; \
-                         } \
-                       }
-
-/*
- * The panic() function was used in the original code to die, we redefine
- * it as macro to start the chain of ascending returns that in conjunction
- * with the try(b) above will simulate a sort of "exception handling"
- */
-
-#define panic(e)       { \
-                       return (e); \
-                       }
-
-/*
- * ve() and e() are used to set the return error,
- * the most appropriate use for these is inside panic(...) 
- */
-#define MAX_ERR_MSG_LEN        1024
-static char errmsg[MAX_ERR_MSG_LEN];
-
-static char *ve(
-    char *fmt,
-    va_list ap)
-{
-#ifdef HAVE_VSNPRINTF
-    vsnprintf(errmsg, MAX_ERR_MSG_LEN, fmt, ap);
-#else
-    vsprintf(errmsg, fmt, ap);
-#endif
-    EnsureMemFree();
-    return (errmsg);
-}
-
-static char *e(
-    char *fmt,
-    ...)
-{
-    char     *err;
-    va_list   ap;
-
-    va_start(ap, fmt);
-    err = ve(fmt, ap);
-    va_end(ap);
-    return (err);
-}
-
-/* Compare S1 and S2, ignoring case, returning less than, equal to or
-   greater than zero if S1 is lexicographically less than,
-   equal to or greater than S2.  -- copied from GNU libc*/
-static int mystrcasecmp(
-    const char *s1,
-    const char *s2)
-{
-    const unsigned char *p1 = (const unsigned char *) s1;
-    const unsigned char *p2 = (const unsigned char *) s2;
-    unsigned char c1, c2;
-
-    if (p1 == p2)
-        return 0;
-
-    do {
-        c1 = tolower(*p1++);
-        c2 = tolower(*p2++);
-        if (c1 == '\0')
-            break;
-    }
-    while (c1 == c2);
-
-    return c1 - c2;
-}
-
-/*
- * parse a token, checking if it's something special to us
- */
-static int parse_token(
-    char *arg)
-{
-    int       i;
-
-    for (i = 0; Specials[i].name != NULL; i++)
-        if (mystrcasecmp(Specials[i].name, arg) == 0)
-            return sc_tokid = Specials[i].value;
-
-    /* not special - must be some random id */
-    return sc_tokid = ID;
-}                       /* parse_token */
-
-
-
-/*
- * init_scanner() sets up the scanner to eat arguments
- */
-static char *init_scanner(
-    int argc,
-    const char **argv)
-{
-    scp = argv;
-    scc = argc;
-    need = 1;
-    sc_len = 1;
-    while (argc-- > 0)
-        sc_len += strlen(*argv++);
-
-    sc_token = (char *) malloc(sc_len * sizeof(char));
-    if (sc_token == NULL)
-        return "Failed to allocate memory";
-    return TIME_OK;
-}                       /* init_scanner */
-
-/*
- * token() fetches a token from the input stream
- */
-static int token(
-    void)
-{
-    int       idx;
-
-    while (1) {
-        memset(sc_token, '\0', sc_len);
-        sc_tokid = EOF;
-        idx = 0;
-
-        /* if we need to read another argument, walk along the argument list;
-         * when we fall off the arglist, we'll just return EOF forever
-         */
-        if (need) {
-            if (scc < 1)
-                return sc_tokid;
-            sct = *scp;
-            scp++;
-            scc--;
-            need = 0;
-        }
-        /* eat whitespace now - if we walk off the end of the argument,
-         * we'll continue, which puts us up at the top of the while loop
-         * to fetch the next argument in
-         */
-        while (isspace((unsigned char) *sct) || *sct == '_' || *sct == ',')
-            ++sct;
-        if (!*sct) {
-            need = 1;
-            continue;
-        }
-
-        /* preserve the first character of the new token
-         */
-        sc_token[0] = *sct++;
-
-        /* then see what it is
-         */
-        if (isdigit((unsigned char) (sc_token[0]))) {
-            while (isdigit((unsigned char) (*sct)))
-                sc_token[++idx] = *sct++;
-            sc_token[++idx] = '\0';
-            return sc_tokid = NUMBER;
-        } else if (isalpha((unsigned char) (sc_token[0]))) {
-            while (isalpha((unsigned char) (*sct)))
-                sc_token[++idx] = *sct++;
-            sc_token[++idx] = '\0';
-            return parse_token(sc_token);
-        } else
-            switch (sc_token[0]) {
-            case ':':
-                return sc_tokid = COLON;
-            case '.':
-                return sc_tokid = DOT;
-            case '+':
-                return sc_tokid = PLUS;
-            case '-':
-                return sc_tokid = MINUS;
-            case '/':
-                return sc_tokid = SLASH;
-            default:
-                /*OK, we did not make it ... */
-                sct--;
-                return sc_tokid = EOF;
-            }
-    }                   /* while (1) */
-}                       /* token */
-
-
-/* 
- * expect2() gets a token and complains if it's not the token we want
- */
-static char *expect2(
-    int desired,
-    char *complain_fmt,
-    ...)
-{
-    va_list   ap;
-
-    va_start(ap, complain_fmt);
-    if (token() != desired) {
-        panic(ve(complain_fmt, ap));
-    }
-    va_end(ap);
-    return TIME_OK;
-
-}                       /* expect2 */
-
-
-/*
- * plus_minus() is used to parse a single NUMBER TIME-UNIT pair
- *              for the OFFSET-SPEC.
- *              It also applies those m-guessing heuristics.
- */
-static char *plus_minus(
-    struct rrd_time_value *ptv,
-    int doop)
-{
-    static int op = PLUS;
-    static int prev_multiplier = -1;
-    int       delta;
-
-    if (doop >= 0) {
-        op = doop;
-        try(expect2
-            (NUMBER, "There should be number after '%c'",
-             op == PLUS ? '+' : '-'));
-        prev_multiplier = -1;   /* reset months-minutes guessing mechanics */
-    }
-    /* if doop is < 0 then we repeat the previous op
-     * with the prefetched number */
-
-    delta = atoi(sc_token);
-
-    if (token() == MONTHS_MINUTES) {
-        /* hard job to guess what does that -5m means: -5mon or -5min? */
-        switch (prev_multiplier) {
-        case DAYS:
-        case WEEKS:
-        case MONTHS:
-        case YEARS:
-            sc_tokid = MONTHS;
-            break;
-
-        case SECONDS:
-        case MINUTES:
-        case HOURS:
-            sc_tokid = MINUTES;
-            break;
-
-        default:
-            if (delta < 6)  /* it may be some other value but in the context
-                             * of RRD who needs less than 6 min deltas? */
-                sc_tokid = MONTHS;
-            else
-                sc_tokid = MINUTES;
-        }
-    }
-    prev_multiplier = sc_tokid;
-    switch (sc_tokid) {
-    case YEARS:
-        ptv->tm.  tm_year += (
-    op == PLUS) ? delta : -delta;
-
-        return TIME_OK;
-    case MONTHS:
-        ptv->tm.  tm_mon += (
-    op == PLUS) ? delta : -delta;
-
-        return TIME_OK;
-    case WEEKS:
-        delta *= 7;
-        /* FALLTHRU */
-    case DAYS:
-        ptv->tm.  tm_mday += (
-    op == PLUS) ? delta : -delta;
-
-        return TIME_OK;
-    case HOURS:
-        ptv->offset += (op == PLUS) ? delta * 60 * 60 : -delta * 60 * 60;
-        return TIME_OK;
-    case MINUTES:
-        ptv->offset += (op == PLUS) ? delta * 60 : -delta * 60;
-        return TIME_OK;
-    case SECONDS:
-        ptv->offset += (op == PLUS) ? delta : -delta;
-        return TIME_OK;
-    default:           /*default unit is seconds */
-        ptv->offset += (op == PLUS) ? delta : -delta;
-        return TIME_OK;
-    }
-    panic(e("well-known time unit expected after %d", delta));
-    /* NORETURN */
-    return TIME_OK;     /* to make compiler happy :) */
-}                       /* plus_minus */
-
-
-/*
- * tod() computes the time of day (TIME-OF-DAY-SPEC)
- */
-static char *tod(
-    struct rrd_time_value *ptv)
-{
-    int       hour, minute = 0;
-    int       tlen;
-
-    /* save token status in  case we must abort */
-    int       scc_sv = scc;
-    const char *sct_sv = sct;
-    int       sc_tokid_sv = sc_tokid;
-
-    tlen = strlen(sc_token);
-
-    /* first pick out the time of day - we assume a HH (COLON|DOT) MM time
-     */
-    if (tlen > 2) {
-        return TIME_OK;
-    }
-
-    hour = atoi(sc_token);
-
-    token();
-    if (sc_tokid == SLASH || sc_tokid == DOT) {
-        /* guess we are looking at a date */
-        scc = scc_sv;
-        sct = sct_sv;
-        sc_tokid = sc_tokid_sv;
-        sprintf(sc_token, "%d", hour);
-        return TIME_OK;
-    }
-    if (sc_tokid == COLON) {
-        try(expect2(NUMBER,
-                    "Parsing HH:MM syntax, expecting MM as number, got none"));
-        minute = atoi(sc_token);
-        if (minute > 59) {
-            panic(e("parsing HH:MM syntax, got MM = %d (>59!)", minute));
-        }
-        token();
-    }
-
-    /* check if an AM or PM specifier was given
-     */
-    if (sc_tokid == AM || sc_tokid == PM) {
-        if (hour > 12) {
-            panic(e("there cannot be more than 12 AM or PM hours"));
-        }
-        if (sc_tokid == PM) {
-            if (hour != 12) /* 12:xx PM is 12:xx, not 24:xx */
-                hour += 12;
-        } else {
-            if (hour == 12) /* 12:xx AM is 00:xx, not 12:xx */
-                hour = 0;
-        }
-        token();
-    } else if (hour > 23) {
-        /* guess it was not a time then ... */
-        scc = scc_sv;
-        sct = sct_sv;
-        sc_tokid = sc_tokid_sv;
-        sprintf(sc_token, "%d", hour);
-        return TIME_OK;
-    }
-    ptv->tm.  tm_hour = hour;
-    ptv->tm.  tm_min = minute;
-    ptv->tm.  tm_sec = 0;
-
-    if (ptv->tm.tm_hour == 24) {
-        ptv->tm.  tm_hour = 0;
-        ptv->tm.  tm_mday++;
-    }
-    return TIME_OK;
-}                       /* tod */
-
-
-/*
- * assign_date() assigns a date, adjusting year as appropriate
- */
-static char *assign_date(
-    struct rrd_time_value *ptv,
-    long mday,
-    long mon,
-    long year)
-{
-    if (year > 138) {
-        if (year > 1970)
-            year -= 1900;
-        else {
-            panic(e("invalid year %d (should be either 00-99 or >1900)",
-                    year));
-        }
-    } else if (year >= 0 && year < 38) {
-        year += 100;    /* Allow year 2000-2037 to be specified as   */
-    }
-    /* 00-37 until the problem of 2038 year will */
-    /* arise for unices with 32-bit time_t :)    */
-    if (year < 70) {
-        panic(e("won't handle dates before epoch (01/01/1970), sorry"));
-    }
-
-    ptv->tm.  tm_mday = mday;
-    ptv->tm.  tm_mon = mon;
-    ptv->tm.  tm_year = year;
-
-    return TIME_OK;
-}                       /* assign_date */
-
-
-/* 
- * day() picks apart DAY-SPEC-[12]
- */
-static char *day(
-    struct rrd_time_value *ptv)
-{
-    /* using time_t seems to help portability with 64bit oses */
-    time_t    mday = 0, wday, mon, year = ptv->tm.tm_year;
-    int       tlen;
-
-    switch (sc_tokid) {
-    case YESTERDAY:
-        ptv->tm.  tm_mday--;
-
-        /* FALLTRHU */
-    case TODAY:        /* force ourselves to stay in today - no further processing */
-        token();
-        break;
-    case TOMORROW:
-        ptv->tm.  tm_mday++;
-
-        token();
-        break;
-
-    case JAN:
-    case FEB:
-    case MAR:
-    case APR:
-    case MAY:
-    case JUN:
-    case JUL:
-    case AUG:
-    case SEP:
-    case OCT:
-    case NOV:
-    case DEC:
-        /* do month mday [year]
-         */
-        mon = (sc_tokid - JAN);
-        try(expect2(NUMBER, "the day of the month should follow month name"));
-        mday = atol(sc_token);
-        if (token() == NUMBER) {
-            year = atol(sc_token);
-            token();
-        } else
-            year = ptv->tm.tm_year;
-
-        try(assign_date(ptv, mday, mon, year));
-        break;
-
-    case SUN:
-    case MON:
-    case TUE:
-    case WED:
-    case THU:
-    case FRI:
-    case SAT:
-        /* do a particular day of the week
-         */
-        wday = (sc_tokid - SUN);
-        ptv->tm.  tm_mday += (
-    wday - ptv->tm.tm_wday);
-
-        token();
-        break;
-        /*
-           mday = ptv->tm.tm_mday;
-           mday += (wday - ptv->tm.tm_wday);
-           ptv->tm.tm_wday = wday;
-
-           try(assign_date(ptv, mday, ptv->tm.tm_mon, ptv->tm.tm_year));
-           break;
-         */
-
-    case NUMBER:
-        /* get numeric <sec since 1970>, MM/DD/[YY]YY, or DD.MM.[YY]YY
-         */
-        tlen = strlen(sc_token);
-        mon = atol(sc_token);
-        if (mon > 10 * 365 * 24 * 60 * 60) {
-            ptv->tm = *localtime(&mon);
-
-            token();
-            break;
-        }
-
-        if (mon > 19700101 && mon < 24000101) { /*works between 1900 and 2400 */
-            char      cmon[3], cmday[3], cyear[5];
-
-            strncpy(cyear, sc_token, 4);
-            cyear[4] = '\0';
-            year = atol(cyear);
-            strncpy(cmon, &(sc_token[4]), 2);
-            cmon[2] = '\0';
-            mon = atol(cmon);
-            strncpy(cmday, &(sc_token[6]), 2);
-            cmday[2] = '\0';
-            mday = atol(cmday);
-            token();
-        } else {
-            token();
-
-            if (mon <= 31 && (sc_tokid == SLASH || sc_tokid == DOT)) {
-                int       sep;
-
-                sep = sc_tokid;
-                try(expect2(NUMBER, "there should be %s number after '%c'",
-                            sep == DOT ? "month" : "day",
-                            sep == DOT ? '.' : '/'));
-                mday = atol(sc_token);
-                if (token() == sep) {
-                    try(expect2
-                        (NUMBER, "there should be year number after '%c'",
-                         sep == DOT ? '.' : '/'));
-                    year = atol(sc_token);
-                    token();
-                }
-
-                /* flip months and days for European timing
-                 */
-                if (sep == DOT) {
-                    long      x = mday;
-
-                    mday = mon;
-                    mon = x;
-                }
-            }
-        }
-
-        mon--;
-        if (mon < 0 || mon > 11) {
-            panic(e("did you really mean month %d?", mon + 1));
-        }
-        if (mday < 1 || mday > 31) {
-            panic(e("I'm afraid that %d is not a valid day of the month",
-                    mday));
-        }
-        try(assign_date(ptv, mday, mon, year));
-        break;
-    }                   /* case */
-    return TIME_OK;
-}                       /* month */
-
-
-/* Global functions */
-
-
-/*
- * parsetime() is the external interface that takes tspec, parses
- * it and puts the result in the rrd_time_value structure *ptv.
- * It can return either absolute times (these are ensured to be
- * correct) or relative time references that are expected to be
- * added to some absolute time value and then normalized by
- * mktime() The return value is either TIME_OK (aka NULL) or
- * the pointer to the error message in the case of problems
- */
-char     *parsetime(
-    const char *tspec,
-    struct rrd_time_value *ptv)
-{
-    time_t    now = time(NULL);
-    int       hr = 0;
-
-    /* this MUST be initialized to zero for midnight/noon/teatime */
-
-    Specials = VariousWords;    /* initialize special words context */
-
-    try(init_scanner(1, &tspec));
-
-    /* establish the default time reference */
-    ptv->type = ABSOLUTE_TIME;
-    ptv->offset = 0;
-    ptv->tm = *localtime(&now);
-    ptv->tm.  tm_isdst = -1;    /* mk time can figure dst by default ... */
-
-    token();
-    switch (sc_tokid) {
-    case PLUS:
-    case MINUS:
-        break;          /* jump to OFFSET-SPEC part */
-
-    case START:
-        ptv->type = RELATIVE_TO_START_TIME;
-        goto KeepItRelative;
-    case END:
-        ptv->type = RELATIVE_TO_END_TIME;
-      KeepItRelative:
-        ptv->tm.  tm_sec = 0;
-        ptv->tm.  tm_min = 0;
-        ptv->tm.  tm_hour = 0;
-        ptv->tm.  tm_mday = 0;
-        ptv->tm.  tm_mon = 0;
-        ptv->tm.  tm_year = 0;
-
-        /* FALLTHRU */
-    case NOW:
-    {
-        int       time_reference = sc_tokid;
-
-        token();
-        if (sc_tokid == PLUS || sc_tokid == MINUS)
-            break;
-        if (time_reference != NOW) {
-            panic(e("'start' or 'end' MUST be followed by +|- offset"));
-        } else if (sc_tokid != EOF) {
-            panic(e("if 'now' is followed by a token it must be +|- offset"));
-        }
-    };
-        break;
-
-        /* Only absolute time specifications below */
-    case NUMBER:
-    {
-        long      hour_sv = ptv->tm.tm_hour;
-        long      year_sv = ptv->tm.tm_year;
-
-        ptv->tm.  tm_hour = 30;
-        ptv->tm.  tm_year = 30000;
-
-        try(tod(ptv))
-            try(day(ptv))
-            if (ptv->tm.tm_hour == 30 && ptv->tm.tm_year != 30000) {
-            try(tod(ptv))
-        }
-        if (ptv->tm.tm_hour == 30) {
-            ptv->tm.  tm_hour = hour_sv;
-        }
-        if (ptv->tm.tm_year == 30000) {
-            ptv->tm.  tm_year = year_sv;
-        }
-    };
-        break;
-        /* fix month parsing */
-    case JAN:
-    case FEB:
-    case MAR:
-    case APR:
-    case MAY:
-    case JUN:
-    case JUL:
-    case AUG:
-    case SEP:
-    case OCT:
-    case NOV:
-    case DEC:
-        try(day(ptv));
-        if (sc_tokid != NUMBER)
-            break;
-        try(tod(ptv))
-            break;
-
-        /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialized
-         * hr to zero up above, then fall into this case in such a
-         * way so we add +12 +4 hours to it for teatime, +12 hours
-         * to it for noon, and nothing at all for midnight, then
-         * set our rettime to that hour before leaping into the
-         * month scanner
-         */
-    case TEATIME:
-        hr += 4;
-        /* FALLTHRU */
-    case NOON:
-        hr += 12;
-        /* FALLTHRU */
-    case MIDNIGHT:
-        /* if (ptv->tm.tm_hour >= hr) {
-           ptv->tm.tm_mday++;
-           ptv->tm.tm_wday++;
-           } *//* shifting does not makes sense here ... noon is noon */
-        ptv->tm.  tm_hour = hr;
-        ptv->tm.  tm_min = 0;
-        ptv->tm.  tm_sec = 0;
-
-        token();
-        try(day(ptv));
-        break;
-    default:
-        panic(e("unparsable time: %s%s", sc_token, sct));
-        break;
-    }                   /* ugly case statement */
-
-    /*
-     * the OFFSET-SPEC part
-     *
-     * (NOTE, the sc_tokid was prefetched for us by the previous code)
-     */
-    if (sc_tokid == PLUS || sc_tokid == MINUS) {
-        Specials = TimeMultipliers; /* switch special words context */
-        while (sc_tokid == PLUS || sc_tokid == MINUS || sc_tokid == NUMBER) {
-            if (sc_tokid == NUMBER) {
-                try(plus_minus(ptv, PREVIOUS_OP));
-            } else
-                try(plus_minus(ptv, sc_tokid));
-            token();    /* We will get EOF eventually but that's OK, since
-                           token() will return us as many EOFs as needed */
-        }
-    }
-
-    /* now we should be at EOF */
-    if (sc_tokid != EOF) {
-        panic(e("unparsable trailing text: '...%s%s'", sc_token, sct));
-    }
-
-    if (ptv->type == ABSOLUTE_TIME)
-        if (mktime(&ptv->tm) == -1) {   /* normalize & check */
-            /* can happen for "nonexistent" times, e.g. around 3am */
-            /* when winter -> summer time correction eats a hour */
-            panic(e("the specified time is incorrect (out of range?)"));
-        }
-    EnsureMemFree();
-    return TIME_OK;
-}                       /* parsetime */
-
-
-int proc_start_end(
-    struct rrd_time_value *start_tv,
-    struct rrd_time_value *end_tv,
-    time_t *start,
-    time_t *end)
-{
-    if (start_tv->type == RELATIVE_TO_END_TIME &&   /* same as the line above */
-        end_tv->type == RELATIVE_TO_START_TIME) {
-        rrd_set_error("the start and end times cannot be specified "
-                      "relative to each other");
-        return -1;
-    }
-
-    if (start_tv->type == RELATIVE_TO_START_TIME) {
-        rrd_set_error
-            ("the start time cannot be specified relative to itself");
-        return -1;
-    }
-
-    if (end_tv->type == RELATIVE_TO_END_TIME) {
-        rrd_set_error("the end time cannot be specified relative to itself");
-        return -1;
-    }
-
-    if (start_tv->type == RELATIVE_TO_END_TIME) {
-        struct tm tmtmp;
-
-        *end = mktime(&(end_tv->tm)) + end_tv->offset;
-        tmtmp = *localtime(end);    /* reinit end including offset */
-        tmtmp.tm_mday += start_tv->tm.tm_mday;
-        tmtmp.tm_mon += start_tv->tm.tm_mon;
-        tmtmp.tm_year += start_tv->tm.tm_year;
-
-        *start = mktime(&tmtmp) + start_tv->offset;
-    } else {
-        *start = mktime(&(start_tv->tm)) + start_tv->offset;
-    }
-    if (end_tv->type == RELATIVE_TO_START_TIME) {
-        struct tm tmtmp;
-
-        *start = mktime(&(start_tv->tm)) + start_tv->offset;
-        tmtmp = *localtime(start);
-        tmtmp.tm_mday += end_tv->tm.tm_mday;
-        tmtmp.tm_mon += end_tv->tm.tm_mon;
-        tmtmp.tm_year += end_tv->tm.tm_year;
-
-        *end = mktime(&tmtmp) + end_tv->offset;
-    } else {
-        *end = mktime(&(end_tv->tm)) + end_tv->offset;
-    }
-    return 0;
-}                       /* proc_start_end */
diff --git a/program/src/parsetime.h b/program/src/parsetime.h
deleted file mode 100644 (file)
index d9a34e8..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef __PARSETIME_H__
-#define __PARSETIME_H__
-
-#include <stdio.h>
-
-#include "rrd.h"
-
-#endif
index 376d84f1c4a0895909b73b5fd14a5ad6b6e58650..3d62ed9513847ce84a9da44200872f46e352abcb 100644 (file)
@@ -60,15 +60,15 @@ extern    "C" {
 
 /* Formerly rrd_nan_inf.h */
 #ifndef DNAN
-# define DNAN set_to_DNAN()
+# define DNAN rrd_set_to_DNAN()
 #endif
 
 #ifndef DINF
-# define DINF set_to_DINF()
+# define DINF rrd_set_to_DINF()
 #endif
-    double    set_to_DNAN(
+    double    rrd_set_to_DNAN(
     void);
-    double    set_to_DINF(
+    double    rrd_set_to_DINF(
     void);
 /* end of rrd_nan_inf.h */
 
@@ -92,36 +92,51 @@ extern    "C" {
         unsigned char *ptr; /* pointer */
     } rrd_blob_t;
 
-    enum info_type { RD_I_VAL = 0,
+    typedef enum rrd_info_type { RD_I_VAL = 0,
         RD_I_CNT,
         RD_I_STR,
         RD_I_INT,
         RD_I_BLO
-    };
+    } rrd_info_type_t;
 
-    typedef union infoval {
+    typedef union rrd_infoval {
         unsigned long u_cnt;
         rrd_value_t u_val;
         char     *u_str;
         int       u_int;
-        struct rrd_blob_t u_blo;
-    } infoval;
+        rrd_blob_t u_blo;
+    } rrd_infoval_t;
 
-    typedef struct info_t {
+    typedef struct rrd_info_t {
         char     *key;
-        enum info_type type;
-        union infoval value;
-        struct info_t *next;
-    } info_t;
+        rrd_info_type_t type;
+        rrd_infoval_t value;
+        struct rrd_info_t *next;
+    } rrd_info_t;
 
 
 /* main function blocks */
     int       rrd_create(
     int,
     char **);
+    rrd_info_t *rrd_info(
+    int,
+    char **);
+    rrd_info_t *rrd_info_push(
+    rrd_info_t *,
+    char *,
+    rrd_info_type_t,
+    rrd_infoval_t);
+    void      rrd_info_print(
+    rrd_info_t *data);
+    void      rrd_info_free(
+    rrd_info_t *);
     int       rrd_update(
     int,
     char **);
+    rrd_info_t *rrd_update_v(
+    int,
+    char **);
     int       rrd_graph(
     int,
     char **,
@@ -131,7 +146,7 @@ extern    "C" {
     FILE *,
     double *,
     double *);
-    info_t   *rrd_graph_v(
+    rrd_info_t *rrd_graph_v(
     int,
     char **);
 
@@ -156,6 +171,13 @@ extern    "C" {
     time_t    rrd_last(
     int,
     char **);
+    int       rrd_lastupdate(
+    int argc,
+    char **argv,
+    time_t *last_update,
+    unsigned long *ds_cnt,
+    char ***ds_namv,
+    char ***last_ds);
     time_t    rrd_first(
     int,
     char **);
@@ -210,39 +232,39 @@ extern    "C" {
     const char *filename,
     int rraindex);
 
-/* Transplanted from parsetime.h */
+/* Transplanted from rrd_parsetime.h */
     typedef enum {
         ABSOLUTE_TIME,
         RELATIVE_TO_START_TIME,
         RELATIVE_TO_END_TIME
-    } timetype;
+    } rrd_timetype_t;
 
 #define TIME_OK NULL
 
-    struct rrd_time_value {
-        timetype  type;
+    typedef struct rrd_time_value {
+        rrd_timetype_t type;
         long      offset;
         struct tm tm;
-    };
+    } rrd_time_value_t;
 
-    char     *parsetime(
+    char     *rrd_parsetime(
     const char *spec,
-    struct rrd_time_value *ptv);
-/* END parsetime.h */
+    rrd_time_value_t *ptv);
+/* END rrd_parsetime.h */
 
-    struct rrd_context {
+    typedef struct rrd_context {
         char      lib_errstr[256];
         char      rrd_error[4096];
-    };
+    } rrd_context_t;
 
 /* returns the current per-thread rrd_context */
-    struct rrd_context *rrd_get_context(
+    rrd_context_t *rrd_get_context(
     void);
 
 
-    int       proc_start_end(
-    struct rrd_time_value *,
-    struct rrd_time_value *,
+    int       rrd_proc_start_end(
+    rrd_time_value_t *,
+    rrd_time_value_t *,
     time_t *,
     time_t *);
 
@@ -257,19 +279,22 @@ extern    "C" {
     char     *rrd_get_error(
     void);
 
+    /* rrd_strerror is thread safe, but still it uses a global buffer
+       (but one per thread), thus subsequent calls within a single
+       thread overwrite the same buffer */
+    const char *rrd_strerror(
+    int err);
+
 /** MULTITHREADED HELPER FUNCTIONS */
-    struct rrd_context *rrd_new_context(
+    rrd_context_t *rrd_new_context(
     void);
     void      rrd_free_context(
-    struct rrd_context *buf);
-
-/* void   rrd_set_error_r  (struct rrd_context *, char *, ...); */
-/* void   rrd_clear_error_r(struct rrd_context *); */
-/* int    rrd_test_error_r (struct rrd_context *); */
-/* char  *rrd_get_error_r  (struct rrd_context *); */
+    rrd_context_t *buf);
 
-    int       LockRRD(
-    int in_file);
+/* void   rrd_set_error_r  (rrd_context_t *, char *, ...); */
+/* void   rrd_clear_error_r(rrd_context_t *); */
+/* int    rrd_test_error_r (rrd_context_t *); */
+/* char  *rrd_get_error_r  (rrd_context_t *); */
 
 #endif                  /* _RRDLIB_H */
 
index d3818db685f6ef8d59c48a432db7a9b92661bbaa..f17dfe1f5b751f0ebac45223db7f57c02ccc35fa 100644 (file)
@@ -391,6 +391,63 @@ char     *stralloc(
     return (nstr);
 }
 
+static int readfile(
+    const char *file_name,
+    char **buffer,
+    int skipfirst)
+{
+    long      writecnt = 0, totalcnt = MEMBLK;
+    long      offset = 0;
+    FILE     *input = NULL;
+    char      c;
+
+    if ((strcmp("-", file_name) == 0)) {
+        input = stdin;
+    } else {
+        if ((input = fopen(file_name, "rb")) == NULL) {
+            rrd_set_error("opening '%s': %s", file_name, rrd_strerror(errno));
+            return (-1);
+        }
+    }
+    if (skipfirst) {
+        do {
+            c = getc(input);
+            offset++;
+        } while (c != '\n' && !feof(input));
+    }
+    if (strcmp("-", file_name)) {
+        fseek(input, 0, SEEK_END);
+        /* have extra space for detecting EOF without realloc */
+        totalcnt = (ftell(input) + 1) / sizeof(char) - offset;
+        if (totalcnt < MEMBLK)
+            totalcnt = MEMBLK;  /* sanitize */
+        fseek(input, offset * sizeof(char), SEEK_SET);
+    }
+    if (((*buffer) = (char *) malloc((totalcnt + 4) * sizeof(char))) == NULL) {
+        perror("Allocate Buffer:");
+        exit(1);
+    };
+    do {
+        writecnt +=
+            fread((*buffer) + writecnt, 1,
+                  (totalcnt - writecnt) * sizeof(char), input);
+        if (writecnt >= totalcnt) {
+            totalcnt += MEMBLK;
+            if (((*buffer) =
+                 rrd_realloc((*buffer),
+                             (totalcnt + 4) * sizeof(char))) == NULL) {
+                perror("Realloc Buffer:");
+                exit(1);
+            };
+        }
+    } while (!feof(input));
+    (*buffer)[writecnt] = '\0';
+    if (strcmp("-", file_name) != 0) {
+        fclose(input);
+    };
+    return writecnt;
+}
+
 int main(
     int argc,
     char *argv[])
@@ -666,7 +723,7 @@ char     *printstrftime(
     long argc,
     const char **args)
 {
-    struct rrd_time_value start_tv, end_tv;
+    rrd_time_value_t start_tv, end_tv;
     char     *parsetime_error = NULL;
     char      formatted[MAX_STRFTIME_SIZE];
     struct tm *the_tm;
@@ -679,19 +736,19 @@ char     *printstrftime(
     }
 
     /* Init start and end time */
-    parsetime("end-24h", &start_tv);
-    parsetime("now", &end_tv);
+    rrd_parsetime("end-24h", &start_tv);
+    rrd_parsetime("now", &end_tv);
 
     /* Parse the start and end times we were given */
-    if ((parsetime_error = parsetime(args[1], &start_tv))) {
+    if ((parsetime_error = rrd_parsetime(args[1], &start_tv))) {
         rrd_set_error("start time: %s", parsetime_error);
         return stralloc("");
     }
-    if ((parsetime_error = parsetime(args[2], &end_tv))) {
+    if ((parsetime_error = rrd_parsetime(args[2], &end_tv))) {
         rrd_set_error("end time: %s", parsetime_error);
         return stralloc("");
     }
-    if (proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
+    if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
         return stralloc("");
     }
 
index 32882f2112d65a82a1eeab4eb0194be822028d5c..960a7a6230d65e8574399bab799d068aea3c1006 100644 (file)
@@ -40,7 +40,7 @@ int rrd_create(
     int       opt;
     time_t    last_up = time(NULL) - 10;
     unsigned long pdp_step = 300;
-    struct rrd_time_value last_up_tv;
+    rrd_time_value_t last_up_tv;
     char     *parsetime_error = NULL;
     long      long_tmp;
     int       rc;
@@ -56,7 +56,7 @@ int rrd_create(
 
         switch (opt) {
         case 'b':
-            if ((parsetime_error = parsetime(optarg, &last_up_tv))) {
+            if ((parsetime_error = rrd_parsetime(optarg, &last_up_tv))) {
                 rrd_set_error("start time: %s", parsetime_error);
                 return (-1);
             }
index e368ca755f86198426e83e50e626f8ad5489f2da..714289f999f192df6e097490174ad5c31c56d34e 100644 (file)
@@ -78,7 +78,7 @@ char     *rrd_get_error(
    context. Using these functions would require to change each and
    every function containing any of the non _r versions... */
 void rrd_set_error_r(
-    struct rrd_context *rrd_ctx,
+    rrd_context_t *rrd_ctx,
     char *fmt,
     ...)
 {
@@ -96,19 +96,19 @@ void rrd_set_error_r(
 }
 
 int rrd_test_error_r(
-    struct rrd_context *rrd_ctx)
+    rrd_context_t *rrd_ctx)
 {
     return rrd_ctx->rrd_error[0] != '\0';
 }
 
 void rrd_clear_error_r(
-    struct rrd_context *rrd_ctx)
+    rrd_context_t *rrd_ctx)
 {
     rrd_ctx->rrd_error[0] = '\0';
 }
 
 char     *rrd_get_error_r(
-    struct rrd_context *rrd_ctx)
+    rrd_context_t *rrd_ctx)
 {
     return rrd_ctx->rrd_error;
 }
@@ -116,11 +116,11 @@ char     *rrd_get_error_r(
 
 /* PS: Should we move this to some other file? It is not really error
    related. */
-struct rrd_context *rrd_new_context(
+rrd_context_t *rrd_new_context(
     void)
 {
-    struct rrd_context *rrd_ctx =
-        (struct rrd_context *) malloc(sizeof(struct rrd_context));
+    rrd_context_t *rrd_ctx =
+        (rrd_context_t *) malloc(sizeof(rrd_context_t));
 
     if (!rrd_ctx) {
         return NULL;
@@ -132,7 +132,7 @@ struct rrd_context *rrd_new_context(
 }
 
 void rrd_free_context(
-    struct rrd_context *rrd_ctx)
+    rrd_context_t *rrd_ctx)
 {
     if (rrd_ctx) {
         free(rrd_ctx);
@@ -141,7 +141,7 @@ void rrd_free_context(
 
 #if 0
 void rrd_globalize_error(
-    struct rrd_context *rrd_ctx)
+    rrd_context_t *rrd_ctx)
 {
     if (rrd_ctx) {
         rrd_set_error(rrd_ctx->rrd_error);
index 1317483b110adf49478d71b509b6b1c446ef4440..76ea2633178c25cc4f32737ecffbff68805f06fc 100644 (file)
@@ -73,7 +73,7 @@ int rrd_fetch(
     time_t    start_tmp = 0, end_tmp = 0;
     const char *cf;
 
-    struct rrd_time_value start_tv, end_tv;
+    rrd_time_value_t start_tv, end_tv;
     char     *parsetime_error = NULL;
     struct option long_options[] = {
         {"resolution", required_argument, 0, 'r'},
@@ -86,8 +86,8 @@ int rrd_fetch(
     opterr = 0;         /* initialize getopt */
 
     /* init start and end time */
-    parsetime("end-24h", &start_tv);
-    parsetime("now", &end_tv);
+    rrd_parsetime("end-24h", &start_tv);
+    rrd_parsetime("now", &end_tv);
 
     while (1) {
         int       option_index = 0;
@@ -100,13 +100,13 @@ int rrd_fetch(
 
         switch (opt) {
         case 's':
-            if ((parsetime_error = parsetime(optarg, &start_tv))) {
+            if ((parsetime_error = rrd_parsetime(optarg, &start_tv))) {
                 rrd_set_error("start time: %s", parsetime_error);
                 return -1;
             }
             break;
         case 'e':
-            if ((parsetime_error = parsetime(optarg, &end_tv))) {
+            if ((parsetime_error = rrd_parsetime(optarg, &end_tv))) {
                 rrd_set_error("end time: %s", parsetime_error);
                 return -1;
             }
@@ -121,7 +121,7 @@ int rrd_fetch(
     }
 
 
-    if (proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
+    if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
         return -1;
     }
 
index a503ca3c6fefa34e8086c68da824a695b6ac384b..dcd90595bdafefa265271889e25320c91fc0a6f2 100644 (file)
@@ -1520,7 +1520,7 @@ int print_calc(
             }
 
             if (im->gdes[i].gf == GF_PRINT) {
-                infoval   prline;
+                rrd_infoval_t prline;
 
                 if (im->gdes[i].strftm) {
                     prline.u_str = malloc((FMT_LEG_LEN + 2) * sizeof(char));
@@ -2947,7 +2947,7 @@ int graph_paint(
     int       lazy = lazy_check(im);
     double    areazero = 0.0;
     graph_desc_t *lastgdes = NULL;
-    infoval   info;
+    rrd_infoval_t info;
     PangoFontMap *font_map = pango_cairo_font_map_get_default();
 
     /* if we are lazy and there is nothing to PRINT ... quit now */
@@ -3529,8 +3529,8 @@ int rrd_graph(
     double *ymax)
 {
     int       prlines = 0;
-    info_t   *grinfo = NULL;
-    info_t   *walker;
+    rrd_info_t *grinfo = NULL;
+    rrd_info_t *walker;
 
     grinfo = rrd_graph_v(argc, argv);
     if (grinfo == NULL)
@@ -3584,7 +3584,7 @@ int rrd_graph(
         /* skip anything else */
         walker = walker->next;
     }
-    info_free(grinfo);
+    rrd_info_free(grinfo);
     return 0;
 }
 
@@ -3595,12 +3595,12 @@ int rrd_graph(
 ** - options parsing  now in rrd_graph_options()
 ** - script parsing   now in rrd_graph_script()
 */
-info_t   *rrd_graph_v(
+rrd_info_t *rrd_graph_v(
     int argc,
     char **argv)
 {
     image_desc_t im;
-    info_t   *grinfo;
+    rrd_info_t  *grinfo;
 
     rrd_graph_init(&im);
     /* a dummy surface so that we can measure text sizes for placements */
@@ -3608,13 +3608,13 @@ info_t   *rrd_graph_v(
     im.cr = cairo_create(im.surface);
     rrd_graph_options(argc, argv, &im);
     if (rrd_test_error()) {
-        info_free(im.grinfo);
+        rrd_info_free(im.grinfo);
         im_free(&im);
         return NULL;
     }
 
     if (optind >= argc) {
-        info_free(im.grinfo);
+        rrd_info_free(im.grinfo);
         im_free(&im);
         rrd_set_error("missing filename");
         return NULL;
@@ -3622,7 +3622,7 @@ info_t   *rrd_graph_v(
 
     if (strlen(argv[optind]) >= MAXPATH) {
         rrd_set_error("filename (including path) too long");
-        info_free(im.grinfo);
+        rrd_info_free(im.grinfo);
         im_free(&im);
         return NULL;
     }
@@ -3636,7 +3636,7 @@ info_t   *rrd_graph_v(
 
     rrd_graph_script(argc, argv, &im, 1);
     if (rrd_test_error()) {
-        info_free(im.grinfo);
+        rrd_info_free(im.grinfo);
         im_free(&im);
         return NULL;
     }
@@ -3644,7 +3644,7 @@ info_t   *rrd_graph_v(
     /* Everything is now read and the actual work can start */
 
     if (graph_paint(&im) == -1) {
-        info_free(im.grinfo);
+        rrd_info_free(im.grinfo);
         im_free(&im);
         return NULL;
     }
@@ -3655,7 +3655,7 @@ info_t   *rrd_graph_v(
      */
 
     if (im.imginfo) {
-        infoval   info;
+        rrd_infoval_t info;
 
         info.u_str =
             sprintf_alloc(im.imginfo,
@@ -3666,7 +3666,7 @@ info_t   *rrd_graph_v(
         free(info.u_str);
     }
     if (im.rendered_image) {
-        infoval   img;
+        rrd_infoval_t img;
 
         img.u_blo.size = im.rendered_image_size;
         img.u_blo.ptr = im.rendered_image;
@@ -3705,8 +3705,8 @@ void rrd_graph_init(
     im->grid_dash_off = 1;
     im->grid_dash_on = 1;
     im->gridfit = 1;
-    im->grinfo = (info_t *) NULL;
-    im->grinfo_current = (info_t *) NULL;
+    im->grinfo = (rrd_info_t *) NULL;
+    im->grinfo_current = (rrd_info_t *) NULL;
     im->imgformat = IF_PNG;
     im->imginfo = NULL;
     im->lazy = 0;
@@ -3795,7 +3795,7 @@ void rrd_graph_options(
     char      scan_gtm[12], scan_mtm[12], scan_ltm[12], col_nam[12];
     time_t    start_tmp = 0, end_tmp = 0;
     long      long_tmp;
-    struct rrd_time_value start_tv, end_tv;
+    rrd_time_value_t start_tv, end_tv;
     long unsigned int color;
     char     *old_locale = "";
 
@@ -3853,8 +3853,8 @@ void rrd_graph_options(
 
     optind = 0;
     opterr = 0;         /* initialize getopt */
-    parsetime("end-24h", &start_tv);
-    parsetime("now", &end_tv);
+    rrd_parsetime("end-24h", &start_tv);
+    rrd_parsetime("now", &end_tv);
     while (1) {
         int       option_index = 0;
         int       opt;
@@ -3927,13 +3927,13 @@ void rrd_graph_options(
             im->with_markup = 1;
             break;
         case 's':
-            if ((parsetime_error = parsetime(optarg, &start_tv))) {
+            if ((parsetime_error = rrd_parsetime(optarg, &start_tv))) {
                 rrd_set_error("start time: %s", parsetime_error);
                 return;
             }
             break;
         case 'e':
-            if ((parsetime_error = parsetime(optarg, &end_tv))) {
+            if ((parsetime_error = rrd_parsetime(optarg, &end_tv))) {
                 rrd_set_error("end time: %s", parsetime_error);
                 return;
             }
@@ -4220,8 +4220,8 @@ void rrd_graph_options(
         return;
     }
 
-    if (proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
-        /* error string is set in parsetime.c */
+    if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
+        /* error string is set in rrd_parsetime.c */
         return;
     }
 
@@ -4686,10 +4686,10 @@ int vdef_percent_compar(
 void grinfo_push(
     image_desc_t *im,
     char *key,
-    enum info_type type,
-    infoval value)
+    rrd_info_type_t type,
+    rrd_infoval_t value)
 {
-    im->grinfo_current = info_push(im->grinfo_current, key, type, value);
+    im->grinfo_current = rrd_info_push(im->grinfo_current, key, type, value);
     if (im->grinfo == NULL) {
         im->grinfo = im->grinfo_current;
     }
index 0642d4199212d7133610d4f6053d994736a64617..1b452848edb0e15663dd08c3285bab1200166fe4 100644 (file)
@@ -244,8 +244,8 @@ typedef struct image_desc_t {
     cairo_font_options_t *font_options; /* cairo font options */
     cairo_antialias_t graph_antialias;  /* antialiasing for the graph */
 
-    info_t   *grinfo;   /* root pointer to extra graph info */
-    info_t   *grinfo_current;   /* pointing to current entry */
+    rrd_info_t *grinfo;   /* root pointer to extra graph info */
+    rrd_info_t *grinfo_current;   /* pointing to current entry */
 } image_desc_t;
 
 /* Prototypes */
@@ -454,5 +454,5 @@ void      gfx_area_fit(
 void      grinfo_push(
     image_desc_t *im,
     char *key,
-    enum info_type type,
-    infoval value);
+    rrd_info_type_t type,
+    rrd_infoval_t value);
index 13d50418d2eca11a385ae8a3db108157dd7bc93b..e6dd4e09de11d296c3d17071002799f30fa7fb67 100644 (file)
@@ -889,7 +889,7 @@ int rrd_parse_def(
     int       i = 0;
     char      command[7];   /* step, start, end, reduce */
     char      tmpstr[256];
-    struct rrd_time_value start_tv, end_tv;
+    rrd_time_value_t start_tv, end_tv;
     time_t    start_tmp = 0, end_tmp = 0;
     char     *parsetime_error = NULL;
 
@@ -951,7 +951,7 @@ int rrd_parse_def(
         } else if (!strcmp("start", command)) {
             i = scan_for_col(&line[*eaten], 255, tmpstr);
             (*eaten) += i;
-            if ((parsetime_error = parsetime(tmpstr, &start_tv))) {
+            if ((parsetime_error = rrd_parsetime(tmpstr, &start_tv))) {
                 rrd_set_error("start time: %s", parsetime_error);
                 return 1;
             }
@@ -959,7 +959,7 @@ int rrd_parse_def(
         } else if (!strcmp("end", command)) {
             i = scan_for_col(&line[*eaten], 255, tmpstr);
             (*eaten) += i;
-            if ((parsetime_error = parsetime(tmpstr, &end_tv))) {
+            if ((parsetime_error = rrd_parsetime(tmpstr, &end_tv))) {
                 rrd_set_error("end time: %s", parsetime_error);
                 return 1;
             }
@@ -978,8 +978,8 @@ int rrd_parse_def(
         }
         (*eaten)++;
     }
-    if (proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
-        /* error string is set in parsetime.c */
+    if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
+        /* error string is set in rrd_parsetime.c */
         return 1;
     }
     if (start_tmp < 3600 * 24 * 365 * 10) {
index f53a752ee29e7755daae0caeb43b750f66f28462..6d1a75b283066264497cdba64da753e0516a6947 100644 (file)
@@ -9,10 +9,10 @@
 #include <stdarg.h>
 
 /* proto */
-info_t   *rrd_info(
+rrd_info_t *rrd_info(
     int,
     char **);
-info_t   *rrd_info_r(
+rrd_info_t *rrd_info_r(
     char *filename);
 
 /* allocate memory for string */
@@ -36,19 +36,19 @@ char     *sprintf_alloc(
     return str;
 }
 
-/* the function formerly known as push was renamed info_push because
- * it is now used outside the scope of this file */
-info_t
-         *info_push(
-    info_t *info,
+/* the function formerly known as push was renamed to info_push and later
+ * rrd_info_push because it is now used outside the scope of this file */
+rrd_info_t
+         *rrd_info_push(
+    rrd_info_t *info,
     char *key,
-    enum info_type type,
-    infoval value)
+    rrd_info_type_t type,
+    rrd_infoval_t value)
 {
-    info_t   *next;
+    rrd_info_t *next;
 
     next = malloc(sizeof(*next));
-    next->next = (info_t *) 0;
+    next->next = (rrd_info_t *) 0;
     if (info)
         info->next = next;
     next->type = type;
@@ -78,11 +78,11 @@ info_t
 }
 
 
-info_t   *rrd_info(
+rrd_info_t *rrd_info(
     int argc,
     char **argv)
 {
-    info_t   *info;
+    rrd_info_t *info;
 
     if (argc < 2) {
         rrd_set_error("please specify an rrd");
@@ -96,13 +96,13 @@ info_t   *rrd_info(
 
 
 
-info_t   *rrd_info_r(
+rrd_info_t *rrd_info_r(
     char *filename)
 {
     unsigned int i, ii = 0;
     rrd_t     rrd;
-    info_t   *data = NULL, *cd;
-    infoval   info;
+    rrd_info_t   *data = NULL, *cd;
+    rrd_infoval_t info;
     rrd_file_t *rrd_file;
     enum cf_en current_cf;
     enum dst_en current_ds;
@@ -112,22 +112,23 @@ info_t   *rrd_info_r(
         goto err_free;
 
     info.u_str = filename;
-    cd = info_push(NULL, sprintf_alloc("filename"), RD_I_STR, info);
+    cd = rrd_info_push(NULL, sprintf_alloc("filename"), RD_I_STR, info);
     data = cd;
 
     info.u_str = rrd.stat_head->version;
-    cd = info_push(cd, sprintf_alloc("rrd_version"), RD_I_STR, info);
+    cd = rrd_info_push(cd, sprintf_alloc("rrd_version"), RD_I_STR, info);
 
     info.u_cnt = rrd.stat_head->pdp_step;
-    cd = info_push(cd, sprintf_alloc("step"), RD_I_CNT, info);
+    cd = rrd_info_push(cd, sprintf_alloc("step"), RD_I_CNT, info);
 
     info.u_cnt = rrd.live_head->last_up;
-    cd = info_push(cd, sprintf_alloc("last_update"), RD_I_CNT, info);
+    cd = rrd_info_push(cd, sprintf_alloc("last_update"), RD_I_CNT, info);
 
     for (i = 0; i < rrd.stat_head->ds_cnt; i++) {
 
         info.u_str = rrd.ds_def[i].dst;
-        cd = info_push(cd, sprintf_alloc("ds[%s].type", rrd.ds_def[i].ds_nam),
+        cd = rrd_info_push(cd, sprintf_alloc("ds[%s].type",
+                       rrd.ds_def[i].ds_nam),
                        RD_I_STR, info);
 
         current_ds = dst_conv(rrd.ds_def[i].dst);
@@ -139,7 +140,7 @@ info_t   *rrd_info_r(
             rpn_compact2str((rpn_cdefds_t *) &(rrd.ds_def[i].par[DS_cdef]),
                             rrd.ds_def, &buffer);
             info.u_str = buffer;
-            cd = info_push(cd,
+            cd = rrd_info_push(cd,
                            sprintf_alloc("ds[%s].cdef", rrd.ds_def[i].ds_nam),
                            RD_I_STR, info);
             free(buffer);
@@ -147,97 +148,97 @@ info_t   *rrd_info_r(
             break;
         default:
             info.u_cnt = rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt;
-            cd = info_push(cd,
+            cd = rrd_info_push(cd,
                            sprintf_alloc("ds[%s].minimal_heartbeat",
                                          rrd.ds_def[i].ds_nam), RD_I_CNT,
                            info);
 
             info.u_val = rrd.ds_def[i].par[DS_min_val].u_val;
-            cd = info_push(cd,
+            cd = rrd_info_push(cd,
                            sprintf_alloc("ds[%s].min", rrd.ds_def[i].ds_nam),
                            RD_I_VAL, info);
 
             info.u_val = rrd.ds_def[i].par[DS_max_val].u_val;
-            cd = info_push(cd,
+            cd = rrd_info_push(cd,
                            sprintf_alloc("ds[%s].max", rrd.ds_def[i].ds_nam),
                            RD_I_VAL, info);
             break;
         }
 
         info.u_str = rrd.pdp_prep[i].last_ds;
-        cd = info_push(cd,
+        cd = rrd_info_push(cd,
                        sprintf_alloc("ds[%s].last_ds", rrd.ds_def[i].ds_nam),
                        RD_I_STR, info);
 
         info.u_val = rrd.pdp_prep[i].scratch[PDP_val].u_val;
-        cd = info_push(cd,
+        cd = rrd_info_push(cd,
                        sprintf_alloc("ds[%s].value", rrd.ds_def[i].ds_nam),
                        RD_I_VAL, info);
 
         info.u_cnt = rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt;
-        cd = info_push(cd,
+        cd = rrd_info_push(cd,
                        sprintf_alloc("ds[%s].unknown_sec",
                                      rrd.ds_def[i].ds_nam), RD_I_CNT, info);
     }
 
     for (i = 0; i < rrd.stat_head->rra_cnt; i++) {
         info.u_str = rrd.rra_def[i].cf_nam;
-        cd = info_push(cd, sprintf_alloc("rra[%d].cf", i), RD_I_STR, info);
+        cd = rrd_info_push(cd, sprintf_alloc("rra[%d].cf", i), RD_I_STR, info);
         current_cf = cf_conv(rrd.rra_def[i].cf_nam);
 
         info.u_cnt = rrd.rra_def[i].row_cnt;
-        cd = info_push(cd, sprintf_alloc("rra[%d].rows", i), RD_I_CNT, info);
+        cd = rrd_info_push(cd, sprintf_alloc("rra[%d].rows", i), RD_I_CNT, info);
 
         info.u_cnt = rrd.rra_ptr[i].cur_row;
-        cd = info_push(cd, sprintf_alloc("rra[%d].cur_row", i), RD_I_CNT,
+        cd = rrd_info_push(cd, sprintf_alloc("rra[%d].cur_row", i), RD_I_CNT,
                        info);
 
         info.u_cnt = rrd.rra_def[i].pdp_cnt;
-        cd = info_push(cd, sprintf_alloc("rra[%d].pdp_per_row", i), RD_I_CNT,
+        cd = rrd_info_push(cd, sprintf_alloc("rra[%d].pdp_per_row", i), RD_I_CNT,
                        info);
 
         switch (current_cf) {
         case CF_HWPREDICT:
         case CF_MHWPREDICT:
             info.u_val = rrd.rra_def[i].par[RRA_hw_alpha].u_val;
-            cd = info_push(cd, sprintf_alloc("rra[%d].alpha", i), RD_I_VAL,
+            cd = rrd_info_push(cd, sprintf_alloc("rra[%d].alpha", i), RD_I_VAL,
                            info);
             info.u_val = rrd.rra_def[i].par[RRA_hw_beta].u_val;
-            cd = info_push(cd, sprintf_alloc("rra[%d].beta", i), RD_I_VAL,
+            cd = rrd_info_push(cd, sprintf_alloc("rra[%d].beta", i), RD_I_VAL,
                            info);
             break;
         case CF_SEASONAL:
         case CF_DEVSEASONAL:
             info.u_val = rrd.rra_def[i].par[RRA_seasonal_gamma].u_val;
-            cd = info_push(cd, sprintf_alloc("rra[%d].gamma", i), RD_I_VAL,
+            cd = rrd_info_push(cd, sprintf_alloc("rra[%d].gamma", i), RD_I_VAL,
                            info);
             if (atoi(rrd.stat_head->version) >= 4) {
                 info.u_val =
                     rrd.rra_def[i].par[RRA_seasonal_smoothing_window].u_val;
-                cd = info_push(cd,
+                cd = rrd_info_push(cd,
                                sprintf_alloc("rra[%d].smoothing_window", i),
                                RD_I_VAL, info);
             }
             break;
         case CF_FAILURES:
             info.u_val = rrd.rra_def[i].par[RRA_delta_pos].u_val;
-            cd = info_push(cd, sprintf_alloc("rra[%d].delta_pos", i),
+            cd = rrd_info_push(cd, sprintf_alloc("rra[%d].delta_pos", i),
                            RD_I_VAL, info);
             info.u_val = rrd.rra_def[i].par[RRA_delta_neg].u_val;
-            cd = info_push(cd, sprintf_alloc("rra[%d].delta_neg", i),
+            cd = rrd_info_push(cd, sprintf_alloc("rra[%d].delta_neg", i),
                            RD_I_VAL, info);
             info.u_cnt = rrd.rra_def[i].par[RRA_failure_threshold].u_cnt;
-            cd = info_push(cd, sprintf_alloc("rra[%d].failure_threshold", i),
+            cd = rrd_info_push(cd, sprintf_alloc("rra[%d].failure_threshold", i),
                            RD_I_CNT, info);
             info.u_cnt = rrd.rra_def[i].par[RRA_window_len].u_cnt;
-            cd = info_push(cd, sprintf_alloc("rra[%d].window_length", i),
+            cd = rrd_info_push(cd, sprintf_alloc("rra[%d].window_length", i),
                            RD_I_CNT, info);
             break;
         case CF_DEVPREDICT:
             break;
         default:
             info.u_val = rrd.rra_def[i].par[RRA_cdp_xff_val].u_val;
-            cd = info_push(cd, sprintf_alloc("rra[%d].xff", i), RD_I_VAL,
+            cd = rrd_info_push(cd, sprintf_alloc("rra[%d].xff", i), RD_I_VAL,
                            info);
             break;
         }
@@ -249,19 +250,19 @@ info_t   *rrd_info_r(
                 info.u_val =
                     rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
                                  ii].scratch[CDP_hw_intercept].u_val;
-                cd = info_push(cd,
+                cd = rrd_info_push(cd,
                                sprintf_alloc("rra[%d].cdp_prep[%d].intercept",
                                              i, ii), RD_I_VAL, info);
                 info.u_val =
                     rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
                                  ii].scratch[CDP_hw_slope].u_val;
-                cd = info_push(cd,
+                cd = rrd_info_push(cd,
                                sprintf_alloc("rra[%d].cdp_prep[%d].slope", i,
                                              ii), RD_I_VAL, info);
                 info.u_cnt =
                     rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
                                  ii].scratch[CDP_null_count].u_cnt;
-                cd = info_push(cd,
+                cd = rrd_info_push(cd,
                                sprintf_alloc("rra[%d].cdp_prep[%d].NaN_count",
                                              i, ii), RD_I_CNT, info);
                 break;
@@ -269,7 +270,7 @@ info_t   *rrd_info_r(
                 info.u_val =
                     rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
                                  ii].scratch[CDP_hw_seasonal].u_val;
-                cd = info_push(cd,
+                cd = rrd_info_push(cd,
                                sprintf_alloc("rra[%d].cdp_prep[%d].seasonal",
                                              i, ii), RD_I_VAL, info);
                 break;
@@ -277,7 +278,7 @@ info_t   *rrd_info_r(
                 info.u_val =
                     rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
                                  ii].scratch[CDP_seasonal_deviation].u_val;
-                cd = info_push(cd,
+                cd = rrd_info_push(cd,
                                sprintf_alloc("rra[%d].cdp_prep[%d].deviation",
                                              i, ii), RD_I_VAL, info);
                 break;
@@ -296,7 +297,7 @@ info_t   *rrd_info_r(
                     history[j] = (violations_array[j] == 1) ? '1' : '0';
                 history[j] = '\0';
                 info.u_str = history;
-                cd = info_push(cd,
+                cd = rrd_info_push(cd,
                                sprintf_alloc("rra[%d].cdp_prep[%d].history",
                                              i, ii), RD_I_STR, info);
             }
@@ -305,13 +306,13 @@ info_t   *rrd_info_r(
                 info.u_val =
                     rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
                                  ii].scratch[CDP_val].u_val;
-                cd = info_push(cd,
+                cd = rrd_info_push(cd,
                                sprintf_alloc("rra[%d].cdp_prep[%d].value", i,
                                              ii), RD_I_VAL, info);
                 info.u_cnt =
                     rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
                                  ii].scratch[CDP_unkn_pdp_cnt].u_cnt;
-                cd = info_push(cd,
+                cd = rrd_info_push(cd,
                                sprintf_alloc
                                ("rra[%d].cdp_prep[%d].unknown_datapoints", i,
                                 ii), RD_I_CNT, info);
@@ -327,8 +328,8 @@ info_t   *rrd_info_r(
 }
 
 
-void info_print(
-    info_t *data)
+void rrd_info_print(
+    rrd_info_t *data)
 {
     while (data) {
         printf("%s = ", data->key);
@@ -358,10 +359,10 @@ void info_print(
     }
 }
 
-void info_free(
-    info_t *data)
+void rrd_info_free(
+    rrd_info_t *data)
 {
-    info_t   *save;
+    rrd_info_t *save;
 
     while (data) {
         save = data;
index 1787948e409ea570af345a33a3bc757fe12ddeea..2914d1a3454fb9dc926113e7477e8379ecf5f58c 100644 (file)
@@ -19,7 +19,7 @@ double    dinf;
 
 #endif
 
-double set_to_DNAN(
+double rrd_set_to_DNAN(
     void)
 {
     if (!done_nan) {
@@ -29,7 +29,7 @@ double set_to_DNAN(
     return dnan;
 }
 
-double set_to_DINF(
+double rrd_set_to_DINF(
     void)
 {
     if (!done_inf) {
index b05ee1af64832217c84e424cea790d23b555f90a..badb6c34c55c1d459b8714359ce07a5801740077 100644 (file)
 /* The global context is very useful in the transition period to even
    more thread-safe stuff, it can be used whereever we need a context
    and do not need to worry about concurrency. */
-static struct rrd_context global_ctx = {
+static rrd_context_t global_ctx = {
     "",
     ""
 };
 
 /* #include <stdarg.h> */
 
-struct rrd_context *rrd_get_context(
+rrd_context_t *rrd_get_context(
     void)
 {
     return &global_ctx;
index e92ae5d1b89f7abecf7b25ebd078f29cabe6ce97..d061685a3dac2ffc9f3a48487988856f1d77f6e5 100644 (file)
@@ -564,63 +564,3 @@ void rrd_freemem(
     free(mem);
 }
 
-
-/* XXX: FIXME: missing documentation.  */
-/*XXX: FIXME should be renamed to rrd_readfile or _rrd_readfile */
-
-int /*_rrd_*/ readfile(
-    const char *file_name,
-    char **buffer,
-    int skipfirst)
-{
-    long      writecnt = 0, totalcnt = MEMBLK;
-    long      offset = 0;
-    FILE     *input = NULL;
-    char      c;
-
-    if ((strcmp("-", file_name) == 0)) {
-        input = stdin;
-    } else {
-        if ((input = fopen(file_name, "rb")) == NULL) {
-            rrd_set_error("opening '%s': %s", file_name, rrd_strerror(errno));
-            return (-1);
-        }
-    }
-    if (skipfirst) {
-        do {
-            c = getc(input);
-            offset++;
-        } while (c != '\n' && !feof(input));
-    }
-    if (strcmp("-", file_name)) {
-        fseek(input, 0, SEEK_END);
-        /* have extra space for detecting EOF without realloc */
-        totalcnt = (ftell(input) + 1) / sizeof(char) - offset;
-        if (totalcnt < MEMBLK)
-            totalcnt = MEMBLK;  /* sanitize */
-        fseek(input, offset * sizeof(char), SEEK_SET);
-    }
-    if (((*buffer) = (char *) malloc((totalcnt + 4) * sizeof(char))) == NULL) {
-        perror("Allocate Buffer:");
-        exit(1);
-    };
-    do {
-        writecnt +=
-            fread((*buffer) + writecnt, 1,
-                  (totalcnt - writecnt) * sizeof(char), input);
-        if (writecnt >= totalcnt) {
-            totalcnt += MEMBLK;
-            if (((*buffer) =
-                 rrd_realloc((*buffer),
-                             (totalcnt + 4) * sizeof(char))) == NULL) {
-                perror("Realloc Buffer:");
-                exit(1);
-            };
-        }
-    } while (!feof(input));
-    (*buffer)[writecnt] = '\0';
-    if (strcmp("-", file_name) != 0) {
-        fclose(input);
-    };
-    return writecnt;
-}
diff --git a/program/src/rrd_parsetime.c b/program/src/rrd_parsetime.c
new file mode 100644 (file)
index 0000000..c1aef0b
--- /dev/null
@@ -0,0 +1,1043 @@
+/*  
+ *  rrd_parsetime.c - parse time for at(1)
+ *  Copyright (C) 1993, 1994  Thomas Koenig
+ *
+ *  modifications for English-language times
+ *  Copyright (C) 1993  David Parsons
+ *
+ *  A lot of modifications and extensions 
+ *  (including the new syntax being useful for RRDB)
+ *  Copyright (C) 1999  Oleg Cherevko (aka Olwi Deer)
+ *
+ *  severe structural damage inflicted by Tobi Oetiker in 1999
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author(s) may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* NOTE: nothing in here is thread-safe!!!! Not even the localtime
+   calls ... */
+
+/*
+ * The BNF-like specification of the time syntax parsed is below:
+ *                                                               
+ * As usual, [ X ] means that X is optional, { X } means that X may
+ * be either omitted or specified as many times as needed,
+ * alternatives are separated by |, brackets are used for grouping.
+ * (# marks the beginning of comment that extends to the end of line)
+ *
+ * TIME-SPECIFICATION ::= TIME-REFERENCE [ OFFSET-SPEC ] |
+ *                                        OFFSET-SPEC   |
+ *                        ( START | END ) OFFSET-SPEC 
+ *
+ * TIME-REFERENCE ::= NOW | TIME-OF-DAY-SPEC [ DAY-SPEC-1 ] |
+ *                        [ TIME-OF-DAY-SPEC ] DAY-SPEC-2
+ *
+ * TIME-OF-DAY-SPEC ::= NUMBER (':') NUMBER [am|pm] | # HH:MM
+ *                     'noon' | 'midnight' | 'teatime'
+ *
+ * DAY-SPEC-1 ::= NUMBER '/' NUMBER '/' NUMBER |  # MM/DD/[YY]YY
+ *                NUMBER '.' NUMBER '.' NUMBER |  # DD.MM.[YY]YY
+ *                NUMBER                          # Seconds since 1970
+ *                NUMBER                          # YYYYMMDD
+ *
+ * DAY-SPEC-2 ::= MONTH-NAME NUMBER [NUMBER] |    # Month DD [YY]YY
+ *                'yesterday' | 'today' | 'tomorrow' |
+ *                DAY-OF-WEEK
+ *
+ *
+ * OFFSET-SPEC ::= '+'|'-' NUMBER TIME-UNIT { ['+'|'-'] NUMBER TIME-UNIT }
+ *
+ * TIME-UNIT ::= SECONDS | MINUTES | HOURS |
+ *               DAYS | WEEKS | MONTHS | YEARS
+ *
+ * NOW ::= 'now' | 'n'
+ *
+ * START ::= 'start' | 's'
+ * END   ::= 'end' | 'e'
+ *
+ * SECONDS ::= 'seconds' | 'second' | 'sec' | 's'
+ * MINUTES ::= 'minutes' | 'minute' | 'min' | 'm'
+ * HOURS   ::= 'hours' | 'hour' | 'hr' | 'h'
+ * DAYS    ::= 'days' | 'day' | 'd'
+ * WEEKS   ::= 'weeks' | 'week' | 'wk' | 'w'
+ * MONTHS  ::= 'months' | 'month' | 'mon' | 'm'
+ * YEARS   ::= 'years' | 'year' | 'yr' | 'y'
+ *
+ * MONTH-NAME ::= 'jan' | 'january' | 'feb' | 'february' | 'mar' | 'march' |
+ *                'apr' | 'april' | 'may' | 'jun' | 'june' | 'jul' | 'july' |
+ *                'aug' | 'august' | 'sep' | 'september' | 'oct' | 'october' |
+ *               'nov' | 'november' | 'dec' | 'december'
+ *
+ * DAY-OF-WEEK ::= 'sunday' | 'sun' | 'monday' | 'mon' | 'tuesday' | 'tue' |
+ *                 'wednesday' | 'wed' | 'thursday' | 'thu' | 'friday' | 'fri' |
+ *                 'saturday' | 'sat'
+ *
+ *
+ * As you may note, there is an ambiguity with respect to
+ * the 'm' time unit (which can mean either minutes or months).
+ * To cope with this, code tries to read users mind :) by applying
+ * certain heuristics. There are two of them:
+ *
+ * 1. If 'm' is used in context of (i.e. right after the) years,
+ *    months, weeks, or days it is assumed to mean months, while
+ *    in the context of hours, minutes, and seconds it means minutes.
+ *    (e.g., in -1y6m or +3w1m 'm' means 'months', while in
+ *    -3h20m or +5s2m 'm' means 'minutes')
+ *
+ * 2. Out of context (i.e. right after the '+' or '-' sign) the
+ *    meaning of 'm' is guessed from the number it directly follows.
+ *    Currently, if the number absolute value is below 25 it is assumed
+ *    that 'm' means months, otherwise it is treated as minutes.
+ *    (e.g., -25m == -25 minutes, while +24m == +24 months)
+ *
+ */
+
+/* System Headers */
+
+/* Local headers */
+
+#include "rrd_tool.h"
+#include <stdarg.h>
+
+/* Structures and unions */
+
+enum {                  /* symbols */
+    MIDNIGHT, NOON, TEATIME,
+    PM, AM, YESTERDAY, TODAY, TOMORROW, NOW, START, END,
+    SECONDS, MINUTES, HOURS, DAYS, WEEKS, MONTHS, YEARS,
+    MONTHS_MINUTES,
+    NUMBER, PLUS, MINUS, DOT, COLON, SLASH, ID, JUNK,
+    JAN, FEB, MAR, APR, MAY, JUN,
+    JUL, AUG, SEP, OCT, NOV, DEC,
+    SUN, MON, TUE, WED, THU, FRI, SAT
+};
+
+/* the below is for plus_minus() */
+#define PREVIOUS_OP    (-1)
+
+/* parse translation table - table driven parsers can be your FRIEND!
+ */
+struct SpecialToken {
+    char     *name;     /* token name */
+    int       value;    /* token id */
+};
+static const struct SpecialToken VariousWords[] = {
+    {"midnight", MIDNIGHT}, /* 00:00:00 of today or tomorrow */
+    {"noon", NOON},     /* 12:00:00 of today or tomorrow */
+    {"teatime", TEATIME},   /* 16:00:00 of today or tomorrow */
+    {"am", AM},         /* morning times for 0-12 clock */
+    {"pm", PM},         /* evening times for 0-12 clock */
+    {"tomorrow", TOMORROW},
+    {"yesterday", YESTERDAY},
+    {"today", TODAY},
+    {"now", NOW},
+    {"n", NOW},
+    {"start", START},
+    {"s", START},
+    {"end", END},
+    {"e", END},
+
+    {"jan", JAN},
+    {"feb", FEB},
+    {"mar", MAR},
+    {"apr", APR},
+    {"may", MAY},
+    {"jun", JUN},
+    {"jul", JUL},
+    {"aug", AUG},
+    {"sep", SEP},
+    {"oct", OCT},
+    {"nov", NOV},
+    {"dec", DEC},
+    {"january", JAN},
+    {"february", FEB},
+    {"march", MAR},
+    {"april", APR},
+    {"may", MAY},
+    {"june", JUN},
+    {"july", JUL},
+    {"august", AUG},
+    {"september", SEP},
+    {"october", OCT},
+    {"november", NOV},
+    {"december", DEC},
+    {"sunday", SUN},
+    {"sun", SUN},
+    {"monday", MON},
+    {"mon", MON},
+    {"tuesday", TUE},
+    {"tue", TUE},
+    {"wednesday", WED},
+    {"wed", WED},
+    {"thursday", THU},
+    {"thu", THU},
+    {"friday", FRI},
+    {"fri", FRI},
+    {"saturday", SAT},
+    {"sat", SAT},
+    {NULL, 0}           /*** SENTINEL ***/
+};
+
+static const struct SpecialToken TimeMultipliers[] = {
+    {"second", SECONDS},    /* seconds multiplier */
+    {"seconds", SECONDS},   /* (pluralized) */
+    {"sec", SECONDS},   /* (generic) */
+    {"s", SECONDS},     /* (short generic) */
+    {"minute", MINUTES},    /* minutes multiplier */
+    {"minutes", MINUTES},   /* (pluralized) */
+    {"min", MINUTES},   /* (generic) */
+    {"m", MONTHS_MINUTES},  /* (short generic) */
+    {"hour", HOURS},    /* hours ... */
+    {"hours", HOURS},   /* (pluralized) */
+    {"hr", HOURS},      /* (generic) */
+    {"h", HOURS},       /* (short generic) */
+    {"day", DAYS},      /* days ... */
+    {"days", DAYS},     /* (pluralized) */
+    {"d", DAYS},        /* (short generic) */
+    {"week", WEEKS},    /* week ... */
+    {"weeks", WEEKS},   /* (pluralized) */
+    {"wk", WEEKS},      /* (generic) */
+    {"w", WEEKS},       /* (short generic) */
+    {"month", MONTHS},  /* week ... */
+    {"months", MONTHS}, /* (pluralized) */
+    {"mon", MONTHS},    /* (generic) */
+    {"year", YEARS},    /* year ... */
+    {"years", YEARS},   /* (pluralized) */
+    {"yr", YEARS},      /* (generic) */
+    {"y", YEARS},       /* (short generic) */
+    {NULL, 0}           /*** SENTINEL ***/
+};
+
+/* File scope variables */
+
+/* context dependent list of specials for parser to recognize,
+ * required for us to be able distinguish between 'mon' as 'month'
+ * and 'mon' as 'monday'
+ */
+static const struct SpecialToken *Specials;
+
+static const char **scp;    /* scanner - pointer at arglist */
+static char scc;        /* scanner - count of remaining arguments */
+static const char *sct; /* scanner - next char pointer in current argument */
+static int need;        /* scanner - need to advance to next argument */
+
+static char *sc_token = NULL;   /* scanner - token buffer */
+static size_t sc_len;   /* scanner - length of token buffer */
+static int sc_tokid;    /* scanner - token id */
+
+/* Local functions */
+static void EnsureMemFree(
+    void);
+
+static void EnsureMemFree(
+    void)
+{
+    if (sc_token) {
+        free(sc_token);
+        sc_token = NULL;
+    }
+}
+
+/*
+ * A hack to compensate for the lack of the C++ exceptions
+ *
+ * Every function func that might generate parsing "exception"
+ * should return TIME_OK (aka NULL) or pointer to the error message,
+ * and should be called like this: try(func(args));
+ *
+ * if the try is not successful it will reset the token pointer ...
+ *
+ * [NOTE: when try(...) is used as the only statement in the "if-true"
+ *  part of the if statement that also has an "else" part it should be
+ *  either enclosed in the curly braces (despite the fact that it looks
+ *  like a single statement) or NOT followed by the ";"]
+ */
+#define try(b)         { \
+                       char *_e; \
+                       if((_e=(b))) \
+                         { \
+                         EnsureMemFree(); \
+                         return _e; \
+                         } \
+                       }
+
+/*
+ * The panic() function was used in the original code to die, we redefine
+ * it as macro to start the chain of ascending returns that in conjunction
+ * with the try(b) above will simulate a sort of "exception handling"
+ */
+
+#define panic(e)       { \
+                       return (e); \
+                       }
+
+/*
+ * ve() and e() are used to set the return error,
+ * the most appropriate use for these is inside panic(...) 
+ */
+#define MAX_ERR_MSG_LEN        1024
+static char errmsg[MAX_ERR_MSG_LEN];
+
+static char *ve(
+    char *fmt,
+    va_list ap)
+{
+#ifdef HAVE_VSNPRINTF
+    vsnprintf(errmsg, MAX_ERR_MSG_LEN, fmt, ap);
+#else
+    vsprintf(errmsg, fmt, ap);
+#endif
+    EnsureMemFree();
+    return (errmsg);
+}
+
+static char *e(
+    char *fmt,
+    ...)
+{
+    char     *err;
+    va_list   ap;
+
+    va_start(ap, fmt);
+    err = ve(fmt, ap);
+    va_end(ap);
+    return (err);
+}
+
+/* Compare S1 and S2, ignoring case, returning less than, equal to or
+   greater than zero if S1 is lexicographically less than,
+   equal to or greater than S2.  -- copied from GNU libc*/
+static int mystrcasecmp(
+    const char *s1,
+    const char *s2)
+{
+    const unsigned char *p1 = (const unsigned char *) s1;
+    const unsigned char *p2 = (const unsigned char *) s2;
+    unsigned char c1, c2;
+
+    if (p1 == p2)
+        return 0;
+
+    do {
+        c1 = tolower(*p1++);
+        c2 = tolower(*p2++);
+        if (c1 == '\0')
+            break;
+    }
+    while (c1 == c2);
+
+    return c1 - c2;
+}
+
+/*
+ * parse a token, checking if it's something special to us
+ */
+static int parse_token(
+    char *arg)
+{
+    int       i;
+
+    for (i = 0; Specials[i].name != NULL; i++)
+        if (mystrcasecmp(Specials[i].name, arg) == 0)
+            return sc_tokid = Specials[i].value;
+
+    /* not special - must be some random id */
+    return sc_tokid = ID;
+}                       /* parse_token */
+
+
+
+/*
+ * init_scanner() sets up the scanner to eat arguments
+ */
+static char *init_scanner(
+    int argc,
+    const char **argv)
+{
+    scp = argv;
+    scc = argc;
+    need = 1;
+    sc_len = 1;
+    while (argc-- > 0)
+        sc_len += strlen(*argv++);
+
+    sc_token = (char *) malloc(sc_len * sizeof(char));
+    if (sc_token == NULL)
+        return "Failed to allocate memory";
+    return TIME_OK;
+}                       /* init_scanner */
+
+/*
+ * token() fetches a token from the input stream
+ */
+static int token(
+    void)
+{
+    int       idx;
+
+    while (1) {
+        memset(sc_token, '\0', sc_len);
+        sc_tokid = EOF;
+        idx = 0;
+
+        /* if we need to read another argument, walk along the argument list;
+         * when we fall off the arglist, we'll just return EOF forever
+         */
+        if (need) {
+            if (scc < 1)
+                return sc_tokid;
+            sct = *scp;
+            scp++;
+            scc--;
+            need = 0;
+        }
+        /* eat whitespace now - if we walk off the end of the argument,
+         * we'll continue, which puts us up at the top of the while loop
+         * to fetch the next argument in
+         */
+        while (isspace((unsigned char) *sct) || *sct == '_' || *sct == ',')
+            ++sct;
+        if (!*sct) {
+            need = 1;
+            continue;
+        }
+
+        /* preserve the first character of the new token
+         */
+        sc_token[0] = *sct++;
+
+        /* then see what it is
+         */
+        if (isdigit((unsigned char) (sc_token[0]))) {
+            while (isdigit((unsigned char) (*sct)))
+                sc_token[++idx] = *sct++;
+            sc_token[++idx] = '\0';
+            return sc_tokid = NUMBER;
+        } else if (isalpha((unsigned char) (sc_token[0]))) {
+            while (isalpha((unsigned char) (*sct)))
+                sc_token[++idx] = *sct++;
+            sc_token[++idx] = '\0';
+            return parse_token(sc_token);
+        } else
+            switch (sc_token[0]) {
+            case ':':
+                return sc_tokid = COLON;
+            case '.':
+                return sc_tokid = DOT;
+            case '+':
+                return sc_tokid = PLUS;
+            case '-':
+                return sc_tokid = MINUS;
+            case '/':
+                return sc_tokid = SLASH;
+            default:
+                /*OK, we did not make it ... */
+                sct--;
+                return sc_tokid = EOF;
+            }
+    }                   /* while (1) */
+}                       /* token */
+
+
+/* 
+ * expect2() gets a token and complains if it's not the token we want
+ */
+static char *expect2(
+    int desired,
+    char *complain_fmt,
+    ...)
+{
+    va_list   ap;
+
+    va_start(ap, complain_fmt);
+    if (token() != desired) {
+        panic(ve(complain_fmt, ap));
+    }
+    va_end(ap);
+    return TIME_OK;
+
+}                       /* expect2 */
+
+
+/*
+ * plus_minus() is used to parse a single NUMBER TIME-UNIT pair
+ *              for the OFFSET-SPEC.
+ *              It also applies those m-guessing heuristics.
+ */
+static char *plus_minus(
+    rrd_time_value_t *ptv,
+    int doop)
+{
+    static int op = PLUS;
+    static int prev_multiplier = -1;
+    int       delta;
+
+    if (doop >= 0) {
+        op = doop;
+        try(expect2
+            (NUMBER, "There should be number after '%c'",
+             op == PLUS ? '+' : '-'));
+        prev_multiplier = -1;   /* reset months-minutes guessing mechanics */
+    }
+    /* if doop is < 0 then we repeat the previous op
+     * with the prefetched number */
+
+    delta = atoi(sc_token);
+
+    if (token() == MONTHS_MINUTES) {
+        /* hard job to guess what does that -5m means: -5mon or -5min? */
+        switch (prev_multiplier) {
+        case DAYS:
+        case WEEKS:
+        case MONTHS:
+        case YEARS:
+            sc_tokid = MONTHS;
+            break;
+
+        case SECONDS:
+        case MINUTES:
+        case HOURS:
+            sc_tokid = MINUTES;
+            break;
+
+        default:
+            if (delta < 6)  /* it may be some other value but in the context
+                             * of RRD who needs less than 6 min deltas? */
+                sc_tokid = MONTHS;
+            else
+                sc_tokid = MINUTES;
+        }
+    }
+    prev_multiplier = sc_tokid;
+    switch (sc_tokid) {
+    case YEARS:
+        ptv->tm.  tm_year += (
+    op == PLUS) ? delta : -delta;
+
+        return TIME_OK;
+    case MONTHS:
+        ptv->tm.  tm_mon += (
+    op == PLUS) ? delta : -delta;
+
+        return TIME_OK;
+    case WEEKS:
+        delta *= 7;
+        /* FALLTHRU */
+    case DAYS:
+        ptv->tm.  tm_mday += (
+    op == PLUS) ? delta : -delta;
+
+        return TIME_OK;
+    case HOURS:
+        ptv->offset += (op == PLUS) ? delta * 60 * 60 : -delta * 60 * 60;
+        return TIME_OK;
+    case MINUTES:
+        ptv->offset += (op == PLUS) ? delta * 60 : -delta * 60;
+        return TIME_OK;
+    case SECONDS:
+        ptv->offset += (op == PLUS) ? delta : -delta;
+        return TIME_OK;
+    default:           /*default unit is seconds */
+        ptv->offset += (op == PLUS) ? delta : -delta;
+        return TIME_OK;
+    }
+    panic(e("well-known time unit expected after %d", delta));
+    /* NORETURN */
+    return TIME_OK;     /* to make compiler happy :) */
+}                       /* plus_minus */
+
+
+/*
+ * tod() computes the time of day (TIME-OF-DAY-SPEC)
+ */
+static char *tod(
+    rrd_time_value_t *ptv)
+{
+    int       hour, minute = 0;
+    int       tlen;
+
+    /* save token status in  case we must abort */
+    int       scc_sv = scc;
+    const char *sct_sv = sct;
+    int       sc_tokid_sv = sc_tokid;
+
+    tlen = strlen(sc_token);
+
+    /* first pick out the time of day - we assume a HH (COLON|DOT) MM time
+     */
+    if (tlen > 2) {
+        return TIME_OK;
+    }
+
+    hour = atoi(sc_token);
+
+    token();
+    if (sc_tokid == SLASH || sc_tokid == DOT) {
+        /* guess we are looking at a date */
+        scc = scc_sv;
+        sct = sct_sv;
+        sc_tokid = sc_tokid_sv;
+        sprintf(sc_token, "%d", hour);
+        return TIME_OK;
+    }
+    if (sc_tokid == COLON) {
+        try(expect2(NUMBER,
+                    "Parsing HH:MM syntax, expecting MM as number, got none"));
+        minute = atoi(sc_token);
+        if (minute > 59) {
+            panic(e("parsing HH:MM syntax, got MM = %d (>59!)", minute));
+        }
+        token();
+    }
+
+    /* check if an AM or PM specifier was given
+     */
+    if (sc_tokid == AM || sc_tokid == PM) {
+        if (hour > 12) {
+            panic(e("there cannot be more than 12 AM or PM hours"));
+        }
+        if (sc_tokid == PM) {
+            if (hour != 12) /* 12:xx PM is 12:xx, not 24:xx */
+                hour += 12;
+        } else {
+            if (hour == 12) /* 12:xx AM is 00:xx, not 12:xx */
+                hour = 0;
+        }
+        token();
+    } else if (hour > 23) {
+        /* guess it was not a time then ... */
+        scc = scc_sv;
+        sct = sct_sv;
+        sc_tokid = sc_tokid_sv;
+        sprintf(sc_token, "%d", hour);
+        return TIME_OK;
+    }
+    ptv->tm.  tm_hour = hour;
+    ptv->tm.  tm_min = minute;
+    ptv->tm.  tm_sec = 0;
+
+    if (ptv->tm.tm_hour == 24) {
+        ptv->tm.  tm_hour = 0;
+        ptv->tm.  tm_mday++;
+    }
+    return TIME_OK;
+}                       /* tod */
+
+
+/*
+ * assign_date() assigns a date, adjusting year as appropriate
+ */
+static char *assign_date(
+    rrd_time_value_t *ptv,
+    long mday,
+    long mon,
+    long year)
+{
+    if (year > 138) {
+        if (year > 1970)
+            year -= 1900;
+        else {
+            panic(e("invalid year %d (should be either 00-99 or >1900)",
+                    year));
+        }
+    } else if (year >= 0 && year < 38) {
+        year += 100;    /* Allow year 2000-2037 to be specified as   */
+    }
+    /* 00-37 until the problem of 2038 year will */
+    /* arise for unices with 32-bit time_t :)    */
+    if (year < 70) {
+        panic(e("won't handle dates before epoch (01/01/1970), sorry"));
+    }
+
+    ptv->tm.  tm_mday = mday;
+    ptv->tm.  tm_mon = mon;
+    ptv->tm.  tm_year = year;
+
+    return TIME_OK;
+}                       /* assign_date */
+
+
+/* 
+ * day() picks apart DAY-SPEC-[12]
+ */
+static char *day(
+    rrd_time_value_t *ptv)
+{
+    /* using time_t seems to help portability with 64bit oses */
+    time_t    mday = 0, wday, mon, year = ptv->tm.tm_year;
+    int       tlen;
+
+    switch (sc_tokid) {
+    case YESTERDAY:
+        ptv->tm.  tm_mday--;
+
+        /* FALLTRHU */
+    case TODAY:        /* force ourselves to stay in today - no further processing */
+        token();
+        break;
+    case TOMORROW:
+        ptv->tm.  tm_mday++;
+
+        token();
+        break;
+
+    case JAN:
+    case FEB:
+    case MAR:
+    case APR:
+    case MAY:
+    case JUN:
+    case JUL:
+    case AUG:
+    case SEP:
+    case OCT:
+    case NOV:
+    case DEC:
+        /* do month mday [year]
+         */
+        mon = (sc_tokid - JAN);
+        try(expect2(NUMBER, "the day of the month should follow month name"));
+        mday = atol(sc_token);
+        if (token() == NUMBER) {
+            year = atol(sc_token);
+            token();
+        } else
+            year = ptv->tm.tm_year;
+
+        try(assign_date(ptv, mday, mon, year));
+        break;
+
+    case SUN:
+    case MON:
+    case TUE:
+    case WED:
+    case THU:
+    case FRI:
+    case SAT:
+        /* do a particular day of the week
+         */
+        wday = (sc_tokid - SUN);
+        ptv->tm.  tm_mday += (
+    wday - ptv->tm.tm_wday);
+
+        token();
+        break;
+        /*
+           mday = ptv->tm.tm_mday;
+           mday += (wday - ptv->tm.tm_wday);
+           ptv->tm.tm_wday = wday;
+
+           try(assign_date(ptv, mday, ptv->tm.tm_mon, ptv->tm.tm_year));
+           break;
+         */
+
+    case NUMBER:
+        /* get numeric <sec since 1970>, MM/DD/[YY]YY, or DD.MM.[YY]YY
+         */
+        tlen = strlen(sc_token);
+        mon = atol(sc_token);
+        if (mon > 10 * 365 * 24 * 60 * 60) {
+            ptv->tm = *localtime(&mon);
+
+            token();
+            break;
+        }
+
+        if (mon > 19700101 && mon < 24000101) { /*works between 1900 and 2400 */
+            char      cmon[3], cmday[3], cyear[5];
+
+            strncpy(cyear, sc_token, 4);
+            cyear[4] = '\0';
+            year = atol(cyear);
+            strncpy(cmon, &(sc_token[4]), 2);
+            cmon[2] = '\0';
+            mon = atol(cmon);
+            strncpy(cmday, &(sc_token[6]), 2);
+            cmday[2] = '\0';
+            mday = atol(cmday);
+            token();
+        } else {
+            token();
+
+            if (mon <= 31 && (sc_tokid == SLASH || sc_tokid == DOT)) {
+                int       sep;
+
+                sep = sc_tokid;
+                try(expect2(NUMBER, "there should be %s number after '%c'",
+                            sep == DOT ? "month" : "day",
+                            sep == DOT ? '.' : '/'));
+                mday = atol(sc_token);
+                if (token() == sep) {
+                    try(expect2
+                        (NUMBER, "there should be year number after '%c'",
+                         sep == DOT ? '.' : '/'));
+                    year = atol(sc_token);
+                    token();
+                }
+
+                /* flip months and days for European timing
+                 */
+                if (sep == DOT) {
+                    long      x = mday;
+
+                    mday = mon;
+                    mon = x;
+                }
+            }
+        }
+
+        mon--;
+        if (mon < 0 || mon > 11) {
+            panic(e("did you really mean month %d?", mon + 1));
+        }
+        if (mday < 1 || mday > 31) {
+            panic(e("I'm afraid that %d is not a valid day of the month",
+                    mday));
+        }
+        try(assign_date(ptv, mday, mon, year));
+        break;
+    }                   /* case */
+    return TIME_OK;
+}                       /* month */
+
+
+/* Global functions */
+
+
+/*
+ * rrd_parsetime() is the external interface that takes tspec, parses
+ * it and puts the result in the rrd_time_value structure *ptv.
+ * It can return either absolute times (these are ensured to be
+ * correct) or relative time references that are expected to be
+ * added to some absolute time value and then normalized by
+ * mktime() The return value is either TIME_OK (aka NULL) or
+ * the pointer to the error message in the case of problems
+ */
+char     *rrd_parsetime(
+    const char *tspec,
+    rrd_time_value_t *ptv)
+{
+    time_t    now = time(NULL);
+    int       hr = 0;
+
+    /* this MUST be initialized to zero for midnight/noon/teatime */
+
+    Specials = VariousWords;    /* initialize special words context */
+
+    try(init_scanner(1, &tspec));
+
+    /* establish the default time reference */
+    ptv->type = ABSOLUTE_TIME;
+    ptv->offset = 0;
+    ptv->tm = *localtime(&now);
+    ptv->tm.  tm_isdst = -1;    /* mk time can figure dst by default ... */
+
+    token();
+    switch (sc_tokid) {
+    case PLUS:
+    case MINUS:
+        break;          /* jump to OFFSET-SPEC part */
+
+    case START:
+        ptv->type = RELATIVE_TO_START_TIME;
+        goto KeepItRelative;
+    case END:
+        ptv->type = RELATIVE_TO_END_TIME;
+      KeepItRelative:
+        ptv->tm.  tm_sec = 0;
+        ptv->tm.  tm_min = 0;
+        ptv->tm.  tm_hour = 0;
+        ptv->tm.  tm_mday = 0;
+        ptv->tm.  tm_mon = 0;
+        ptv->tm.  tm_year = 0;
+
+        /* FALLTHRU */
+    case NOW:
+    {
+        int       time_reference = sc_tokid;
+
+        token();
+        if (sc_tokid == PLUS || sc_tokid == MINUS)
+            break;
+        if (time_reference != NOW) {
+            panic(e("'start' or 'end' MUST be followed by +|- offset"));
+        } else if (sc_tokid != EOF) {
+            panic(e("if 'now' is followed by a token it must be +|- offset"));
+        }
+    };
+        break;
+
+        /* Only absolute time specifications below */
+    case NUMBER:
+    {
+        long      hour_sv = ptv->tm.tm_hour;
+        long      year_sv = ptv->tm.tm_year;
+
+        ptv->tm.  tm_hour = 30;
+        ptv->tm.  tm_year = 30000;
+
+        try(tod(ptv))
+            try(day(ptv))
+            if (ptv->tm.tm_hour == 30 && ptv->tm.tm_year != 30000) {
+            try(tod(ptv))
+        }
+        if (ptv->tm.tm_hour == 30) {
+            ptv->tm.  tm_hour = hour_sv;
+        }
+        if (ptv->tm.tm_year == 30000) {
+            ptv->tm.  tm_year = year_sv;
+        }
+    };
+        break;
+        /* fix month parsing */
+    case JAN:
+    case FEB:
+    case MAR:
+    case APR:
+    case MAY:
+    case JUN:
+    case JUL:
+    case AUG:
+    case SEP:
+    case OCT:
+    case NOV:
+    case DEC:
+        try(day(ptv));
+        if (sc_tokid != NUMBER)
+            break;
+        try(tod(ptv))
+            break;
+
+        /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialized
+         * hr to zero up above, then fall into this case in such a
+         * way so we add +12 +4 hours to it for teatime, +12 hours
+         * to it for noon, and nothing at all for midnight, then
+         * set our rettime to that hour before leaping into the
+         * month scanner
+         */
+    case TEATIME:
+        hr += 4;
+        /* FALLTHRU */
+    case NOON:
+        hr += 12;
+        /* FALLTHRU */
+    case MIDNIGHT:
+        /* if (ptv->tm.tm_hour >= hr) {
+           ptv->tm.tm_mday++;
+           ptv->tm.tm_wday++;
+           } *//* shifting does not makes sense here ... noon is noon */
+        ptv->tm.  tm_hour = hr;
+        ptv->tm.  tm_min = 0;
+        ptv->tm.  tm_sec = 0;
+
+        token();
+        try(day(ptv));
+        break;
+    default:
+        panic(e("unparsable time: %s%s", sc_token, sct));
+        break;
+    }                   /* ugly case statement */
+
+    /*
+     * the OFFSET-SPEC part
+     *
+     * (NOTE, the sc_tokid was prefetched for us by the previous code)
+     */
+    if (sc_tokid == PLUS || sc_tokid == MINUS) {
+        Specials = TimeMultipliers; /* switch special words context */
+        while (sc_tokid == PLUS || sc_tokid == MINUS || sc_tokid == NUMBER) {
+            if (sc_tokid == NUMBER) {
+                try(plus_minus(ptv, PREVIOUS_OP));
+            } else
+                try(plus_minus(ptv, sc_tokid));
+            token();    /* We will get EOF eventually but that's OK, since
+                           token() will return us as many EOFs as needed */
+        }
+    }
+
+    /* now we should be at EOF */
+    if (sc_tokid != EOF) {
+        panic(e("unparsable trailing text: '...%s%s'", sc_token, sct));
+    }
+
+    if (ptv->type == ABSOLUTE_TIME)
+        if (mktime(&ptv->tm) == -1) {   /* normalize & check */
+            /* can happen for "nonexistent" times, e.g. around 3am */
+            /* when winter -> summer time correction eats a hour */
+            panic(e("the specified time is incorrect (out of range?)"));
+        }
+    EnsureMemFree();
+    return TIME_OK;
+}                       /* rrd_parsetime */
+
+
+int rrd_proc_start_end(
+    rrd_time_value_t *start_tv,
+    rrd_time_value_t *end_tv,
+    time_t *start,
+    time_t *end)
+{
+    if (start_tv->type == RELATIVE_TO_END_TIME &&   /* same as the line above */
+        end_tv->type == RELATIVE_TO_START_TIME) {
+        rrd_set_error("the start and end times cannot be specified "
+                      "relative to each other");
+        return -1;
+    }
+
+    if (start_tv->type == RELATIVE_TO_START_TIME) {
+        rrd_set_error
+            ("the start time cannot be specified relative to itself");
+        return -1;
+    }
+
+    if (end_tv->type == RELATIVE_TO_END_TIME) {
+        rrd_set_error("the end time cannot be specified relative to itself");
+        return -1;
+    }
+
+    if (start_tv->type == RELATIVE_TO_END_TIME) {
+        struct tm tmtmp;
+
+        *end = mktime(&(end_tv->tm)) + end_tv->offset;
+        tmtmp = *localtime(end);    /* reinit end including offset */
+        tmtmp.tm_mday += start_tv->tm.tm_mday;
+        tmtmp.tm_mon += start_tv->tm.tm_mon;
+        tmtmp.tm_year += start_tv->tm.tm_year;
+
+        *start = mktime(&tmtmp) + start_tv->offset;
+    } else {
+        *start = mktime(&(start_tv->tm)) + start_tv->offset;
+    }
+    if (end_tv->type == RELATIVE_TO_START_TIME) {
+        struct tm tmtmp;
+
+        *start = mktime(&(start_tv->tm)) + start_tv->offset;
+        tmtmp = *localtime(start);
+        tmtmp.tm_mday += end_tv->tm.tm_mday;
+        tmtmp.tm_mon += end_tv->tm.tm_mon;
+        tmtmp.tm_year += end_tv->tm.tm_year;
+
+        *end = mktime(&tmtmp) + end_tv->offset;
+    } else {
+        *end = mktime(&(end_tv->tm)) + end_tv->offset;
+    }
+    return 0;
+}                       /* rrd_proc_start_end */
diff --git a/program/src/rrd_parsetime.h b/program/src/rrd_parsetime.h
new file mode 100644 (file)
index 0000000..d9a34e8
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef __PARSETIME_H__
+#define __PARSETIME_H__
+
+#include <stdio.h>
+
+#include "rrd.h"
+
+#endif
index 56d762617a391060dc162ebf28d8cc9265b3af37..a281bc089f2f5f3713fea10e3ce7bb5dedf42584 100644 (file)
@@ -60,7 +60,7 @@ int rrd_resize(
         rrd_free(&rrdold);
         return (-1);
     }
-    if (LockRRD(rrd_file->fd) != 0) {
+    if (rrd_lock(rrd_file) != 0) {
         rrd_set_error("could not lock original RRD");
         rrd_free(&rrdold);
         rrd_close(rrd_file);
@@ -98,7 +98,7 @@ int rrd_resize(
         rrd_free(&rrdnew);
         return (-1);
     }
-    if (LockRRD(rrd_out_file->fd) != 0) {
+    if (rrd_lock(rrd_out_file) != 0) {
         rrd_set_error("could not lock new RRD");
         rrd_free(&rrdold);
         rrd_close(rrd_file);
index 3138cc8529ecb6c549645beff2ce74c05d5944a9..46d8cd1e8450d810eba62eb31239cd49f0f510c7 100644 (file)
@@ -27,7 +27,7 @@ static pthread_once_t context_key_once = PTHREAD_ONCE_INIT;
 static void context_destroy_context(
     void *ctx_)
 {
-    struct rrd_context *ctx = ctx_;
+    rrd_context_t *ctx = ctx_;
 
     if (ctx)
         rrd_free_context(ctx);
@@ -40,10 +40,10 @@ static void context_get_key(
     pthread_key_create(&context_key, context_destroy_context);
 }
 
-struct rrd_context *rrd_get_context(
+rrd_context_t *rrd_get_context(
     void)
 {
-    struct rrd_context *ctx;
+    rrd_context_t *ctx;
 
     pthread_once(&context_key_once, context_get_key);
     ctx = pthread_getspecific(context_key);
@@ -58,7 +58,7 @@ struct rrd_context *rrd_get_context(
 const char *rrd_strerror(
     int err)
 {
-    struct rrd_context *ctx = rrd_get_context();
+    rrd_context_t *ctx = rrd_get_context();
 
     if (strerror_r(err, ctx->lib_errstr, sizeof(ctx->lib_errstr)))
         return "strerror_r failed. sorry!";
@@ -71,7 +71,7 @@ const char *rrd_strerror(
     int err)
 {
     static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
-    struct rrd_context *ctx;
+    rrd_context_t *ctx;
 
     ctx = rrd_get_context();
     pthread_mutex_lock(&mtx);
index 899fe93db44cd5c0b8bada2537ab3a1962bfa187..8277ca48aec21dadcac6d0f95a0bde0efa9347d4 100644 (file)
@@ -44,10 +44,10 @@ static void context_init_context(
         atexit(context_destroy_context);
     }
 }
-struct rrd_context *rrd_get_context(
+rrd_context_t *rrd_get_context(
     void)
 {
-    struct rrd_context *ctx;
+    rrd_context_t *ctx;
 
     context_init_context();
 
@@ -63,7 +63,7 @@ struct rrd_context *rrd_get_context(
 const char *rrd_strerror(
     int err)
 {
-    struct rrd_context *ctx;
+    rrd_context_t *ctx;
 
     context_init_context();
 
index 33e49c0eb2c23d561120734627cfd1841e6f1df0..f978eaa580aeda2563dac863d35f1fbc844c3795 100644 (file)
@@ -621,15 +621,15 @@ int HandleInputLine(
     else if (strcmp("dump", argv[1]) == 0)
         rrd_dump(argc - 1, &argv[1]);
     else if (strcmp("info", argv[1]) == 0 || strcmp("updatev", argv[1]) == 0) {
-        info_t   *data;
+        rrd_info_t *data;
 
         if (strcmp("info", argv[1]) == 0)
 
             data = rrd_info(argc - 1, &argv[1]);
         else
             data = rrd_update_v(argc - 1, &argv[1]);
-        info_print(data);
-        info_free(data);
+        rrd_info_print(data);
+        rrd_info_free(data);
     }
 
     else if (strcmp("--version", argv[1]) == 0 ||
@@ -804,12 +804,12 @@ int HandleInputLine(
         }
 
     } else if (strcmp("graphv", argv[1]) == 0) {
-        info_t   *grinfo = NULL;    /* 1 to distinguish it from the NULL that rrd_graph sends in */
+        rrd_info_t *grinfo = NULL;    /* 1 to distinguish it from the NULL that rrd_graph sends in */
 
         grinfo = rrd_graph_v(argc - 1, &argv[1]);
         if (grinfo) {
-            info_print(grinfo);
-            info_free(grinfo);
+            rrd_info_print(grinfo);
+            rrd_info_free(grinfo);
         }
 
     } else if (strcmp("tune", argv[1]) == 0)
index 7f0b217dc220e5f6d0997d92b83e3f244c3b81d7..17ea48f13f236be9735397e4038dfaa43e8cbb62 100644 (file)
@@ -63,31 +63,9 @@ extern    "C" {
 
 #define DIM(x) (sizeof(x)/sizeof(x[0]))
 
-    info_t   *rrd_info(
-    int,
-    char **);
-    int       rrd_lastupdate(
-    int argc,
-    char **argv,
-    time_t *last_update,
-    unsigned long *ds_cnt,
-    char ***ds_namv,
-    char ***last_ds);
-    info_t   *rrd_update_v(
-    int,
-    char **);
     char     *sprintf_alloc(
     char *,
     ...);
-    info_t   *info_push(
-    info_t *,
-    char *,
-    enum info_type,
-    infoval);
-    void      info_print(
-    info_t *data);
-    void      info_free(
-    info_t *);
 
 /* HELPER FUNCTIONS */
 
@@ -141,10 +119,8 @@ extern    "C" {
     int whence);
     off_t     rrd_tell(
     rrd_file_t *rrd_file);
-    int       readfile(
-    const char *file,
-    char **buffer,
-    int skipfirst);
+    int       rrd_lock(
+    rrd_file_t *file);
 
 #define RRD_READONLY    (1<<0)
 #define RRD_READWRITE   (1<<1)
@@ -163,12 +139,6 @@ extern    "C" {
     char *a,
     char *b);
 
-    /* rrd_strerror is thread safe, but still it uses a global buffer
-       (but one per thread), thus subsequent calls within a single
-       thread overwrite the same buffer */
-    const char *rrd_strerror(
-    int err);
-
 #endif
 
 #ifdef  __cplusplus
index 44dddf28af89328419f369c855fd60c52992c143..585fa29e357cd0abe76ef8456dae822f4f860106 100644 (file)
@@ -71,7 +71,7 @@ int       _rrd_update(
     const char *tmplt,
     int argc,
     const char **argv,
-    info_t *);
+    rrd_info_t *);
 
 static int allocate_data_structures(
     rrd_t *rrd,
@@ -104,7 +104,7 @@ static int process_arg(
     char **updvals,
     long *tmpl_idx,
     unsigned long tmpl_cnt,
-    info_t **pcdp_summary,
+    rrd_info_t **pcdp_summary,
     int version,
     unsigned long *skip_update,
     int *schedule_smooth);
@@ -258,7 +258,7 @@ static int write_to_rras(
     unsigned long *rra_current,
     time_t current_time,
     unsigned long *skip_update,
-    info_t **pcdp_summary);
+    rrd_info_t **pcdp_summary);
 
 static int write_RRA_row(
     rrd_file_t *rrd_file,
@@ -266,7 +266,7 @@ static int write_RRA_row(
     unsigned long rra_idx,
     unsigned long *rra_current,
     unsigned short CDP_scratch_idx,
-    info_t **pcdp_summary,
+    rrd_info_t **pcdp_summary,
     time_t rra_time);
 
 static int smooth_all_rras(
@@ -317,13 +317,13 @@ static inline void initialize_time(
 
 #define IFDNAN(X,Y) (isnan(X) ? (Y) : (X));
 
-info_t   *rrd_update_v(
+rrd_info_t *rrd_update_v(
     int argc,
     char **argv)
 {
     char     *tmplt = NULL;
-    info_t   *result = NULL;
-    infoval   rc;
+    rrd_info_t   *result = NULL;
+    rrd_infoval_t rc;
     struct option long_options[] = {
         {"template", required_argument, 0, 't'},
         {0, 0, 0, 0}
@@ -359,7 +359,7 @@ info_t   *rrd_update_v(
         goto end_tag;
     }
     rc.u_int = 0;
-    result = info_push(NULL, sprintf_alloc("return_value"), RD_I_INT, rc);
+    result = rrd_info_push(NULL, sprintf_alloc("return_value"), RD_I_INT, rc);
     rc.u_int = _rrd_update(argv[optind], tmplt,
                            argc - optind - 1,
                            (const char **) (argv + optind + 1), result);
@@ -428,7 +428,7 @@ int _rrd_update(
     const char *tmplt,
     int argc,
     const char **argv,
-    info_t *pcdp_summary)
+    rrd_info_t *pcdp_summary)
 {
 
     int       arg_i = 2;
@@ -479,7 +479,7 @@ int _rrd_update(
     /* get exclusive lock to whole file.
      * lock gets removed when we close the file.
      */
-    if (LockRRD(rrd_file->fd) != 0) {
+    if (rrd_lock(rrd_file) != 0) {
         rrd_set_error("could not lock RRD");
         goto err_close;
     }
@@ -560,8 +560,8 @@ int _rrd_update(
  *
  * returns 0 on success
  */
-int LockRRD(
-    int in_file)
+int rrd_lock(
+    rrd_file_t *file)
 {
     int       rcstat;
 
@@ -569,8 +569,8 @@ int LockRRD(
 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
         struct _stat st;
 
-        if (_fstat(in_file, &st) == 0) {
-            rcstat = _locking(in_file, _LK_NBLCK, st.st_size);
+        if (_fstat(file->fd, &st) == 0) {
+            rcstat = _locking(file->fd, _LK_NBLCK, st.st_size);
         } else {
             rcstat = -1;
         }
@@ -582,7 +582,7 @@ int LockRRD(
         lock.l_start = 0;   /* start of file */
         lock.l_whence = SEEK_SET;   /* end of file */
 
-        rcstat = fcntl(in_file, F_SETLK, &lock);
+        rcstat = fcntl(file->fd, F_SETLK, &lock);
 #endif
     }
 
@@ -750,7 +750,7 @@ static int process_arg(
     char **updvals,
     long *tmpl_idx,
     unsigned long tmpl_cnt,
-    info_t **pcdp_summary,
+    rrd_info_t **pcdp_summary,
     int version,
     unsigned long *skip_update,
     int *schedule_smooth)
@@ -926,12 +926,12 @@ static int get_time_from_reading(
     double    tmp;
     char     *parsetime_error = NULL;
     char     *old_locale;
-    struct rrd_time_value ds_tv;
+    rrd_time_value_t ds_tv;
     struct timeval tmp_time;    /* used for time conversion */
 
     /* get the time from the reading ... handle N */
     if (timesyntax == '@') {    /* at-style */
-        if ((parsetime_error = parsetime(updvals[0], &ds_tv))) {
+        if ((parsetime_error = rrd_parsetime(updvals[0], &ds_tv))) {
             rrd_set_error("ds time: %s: %s", updvals[0], parsetime_error);
             return -1;
         }
@@ -1855,7 +1855,7 @@ static int write_to_rras(
     unsigned long *rra_current,
     time_t current_time,
     unsigned long *skip_update,
-    info_t **pcdp_summary)
+    rrd_info_t **pcdp_summary)
 {
     unsigned long rra_idx;
     unsigned long rra_start;
@@ -1963,11 +1963,11 @@ static int write_RRA_row(
     unsigned long rra_idx,
     unsigned long *rra_current,
     unsigned short CDP_scratch_idx,
-    info_t **pcdp_summary,
+    rrd_info_t **pcdp_summary,
     time_t rra_time)
 {
     unsigned long ds_idx, cdp_idx;
-    infoval   iv;
+    rrd_infoval_t iv;
 
     for (ds_idx = 0; ds_idx < rrd->stat_head->ds_cnt; ds_idx++) {
         /* compute the cdp index */
@@ -1980,7 +1980,7 @@ static int write_RRA_row(
         if (*pcdp_summary != NULL) {
             iv.u_val = rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val;
             /* append info to the return hash */
-            *pcdp_summary = info_push(*pcdp_summary,
+            *pcdp_summary = rrd_info_push(*pcdp_summary,
                                       sprintf_alloc("[%d]RRA[%s][%lu]DS[%s]",
                                                     rra_time,
                                                     rrd->rra_def[rra_idx].
index 8cb9689b6a944b43d4e05232a2147b8bd2ec422d..0dc5e671265bfb9fc59e8bd3967cf7f2d02e8ba9 100644 (file)
@@ -56,7 +56,7 @@ int rrd_xport(
 
     image_desc_t im;
     time_t    start_tmp = 0, end_tmp = 0;
-    struct rrd_time_value start_tv, end_tv;
+    rrd_time_value_t start_tv, end_tv;
     char     *parsetime_error = NULL;
     struct option long_options[] = {
         {"start", required_argument, 0, 's'},
@@ -72,8 +72,8 @@ int rrd_xport(
 
     rrd_graph_init(&im);
 
-    parsetime("end-24h", &start_tv);
-    parsetime("now", &end_tv);
+    rrd_parsetime("end-24h", &start_tv);
+    rrd_parsetime("now", &end_tv);
 
     while (1) {
         int       option_index = 0;
@@ -91,13 +91,13 @@ int rrd_xport(
         case 262:
             break;
         case 's':
-            if ((parsetime_error = parsetime(optarg, &start_tv))) {
+            if ((parsetime_error = rrd_parsetime(optarg, &start_tv))) {
                 rrd_set_error("start time: %s", parsetime_error);
                 return -1;
             }
             break;
         case 'e':
-            if ((parsetime_error = parsetime(optarg, &end_tv))) {
+            if ((parsetime_error = rrd_parsetime(optarg, &end_tv))) {
                 rrd_set_error("end time: %s", parsetime_error);
                 return -1;
             }
@@ -115,7 +115,7 @@ int rrd_xport(
         }
     }
 
-    if (proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
+    if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
         return -1;
     }
 
index fbc0c1d7e3c62b076a96655617a2a8cbd94eec29..a5cd6c3cd16370a5966e0203bb749fd7cd98e83f 100644 (file)
@@ -233,7 +233,7 @@ SOURCE=..\src\hash_32.c
 # End Source File\r
 # Begin Source File\r
 \r
-SOURCE=..\src\parsetime.c\r
+SOURCE=..\src\rrd_parsetime.c\r
 # End Source File\r
 # Begin Source File\r
 \r
index 30ca96692787dfb86a7ec24e337d76e30ecc4973..b9822a9afa78de54fd3f72b90894c0125dc48463 100644 (file)
                        </FileConfiguration>\r
                </File>\r
                <File\r
-                       RelativePath="parsetime.c">\r
+                       RelativePath="rrd_parsetime.c">\r
                        <FileConfiguration\r
                                Name="Release|Win32">\r
                                <Tool\r