[rrd-users] [PATCH + RFC] Cleanup the symbols exported by librrd.

Sebastian Harl sh at tokkee.org
Sun Jun 8 16:46:50 CEST 2008


Hi,

(Cc'ing rrd-users, hoping to get more feedback!)

Please find attached a patch which cleans up the symbols exported by
librrd (and librrd_th). See the patch description for a detailed
changelog (I hope I didn't miss anything) and a short rationale for the
patch. I'm sorry that the patch got that long but I had to touch a lot
of places to implement all changes I wanted to do and keep things still
compiling ;-)

I would really like to see this patch in the upcoming 1.3 release, so
further feedback would be very appreciated. So far, I did not do a lot
of testing but I'm going to recompile all reverse dependencies of
rrdtool (i.e. packages which build-depend on rrdtool) which can
currently be found in Debian unstable and report back after that.
However, I'm not going to be able to do much functional testing, so
that's up to you - the users of those packages! ;-)

While creating the patch, I noticed a couple of things which I would
like some more opinions about:

 * Currently, librrd and librrd_th export the same set of symbols (e.g.
   both libraries export the thread-unsafe rrd_fetch() as well as the
   thread-safe rrd_fetch_r()). Is this intended? If not, I guess this
   should be changed before 1.3 as well as that would else require
   another SONAME bump.

 * I did not include rrd_open(), rrd_close(), rrd_write() and similar
   functions in the public interface so far as they look like pretty
   low-level functions which usually are not needed outside of rrdtool.
   OTOH, it does not look like including those functions as well would
   hurt anybody. Any comments on that?

   (NB: I've removed LockRRD() from rrd.h as imho that function only
   makes sense if rrd_open(), etc. are available as well.)

 * I don't really see what librrdupd is used for. As it gets linked into
   librrd statically, the resulting object file should not be any
   different if linked against librrdupd or each single object file.
   What was the motivation behind introducing that library?

This patch is meant to be a first step in cleaning up up the librrd
interface. The next step would be to declare private functions as static
to enforce that they are not meant to be exported on a level defined in
the C standard. I'm pretty sure there are a couple of private functions
which can hardly be declared static - in that case one might think about
adding those functions to the public interface or implement other means
to not export the respective symbols.

TIA for feedback and comments!

Cheers,
Sebastian

-- 
Sebastian "tokkee" Harl +++ GnuPG-ID: 0x8501C7FC +++ http://tokkee.org/

Those who would give up Essential Liberty to purchase a little Temporary
Safety, deserve neither Liberty nor Safety.         -- Benjamin Franklin

-------------- next part --------------
From c6dc9f26ef93333804f125a065b6a04d68fabe79 Mon Sep 17 00:00:00 2001
From: Sebastian Harl <sh at tokkee.org>
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.
---
 program/bindings/perl-shared/RRDs.xs    |   18 +-
 program/bindings/python/rrdtoolmodule.c |   14 +-
 program/bindings/ruby/main.c            |    2 +-
 program/bindings/tcl/tclrrd.c           |    4 +-
 program/doc/rrdthreads.pod              |    2 +-
 program/netware/Makefile                |    2 +-
 program/src/Makefile.am                 |   16 +-
 program/src/librrd.sym                  |   38 ++
 program/src/parsetime.c                 | 1043 -------------------------------
 program/src/parsetime.h                 |    8 -
 program/src/rrd.h                       |  101 ++--
 program/src/rrd_cgi.c                   |   69 ++-
 program/src/rrd_create.c                |    4 +-
 program/src/rrd_error.c                 |   18 +-
 program/src/rrd_fetch.c                 |   12 +-
 program/src/rrd_graph.c                 |   52 +-
 program/src/rrd_graph.h                 |    8 +-
 program/src/rrd_graph_helper.c          |   10 +-
 program/src/rrd_info.c                  |  109 ++--
 program/src/rrd_nan_inf.c               |    4 +-
 program/src/rrd_not_thread_safe.c       |    4 +-
 program/src/rrd_open.c                  |   60 --
 program/src/rrd_parsetime.c             | 1043 +++++++++++++++++++++++++++++++
 program/src/rrd_parsetime.h             |    8 +
 program/src/rrd_resize.c                |    4 +-
 program/src/rrd_thread_safe.c           |   10 +-
 program/src/rrd_thread_safe_nt.c        |    6 +-
 program/src/rrd_tool.c                  |   12 +-
 program/src/rrd_tool.h                  |   34 +-
 program/src/rrd_update.c                |   44 +-
 program/src/rrd_xport.c                 |   12 +-
 program/win32/rrd.dsp                   |    2 +-
 program/win32/rrd.vcproj                |    2 +-
 33 files changed, 1403 insertions(+), 1372 deletions(-)
 create mode 100644 program/src/librrd.sym
 delete mode 100644 program/src/parsetime.c
 delete mode 100644 program/src/parsetime.h
 create mode 100644 program/src/rrd_parsetime.c
 create mode 100644 program/src/rrd_parsetime.h

diff --git a/program/bindings/perl-shared/RRDs.xs b/program/bindings/perl-shared/RRDs.xs
index 5eeba18..b2a70d9 100644
--- a/program/bindings/perl-shared/RRDs.xs
+++ b/program/bindings/perl-shared/RRDs.xs
@@ -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;
diff --git a/program/bindings/python/rrdtoolmodule.c b/program/bindings/python/rrdtoolmodule.c
index 4d3a0d5..b3caf19 100644
--- a/program/bindings/python/rrdtoolmodule.c
+++ b/program/bindings/python/rrdtoolmodule.c
@@ -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;
 }
 
diff --git a/program/bindings/ruby/main.c b/program/bindings/ruby/main.c
index b3e512e..353aa4d 100644
--- a/program/bindings/ruby/main.c
+++ b/program/bindings/ruby/main.c
@@ -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);
diff --git a/program/bindings/tcl/tclrrd.c b/program/bindings/tcl/tclrrd.c
index c99c1b0..d4593bb 100644
--- a/program/bindings/tcl/tclrrd.c
+++ b/program/bindings/tcl/tclrrd.c
@@ -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);
diff --git a/program/doc/rrdthreads.pod b/program/doc/rrdthreads.pod
index ace7ee8..71506e9 100644
--- a/program/doc/rrdthreads.pod
+++ b/program/doc/rrdthreads.pod
@@ -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
diff --git a/program/netware/Makefile b/program/netware/Makefile
index c138dfa..ec4e287 100644
--- a/program/netware/Makefile
+++ b/program/netware/Makefile
@@ -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)
 
diff --git a/program/src/Makefile.am b/program/src/Makefile.am
index daaacb6..65febf6 100644
--- a/program/src/Makefile.am
+++ b/program/src/Makefile.am
@@ -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
index 0000000..0e29bf0
--- /dev/null
+++ b/program/src/librrd.sym
@@ -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
index 8818f1c..0000000
--- a/program/src/parsetime.c
+++ /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
index d9a34e8..0000000
--- a/program/src/parsetime.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef __PARSETIME_H__
-#define __PARSETIME_H__
-
-#include <stdio.h>
-
-#include "rrd.h"
-
-#endif
diff --git a/program/src/rrd.h b/program/src/rrd.h
index 376d84f..3d62ed9 100644
--- a/program/src/rrd.h
+++ b/program/src/rrd.h
@@ -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 */
 
diff --git a/program/src/rrd_cgi.c b/program/src/rrd_cgi.c
index d3818db..f17dfe1 100644
--- a/program/src/rrd_cgi.c
+++ b/program/src/rrd_cgi.c
@@ -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("");
     }
 
diff --git a/program/src/rrd_create.c b/program/src/rrd_create.c
index 32882f2..960a7a6 100644
--- a/program/src/rrd_create.c
+++ b/program/src/rrd_create.c
@@ -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);
             }
diff --git a/program/src/rrd_error.c b/program/src/rrd_error.c
index e368ca7..714289f 100644
--- a/program/src/rrd_error.c
+++ b/program/src/rrd_error.c
@@ -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);
diff --git a/program/src/rrd_fetch.c b/program/src/rrd_fetch.c
index 1317483..76ea263 100644
--- a/program/src/rrd_fetch.c
+++ b/program/src/rrd_fetch.c
@@ -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;
     }
 
diff --git a/program/src/rrd_graph.c b/program/src/rrd_graph.c
index a503ca3..dcd9059 100644
--- a/program/src/rrd_graph.c
+++ b/program/src/rrd_graph.c
@@ -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;
     }
diff --git a/program/src/rrd_graph.h b/program/src/rrd_graph.h
index 0642d41..1b45284 100644
--- a/program/src/rrd_graph.h
+++ b/program/src/rrd_graph.h
@@ -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);
diff --git a/program/src/rrd_graph_helper.c b/program/src/rrd_graph_helper.c
index 13d5041..e6dd4e0 100644
--- a/program/src/rrd_graph_helper.c
+++ b/program/src/rrd_graph_helper.c
@@ -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) {
diff --git a/program/src/rrd_info.c b/program/src/rrd_info.c
index f53a752..6d1a75b 100644
--- a/program/src/rrd_info.c
+++ b/program/src/rrd_info.c
@@ -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;
diff --git a/program/src/rrd_nan_inf.c b/program/src/rrd_nan_inf.c
index 1787948..2914d1a 100644
--- a/program/src/rrd_nan_inf.c
+++ b/program/src/rrd_nan_inf.c
@@ -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) {
diff --git a/program/src/rrd_not_thread_safe.c b/program/src/rrd_not_thread_safe.c
index b05ee1a..badb6c3 100644
--- a/program/src/rrd_not_thread_safe.c
+++ b/program/src/rrd_not_thread_safe.c
@@ -17,14 +17,14 @@
 /* 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;
diff --git a/program/src/rrd_open.c b/program/src/rrd_open.c
index e92ae5d..d061685 100644
--- a/program/src/rrd_open.c
+++ b/program/src/rrd_open.c
@@ -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
index 0000000..c1aef0b
--- /dev/null
+++ b/program/src/rrd_parsetime.c
@@ -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
index 0000000..d9a34e8
--- /dev/null
+++ b/program/src/rrd_parsetime.h
@@ -0,0 +1,8 @@
+#ifndef __PARSETIME_H__
+#define __PARSETIME_H__
+
+#include <stdio.h>
+
+#include "rrd.h"
+
+#endif
diff --git a/program/src/rrd_resize.c b/program/src/rrd_resize.c
index 56d7626..a281bc0 100644
--- a/program/src/rrd_resize.c
+++ b/program/src/rrd_resize.c
@@ -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);
diff --git a/program/src/rrd_thread_safe.c b/program/src/rrd_thread_safe.c
index 3138cc8..46d8cd1 100644
--- a/program/src/rrd_thread_safe.c
+++ b/program/src/rrd_thread_safe.c
@@ -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);
diff --git a/program/src/rrd_thread_safe_nt.c b/program/src/rrd_thread_safe_nt.c
index 899fe93..8277ca4 100644
--- a/program/src/rrd_thread_safe_nt.c
+++ b/program/src/rrd_thread_safe_nt.c
@@ -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();
 
diff --git a/program/src/rrd_tool.c b/program/src/rrd_tool.c
index b388d11..e59df1a 100644
--- a/program/src/rrd_tool.c
+++ b/program/src/rrd_tool.c
@@ -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)
diff --git a/program/src/rrd_tool.h b/program/src/rrd_tool.h
index 7f0b217..17ea48f 100644
--- a/program/src/rrd_tool.h
+++ b/program/src/rrd_tool.h
@@ -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
diff --git a/program/src/rrd_update.c b/program/src/rrd_update.c
index 44dddf2..585fa29 100644
--- a/program/src/rrd_update.c
+++ b/program/src/rrd_update.c
@@ -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].
diff --git a/program/src/rrd_xport.c b/program/src/rrd_xport.c
index 8cb9689..0dc5e67 100644
--- a/program/src/rrd_xport.c
+++ b/program/src/rrd_xport.c
@@ -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;
     }
 
diff --git a/program/win32/rrd.dsp b/program/win32/rrd.dsp
index fbc0c1d..a5cd6c3 100644
--- a/program/win32/rrd.dsp
+++ b/program/win32/rrd.dsp
@@ -233,7 +233,7 @@ SOURCE=..\src\hash_32.c
 # End Source File
 # Begin Source File
 
-SOURCE=..\src\parsetime.c
+SOURCE=..\src\rrd_parsetime.c
 # End Source File
 # Begin Source File
 
diff --git a/program/win32/rrd.vcproj b/program/win32/rrd.vcproj
index 30ca966..b9822a9 100644
--- a/program/win32/rrd.vcproj
+++ b/program/win32/rrd.vcproj
@@ -168,7 +168,7 @@
 			</FileConfiguration>
 		</File>
 		<File
-			RelativePath="parsetime.c">
+			RelativePath="rrd_parsetime.c">
 			<FileConfiguration
 				Name="Release|Win32">
 				<Tool
-- 
1.5.6.rc1.12.g7f718

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: Digital signature
Url : http://lists.oetiker.ch/pipermail/rrd-users/attachments/20080608/2ccc4351/attachment-0001.bin 


More information about the rrd-users mailing list